From 7ee1a161821bbf0030407341482a30f3f33d33e7 Mon Sep 17 00:00:00 2001 From: Elemer Lelik <ethlel@esekilxxen1845.rnd.ericsson.se> Date: Fri, 16 Jan 2015 10:37:52 +0100 Subject: [PATCH] Titan PCAPasp TP Initial Contribution --- PCAPasp_CNL113443.tpd | 52 + doc/PCAPasp_CNL113443_1551.doc | Bin 0 -> 168960 bytes doc/PCAPasp_CNL113443_PRI.doc | Bin 0 -> 80384 bytes src/PCAPasp_PT.cc | 2805 ++++++++++++++++++++++++++++++++ src/PCAPasp_PT.hh | 691 ++++++++ src/PCAPasp_PortType.ttcn | 44 + src/PCAPasp_Types.ttcn | 174 ++ 7 files changed, 3766 insertions(+) create mode 100644 PCAPasp_CNL113443.tpd create mode 100644 doc/PCAPasp_CNL113443_1551.doc create mode 100644 doc/PCAPasp_CNL113443_PRI.doc create mode 100644 src/PCAPasp_PT.cc create mode 100644 src/PCAPasp_PT.hh create mode 100644 src/PCAPasp_PortType.ttcn create mode 100644 src/PCAPasp_Types.ttcn diff --git a/PCAPasp_CNL113443.tpd b/PCAPasp_CNL113443.tpd new file mode 100644 index 0000000..aebba3f --- /dev/null +++ b/PCAPasp_CNL113443.tpd @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (c) 2014 Ericsson + + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html + + + File: PCAPasp_CNL113443.tpd + Description: tpd project file + Rev: R7A + Prodnr: CNL 113 443 + + --> +<TITAN_Project_File_Information version="1.0"> + <ProjectName>PCAPasp_CNL113443</ProjectName> + <Folders> + <FolderResource projectRelativePath="src" relativeURI="src"/> + </Folders> + <Files> + <FileResource projectRelativePath="src/PCAPasp_PT.cc" relativeURI="src/PCAPasp_PT.cc"/> + <FileResource projectRelativePath="src/PCAPasp_PT.hh" relativeURI="src/PCAPasp_PT.hh"/> + <FileResource projectRelativePath="src/PCAPasp_PortType.ttcn" relativeURI="src/PCAPasp_PortType.ttcn"/> + <FileResource projectRelativePath="src/PCAPasp_Types.ttcn" relativeURI="src/PCAPasp_Types.ttcn"/> + </Files> + <ActiveConfiguration>Default</ActiveConfiguration> + <Configurations> + <Configuration name="Default"> + <ProjectProperties> + <MakefileSettings> + <generateInternalMakefile>true</generateInternalMakefile> + <GNUMake>true</GNUMake> + <incrementalDependencyRefresh>true</incrementalDependencyRefresh> + <targetExecutable>bin/PCAPasp_CNL113443</targetExecutable> + <linkerLibraries> + <listItem>pcap</listItem> + </linkerLibraries> + </MakefileSettings> + <LocalBuildSettings> + <workingDirectory>bin</workingDirectory> + </LocalBuildSettings> + <NamingCoventions> + <enableProjectSpecificSettings>true</enableProjectSpecificSettings> + <function>.*</function> + <formalParameter>.*</formalParameter> + </NamingCoventions> + </ProjectProperties> + </Configuration> + </Configurations> +</TITAN_Project_File_Information> diff --git a/doc/PCAPasp_CNL113443_1551.doc b/doc/PCAPasp_CNL113443_1551.doc new file mode 100644 index 0000000000000000000000000000000000000000..a88c1d6e66148cdc9c40949a333a30243d256555 GIT binary patch literal 168960 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 C(ufKG literal 0 HcmV?d00001 diff --git a/doc/PCAPasp_CNL113443_PRI.doc b/doc/PCAPasp_CNL113443_PRI.doc new file mode 100644 index 0000000000000000000000000000000000000000..609480774bd48d1e4e8dc227e5ffba686109fe65 GIT binary patch literal 80384 zcmeF42S5}@+sEhV?F1=Gv7CU|>D7){zygTa8|@Sn4x}j77&VDqG{)FsuZb=8-eWiR z-fQf^hF!k@vwM5&9e03l`FOwgbK!TpGduH4duDcKc6RsViLy86|L%B;X>1LdKKq<+ z%8W$zdN`ggn5`I_iDL=)oS&ah*3-bx0LHJ||B)PcvUm^cx!cr$vA5Q{s1O-L$zn6X zL1o6uu(&>PeV85o3T>ta<(O9tV{zZ`G_d8JoF+xlvgPMns%&)~3l27M2N%onX4-qZ z^2bH(l@Z_^QUTCGbEQA=J`nc9V7~}^=c<e)h}>OEgBdCK9`?BgjP<X{*m<09T?;>2 z_QPv4*2I{xy*`Y&!ry&A#%xh7x<L+QL=Ws~E6E;FV!;`Vt%b*zS&TViKLY->WWFjt z-urXn3;j^MXcP8=J7G_9#hYfn3h5Qxkl7dM`WgPHSUNT0&uH#RzJY#0c?og^`GT49 zN4yDg#C9b)7Sf|j9?A7aJ|^^G>?Ac;|DtV`f)^yDN6@#(Yp=pyT{xi%NwEJM#Jl%R zX3Sj_hfw4L>FrAFJ+UYKqb&~ZS|i@lY~D=%BbDZEuoA-O&;H}TB*zWjg>VE(LV2CV z`Anon$Y)_Ml$T%@_JV(1d%>ONUeHHjugkw+A6}m^Gw4}yn`StIS;%L>tZOgG7s^p{ zU)*_3_x~!hP+q#?t;=4B@0ad%$)mPeW`XO6Gh=n3Cx*-#zjQ_Uy8Y+pfU&W$6q}8# zX+%h5R77O6sOZiz=jf!&WVy4fTTNM;W>GDgHIImD6C5qGjSLBnlts%kGG&pe>6x-# zsp+!l=#cO_KC<Z4)Z`3#rffh`W}+-QEIK&6wk$F|H6bfLQx+xfpOlf5s*r^#;3FkA z6UTN^X-KLfQ?AI&K-kd{A+m0%GH0(kJ_s~X)@_i?)<zcE8L?>{7T!wMEm`K=GdeZi zCn(6r$2%av(^)35^)l5Az|=+-8Qh{-RI}zXHP<9kV(V>ZhoE!|_2PuOtHkP%=2bFj z-n=w#(=bJRa#n&oK^7UCo|&OTsCUVPB6pB(cW*?pU20Z(yj&KNnjqI9)2C!IeFd4` zrcG1hvr<qbTnXur>RU3Ye!Ns4RT(vvr^yuwaz%WSTvsOe@uGc-DJOpt?QI$*kIjHi zkq!z9rX$M!C6gP#$wk&i$@^s`rOPQNWbM-A@kzas;)}|XfRf1%<mG#tw#`b4?<;GU znUye5M~nkYCNYSY=wlj^7^~<lM?Un+Lgmzzzd<FE8pu^HZ+9Q$L|95%GM5if-x7rp z=vlHbxVlDRc$<c&X3E6jM5m`_#U)c>($L1}NK2ry78au`fm|*0cK0=HmMu?7qlTxL zObS%i#Nq^VHPPGM&$MLGRo2Dgq;ho;QZ=%^WML?4W^rL~HIu^lN3*_U)ijW+rUmt% zS07!)sjQ;DMOXbmu8NXkMtKK&dTFmbfy(MwoGh+7k^+(}ZymCf6|p#3Tt)P8_u*vu z=#ZtXe#OWN;;NqyR5r>-XBvZ))vP#KTs8B7mPYyNmZhvp#mVBTl8?JTCrd|u1u1J! zak9AD<KrH{$<mQuLCQK)oGh-+__zmhvV67YeUP$l6eo+T8$Rwqrcr*n^Gm7!#mVCJ z-`&fT6Q(P}l#1_HwBiQ|iVw~A*BvvZ<`*YSsrg<|eF)PPGo|VmCrqjOUeJ9A(^ZyA z-7i{Lpr=yzy`cOMroAxXioeDbCrqjQUeJF3CR+6(&{L`V#fehtz891~AXtYerRtL? zJE?_c*Ho&skVYh<C#dWgE3#7J<mq)yL&96jyu5s5zP>&(#8nzDA5gTLfMBV5w^AzV z4oaz)9-EQYGkRc}Ji|RRGhU&xqHa4`YuHA+$Hy1AiFQv+l&bXwrOmSx@!Zw3sXQY- zJ&C)Ls+`EeV^idu_Y#i{%!?7oWehrE{J=r#<>%*BN0dvZ!BVM(kfkzl*HRjus*p>y z^bKLBqgTg!5p~LV3yuDcRB29E_OWEK@$vGsOmvL06q!lMGK~RL)7VTc<0o%VFW)+z zL3O-+?cAl}PEVo0qGVxBO-;FBm=y6TvWp}Q5xXwj)8W&j(TR`>=5)TF^{-8jAY9pr zlSV6JsO;M{>(xshpILmuw0kQkd1a?U%5?{%(TQ?dCJj!h<0wnYki}9zGBZ_{AkRc_ zvRFle3?1bJSaxU~94<?a9VkzSqtyO#S+mSUdAb4v*sw5nPMkMa3!{RYhP7`eON&j1 zif6)gM&G2go*79gJ!AXE_KeNSOzepv4TL0Mgy$|rdBrMZUhe+xY3XubS$t|r8U_w= zNy$l>0}-K&EV(QrHMu`Ir6~JI+eAmVYvbwZ>E-2xeoJC(hAd7l$Dk%Dn{z`w0F7j+ z++?LCK*tg&eQ|PCP)Hj$1V?gYDX9q<y1)rH9+!pGswE4K43i}(GP(8#C*lSw&!Jg! zmq{a&F{YBs6sei=y0Yj*<Y0e!IyZ7p?Ik0LYPkz%g@WZnN|$qjGGy}D^yDPuS|+kx zMqyB5(&e&Pph%VVQno6w$+CEE1e1ZtsRx9NqOOaw_GKW8P*_|ZrNkycP<(u9dP0(- zH)Vq=GkpU5yxfrhafDU*CeOyK1C7VId}<dKDMJY&rxd+C66E}WC}LD_)uABivi40Q zYw)r0E~vWPuu!JVosg)y1#$58^a<dd`*0=5DIP^s=@{YmN{{VLgG@@5vfz_M11_W} zUCyZsmnEvy`FZ+!ak=8l362hlY?6igl+Nom1)&m$%t?plBeB{u%HPZ150RwKj<j7! zbfhdJD~+o>DhW!(CMVu}a8a_*Rsli&e%=Mah+++C)Is5#7HEq~91@h7jya*Mbi@f| zjlv`KNaf>>@>C3zW#aloxs;Zknwc7pX(g_tQnNCZ3eAtjg?v;6!s(VwflH^Lv<a#5 z48@d8RDTpJbOHK+<RLu1;tTR+De{br*xvGt+Okwd@<3#IY(|DWB@UXK!FfWI<*_MT z^N}H^rT}4bCkjd6+$6}8lPC)01imt4sVIR=2tm>C+2bv3lcd1BMO;>Is_7YAp7Cme z7B94QfOk-ke-YWs7d%@1kf@%~QPg|{co$I~O;iQf&L3S2odt(pMeC$^)CM#sTy;b< z6{bkaOhWw(i)<&4SE+|mE2R2Tsex4aE0r2+TmwbZea<q?xkNimJW4cuAdIYZRJo93 z6hYzX$7bnC@fkGf6NZ_?bVY13P3Sch&s(C`LY<MqUNN=Vw0%glEUH-tcUf3?^9Va* zsZBSUEkt7!8)xffC#!DLExdgjvT7%@^|PyCY#fPeBburNS@qcDj8vpPt`A*Dpu}mZ zii{+>x+L|IrJ|hEYZ!;8x*KbzM-WgrKFy*-Jvw#iBx`Xi4vNrjP;7E+lAWe^N=+)V z&bc+FcW9=supBSXAepyX->6Q2ti4=;><vzXe&SkV9Fi!H?~6V4NsQ5_0%$I%oC`%* z@Y7^q;h^{^H<fj5nU$Ort5C$sqFxTli0xh|+RE^>NN5$&p=CI-UbEDS>nv${5ITm1 zg9t7pT$R|uj#~t`Yvqlcq6u(e`_G@T{};y)_xX*138)R~fB_&Ij0B^=bT9+V1hc>< zuo-LtTfsK49UKG4!Jps(cnBVW$KVNg3Z8+#K`yW`#a%~G23P`XU;`Y16Q}_E!N*r0 zU*&?2ukKvVy?kWb?{j{iF=@t#Ni!zhnL{?|N*i4{Q(4dCTm3%KxGQC3BC$)H-C->& zm$-EB56rV=AZu?TvFFZTfOniPYw|6B5+|6oK5iN_re<fVGP&9%UJ3SB%8F>I&)S06 zmgQJVC^Jxij8NPLv(XGO(S%jt<!YWmkl)voFh0Ir`1ojXW2I@w9CXSv;uFIxc^Bmp zjT`$=nqf~z)oGoF1n2>MU;qq(5g@%72nGSt1uDDxD38hD9;j!AH9z1r<&m(d8CDCn zv|lMU{8lo<0<p6>V#92)VVc;Gqth@}d`8Wn=7$-UXVL?POwvk<%qS;1wktT6R78eV zWVVZPnW-%^b7SVsp~a)#n)U!pf|V{32gaH))|VESuyU@F@~)DwUQj}&H!Yc&ocE;F zA=PcKS7UC~#p_o1I2vuV;SUOaZS&yS@jMEF(ZZH|r-H)@1&0+24t>uXF^QReGK<Z_ zkC_9a$3=vXMGqL571gf*5D0=mT~H5DIX3_eK^M>!bOYT%42T7(APw{b<G^?@0Zasw zKn@^XnF4+UtHB!ZJ6H=gfz4na*bn{yH^5DB3)}`TAKra<VgH3~o3<^TK5_rVyASVv ztFq}gecS$Rut|7$m+^J@53C2!tiyb53gype)*MysKZ?T8^!bn0+;Co&mav4{Shxn4 ztS!-9&kI^5ZEY5xJE!;-P}P1R@zp4g?|{32>hyi^7tlw&HUNge2$%y4-~b$f6YvB6 zAOHk{AW#=jyHFoA0G&Y>&=qt8JwXh}1X-Xz$N`hV6!0UM3Vs6Aw)_mHgY{qo$a{GH z;qk-AxBPni{PD$ki>K!;R@#hK+6-3N6x5tTM|^&4F)Uo>ytdTm&uA$@-sdi?ZqU@0 zf)Xf6>)wQype2NVpN?qJj9QeZ8^I>98EgT^z;W;=cmN)PN8mAd0-l0r;BSx%EX;8o z2W5aIum(1uHmC#KK`00VEkP^L8ngl7AOb{!WRL<BAQhy83@{A*eG{#~t_{1EZP>M8 z*8W)+e!lR7(q@p#rVrVmtobsmiG~toq$P*>GF*u@dzxj)`}E|`Xz?kOjqZBL%U*|N zvzoHC<UT9VnUdB2!tF*$bj_cSjh6JV8dS%qEU6skfjrO!t9kYUCtUvefpefi8Prj5 z4b;HJEeq@cQY+MPuoyfC4azb$7F+<X*6<H@0#jVhqQN_HU3^(G!&9*{HG^&eYR=RQ z|40mUhw~qHMx0Y6>BTrv-TDR$2Q$GeFdO^==770?^kP0(0Cs|1U^mzU_JcpbS#S=V z2f5%mcmZC5SKu`u-FgGw0$b>p9ViFNg9@M`2n0c(E{F#0K?l$gyw81qC-;5s`F-cN zZrr-$=dJs;!e*?>W{}b*CHFmL178zoLANz)B45iZ@n^Ku3f|{@Tm?0KmZY}1^8U2Q z6<UwNwYX%pzqnRsDc|CWukXc-U|3Dc*G`}_pnBgGbOYI7AQ%J&gW=#?Fb~WJ3&3`; z1MCF5z;3VyP}{K&><2f&EpQv$0r$aQ;2n4mJ^(u@^Z=9x_P_x+0&0IMfQldp)CK>_ z>;EK#ty#jnZdmhYw3H#Q8$UsVHGLMe<t5P)dtQQ;5dM8yqU8!yi}JJ{s1F)}#vlSj zg0`S9NCqiD0a8I4pzA_9$N-Z-4www4fS<v1unw#T8^8&05}X33!5MHC(6!|}xBy;) zSKu|s10TRg;Dl?=mtX%Gu1+W;Eji4W;Ydi;EJMEZjrcQKd<tb#TumH<SZm7GlKcNl z*MAE>Hd@ldYEa%*02M(12n0c(E~p3U1G@G%1dTvf&<%74JwQ(o15!a6=m*Au@n8a& z2qu9XK>dd);770;tO37+wO|9-2)2RkU<bGWif!}z?&}LT;_DucD`=)wZAOMK<yYvf z6r~>n`15M5X-R7Kd!2eGXwSc^{U6GQtQO;<wT@MzJiZ7nfy>|uxC*X;>);0X06v0G z;4`4f4Lx8COn@nH0j{7ba0AsqbwGW^nxGbF0YX6-XbIYYaL^7!gZ3a5qyh1`f6V?d z808O9jr)_Q^EN$|*Lb;2;o8QR@dNZciz?&3{CTw;D_VC-q5;p)_thTizfS`Su0GmI zoNw{^@$nL;wkqXyKadVGKqjEReSa_j{0OFkpTIOQ1Iz?-!8|Y@Yy;cD4zLsK0=of? z5B7q6;0Cw}Zh_n29=H!4gD2oAFh-3t0j9tVm;(zyV~jGu64U~<LEiJcyQl6R+J9*M z;zOqnJ<oeSU1c*`X){=5qtQADeZ$UptV*+$=Jn<(Cgil2AfJ;x`SaqOEYY0qZC+C~ zTPJ=#mS&{czrJlKQZwpMUe*EbzzcYT01ya*Ksyi(+Jg?DBj^NZ%-02U1=LbzgMnZW z7z&1gkzf?~4$KAfz<jU(ECh=HjT@JMrC=Y}5B>lLz+rF%oB&^b{XbH^=rU}>pI0kG zzVuJxi5BgpFVx@SuK(?L32L&n)ysmiDcK01uy&NBJvz_pxfZQsH7IXSf>Yozcmke+ zXW(y;3us*b0=xv4XhW<(Szrz9Ksn$9Du9Y000e>{P#4q#^#RQdGz5)6SI`Y~2R%R> zhzE%v3G@M@z<1z#@B=8eFTjro)y9AI9Cg+4otU22UdntO59ZISm0?M8d%wLdxq|i- zF2$0SesNmYf)80O#zku#t4aAf8jJxnZ!->z2g|^6@GJNYtOjeq2Cxxq0;j-fa0Z+O z=fHVDb3YfsCGZ;Lfj8hS_y|4$JzRP9fdQxlDgzm)0-S*hp!uV!zzsA3A>dy*{`07y zOE=a!=_*5BH~jeXYB^m{mrJ50n|Zy|QYZg?TB1c4YEhmx1<gPhXbB=fBxnn$hm;Ib zfC8j~G(huu=^z740y$tZm;$DOpTTVK3z!2ogDqey*ao(P9f0NrcY)pDD!2x&gB#!u zxC{OQU*7)Dt5kFuy71@K%8+mWm*Cc(_Le~?o8oF>4PJtpY%RI}zcl_^!RxseU1K#U zZ>g935a^?KVgL-OmjaA|381-KGhhy?f$E?Js0rMG2k-_yz!!vr2oMR{f+)}q(EM+E z&;fJ=X<#y#0)7NDzy`1hYz9TN_d~MNQ?irf*|G9$l!;|*2x43=@rdaSZr{t$7qRB= zaN};f8GqEHCEb_fkC~&!{cvG`n1#Fkd>t<@gKLfT041r}Gb?lcwB)v+J^!xuzak&< z7ib-GqcWlLNCZ?KQvv1uL2w4#2OohMdc5|)4b%f+U_4k1HiIo-8#oEhfrmgJ{SFgg z3qFba;2*>WwfXp0;xm7X4QkGwiqAa$2MlTg9*PCr7aP<>tDRAco0>B<gIcWB&gjmA zTjCg85*xT<6FsapkeD}@u-*wVj%QG1EtN<5mdwai!X&u0NJDf9Zh3R(Uc2C8!yP62 zV~SXUY9PU_U<s<cgHYC&QN~w*2g<E3pfU~x!@)Q}Wjzbb28+NlupDdzn*f#nRzT%{ z7Esw6gBl<dgn?v`0tSF=Fb&KCzkr2c5!eq7gQMUSut!hY0n`T#KnoBG!ay(37o>n8 zU?}(nEC7qZTCfhR2OGd2pf!5^@AIDJJ<7YsLBEgkZslFeyL9f<{%!j=>_4^U)SBh= zbB;HjTXb&GFZ470*15cAXjK}?yja7U5(~S;M{z%3^$-10YeR)0w*Q#cm9wWk?9mF* z2rr~1atAFy?~sY`ODz^>qq?G~+w&tsP7+!VuDG%H%4);4WWRN!F$^Cgp}j*$u;E(5 zGJIQ?f>lV|N+j6RnjgU>T1Hl%>ZSvravB7vOvGhDWpEcf1n&S=H*xtc&Tq<NA%BnJ z*p~8GthX=32DPepPv@DN;xoFPsfjLOpcloW)%+Y1pV94XpV-+Rv0<0kuvKgj9UEHm z>xGb2LS5u)Cn=VdC6*W&NU(&+a#VtC*-=wp_rN{|``!t3Su@dxUp>v*zr4X(wKJT; za5CvgcZ<R7sH_|TjfS(qFfbb|02UZeq=NZiDe!Q>nh($eB!Ep|2hewfP60Qd*3Q2c zC0c!zSg!cY1F_+z*l?v}hVx=)r^JS1V#7hPVXxS*Lu}YAHmnmHR*4PE#Rl;)m+z@k zYJK(4GV@2&Mk|I^Q`+%IWVgIBGLUn}ACaA@{urfQVuK?%BD-nQdP=*dkyr=E*->+# zS+<yu<<_E=haaw6#L1>3;Y>N<4C$T?wTD943+289cB_FI%A9nd4hR4WK>9Ej2s*JE z$NPXel3@XyK^;K4(hYP6q%X_CMnF3A3P_+Uq&HQSdQ%Jg+Mq2+bHcbEtN~lVO>hgm z&3*gi9{#RBxqjiv(Sv(;?A^R~-QLx|EnPT!;q=MVCwxC)#Drmk`ll<>`Qku(H`Q?_ z*Fp*{DA!iQJ}*VC)`HHk#RVNLI_=9PazzGL+#a_2omRk>D>T@0d)QKemE(S?4d#ju zd!aAN6(Y=B5#s6)85hIQ;^K&kl0Tw$oIj#M#Sz?d#aoVV>mynkU`?q3>j7w8DaA0^ z;(LV3T?t?^9Ox%9<uo2Dm>`LBqj?R0nPMnh6KYbO74p3!j*lwyzd!Pu%3vIza@Yu{ z4DJClj3>%~%Ah`I02+bDpa}>8@5MDjtsPLS`f6PwHG^7jORY<ERV?~~*l=2GI4(A* zHSB85_fD}hwKHl4HEG+$eoAN%d!b`=!1#L4U8=eI%ne((`pO?sz2{qUYDWs%bWVvm zcl;5#;}n{+<B!OW>mG1+{1Mq{v`cnWrcc02Ww{FF*$`z&<rxg9Jez{%palp6EkP^L z8ngk~U?3O*hJs<>8}J*T@#hY(6YK)J!F})-cnjWv_uvEg2#hd#HU=iZ6qo}GP!5y_ z_P_x+fi~dd>yOVL-M-HMJ->W%ALap9FT?NLeOq(B&l!^QJuJw;|1P8K$J$Qnjq!c5 zdM4jeCro8Wy(FcbYQ)F87M(%YjQVi}Qn-E`>STdSW~blBpb92QMRxF}@<r0rdw&If z$sTUi;#8lHC+harbZ95`wEnzcZ8w?V2XP=}DVNBp5nOR923s;0*pkVU%C;GxvJC~A zWjhFVg8`K*l_`~<P*#-xpC~hht%QaTf_vJl`FSorqt+a#8FEVIOwAAa=mndap}?B% zJn6gYVXZd5L^{@0uPZI!o+^X#sV(4cfd$;n@rq1V(KgfNb~R??fL^`EX4DabFpbSf zfx(&j#x3i%&(k-@+HNd6;n#N4-bvk9LETtU-Kbpa-GbJ7w_rwA6kk3OxRb^$W`AJ5 zTV=;2n98RX$dbyi0tf?Cm%_mWus|Hi**Xmo;xqNdhT@K`DO%QgH?4M8F=%7zWoBbe z%fnDH7gaBaJBB%Z6I^R*=00-XS7JQ1L%wnqeMMU;0!LdTB#EGn+Z*k-5zM?T1}C(N zUYXq{u*@e)x|F^-pS`_&B}PsKvFuK<TvJ0cmMxg^)SHt3Xgy(uLYYUP%=?1@U>q0^ zNDn50onRNZ04{>d;4{bv46~>;KuypDgn*`?8R!d=K?cYKS)f0d0cL_lU@=$%mV!UQ z32+je0;j<l@Bw@T63lPu0exTqjDRsP0j9tVn1eTut~@#h_C4DAXy3XwkM=FW{?Wb} z+%MVjXZYXA6UKapKdv<uT2-#SM7zn2mX$pyYLA<Re#|d;)`oAZ8<%;?wc%)YRbzPd zYXaXQ7qskJt{R1}2v~)nE=8E2%m}qHp!F>U@+f9%XEov>TxAdsjc_cOFO~5nKxKLd zJO;Vo6`(SHBh(w(|0*^t`ezMd|NrNY#q9ytYOT=KYMuS8)@oJcE*wHDCA3;XehBtL zt7Y<!JOEVCYGHMSTKme3g?dbNmg*<fMXGmH#|8?qpnbo8)*$x(zj>^g=I^PsZc<CL zwH94d(itBAcvYsNt-0fgigaapRfftzC=0%fsu>DqN(^uJ1pW#(-fllS2^yP`jTza9 z{R`zwod8mZd`+CRTUXF-T~WKWvXfAt56nntOAS%py4q6<oRfkwz!D6m*bAGQlBgL5 z2u^69B{t|jqb8t)e$<?agV8vqj)4TXo~iFMl9a%zwx$xa;5!ynMb(txDW?P^j|xpy za7J!ifzumZRrW^DR7z0bu0RP2>?*31K(OPKpy={fzC1P`G2!@!%3s+@L@x%9?w~w1 z%a`8!{qgO`S5GOyU$_6GIZ$X6$c+_+(IEEJ+cBj2Oy#XMU-N%7#b2Xc!W`gco4ENT zi3c<Cl(<EjvdU{ly35ua46d?fse$vF06mwSv4*ao>KaVCtcjM`uQ{n_k#iDr#$UJp zq8#W%!&udm=@LAdUZp?NOO&|uXOil+F8w8X8*S=#FlY44I(L04pNG`@oyXJh`qgc* zm+0wlv<W(|k6X%I_kyl|-1Fu`anSUi%qf$ThaU}mxAo7m<40id(Q3IlsUMBnu?n5_ zWYH{@#WNokgg+nrd9wigdE(sv&%nJ(Gv%ckxOZu$yi^0n{ru9?_EHVpw=`2;s)75J zX39%7aNO-EJ#8=5zynG%<)s>UU}>hjR09tx&6Jm7;DM#thb!g4OR*aj$VxTvQtU<r zV(3t67nVSbT>ifXUW(nQKvt@Omtr?6kd<oSrPz%MWThH-DR!d*F)>n#jZ7(aqXJo} z241S&s8S3(s5JX<r5t!EcB6tyvkw<kn%$^U4!jima5VW{YHfQ^X?CMZIq*{KMg^5- zA1<ggyHTYacqw+Hf=aUw7gU<vs8SBRRJ&277C23WmD;`3QV+b;`f!x;QVqP+rot%Y z6u3fTO+pJ=lc3M9NvO!JN$6<l(qB(sVZg6UXdzmez^&}(mnS$VmnU?zgjr7?&Q%K( z)Ss4EffWT!Spq_)ja!z-AL7^AST1>>|Df<9PL&J(z>*o{((?~%4b14%7TiPlBr1fN z=i0+;5KPMtzoqquR=aR$#q#k>F0FEvkA#fyX2!V{V5M&`$H05P%QwHa3D1-EZ1U%x z_WA6sF=WDZXchdhYAmLTQ*JKoG+nx5WklvHEZ6;vXD{F3T}t_QUk6_7lbfZ_cEUaT zoIQK-&jTYn{AG8*qu%sHX_vB+&g&=moZ5WOz^}{CiC4$kO�!Y~oqBr-A31KVB4e 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	`0aV7(q9Us4@0<WneR&U`G5N}`!Pm^0+R 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 G)WARUo?&$W literal 0 HcmV?d00001 diff --git a/src/PCAPasp_PT.cc b/src/PCAPasp_PT.cc new file mode 100644 index 0000000..091a53e --- /dev/null +++ b/src/PCAPasp_PT.cc @@ -0,0 +1,2805 @@ +/****************************************************************************** +* Copyright (c) 2005, 2014 Ericsson AB +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Antal Wuh.Hen.Chang - initial implementation and initial documentation +* Adam Delic +* Andrea Darabos +* Endre Kulcsar +* Gabor Szalai +* Tibor Szabo +******************************************************************************/ +// +// File: PCAPasp_PT.cc +// Description: PCAP test port source file +// Rev: R7A +// Prodnr: CNL 113 443 +// + +#include "PCAPasp_PT.hh" + +#include <TTCN3.hh> +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +//#include <openssl/md5.h> +//#include <openssl/hmac.h> +//#include <openssl/aes.h> +//#include <openssl/sha.h> +//#include <openssl/bn.h> + + +#include <sys/socket.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <netinet/if_ether.h> + + +#include "memory.h" + + + +using namespace PCAPasp__Types; +namespace PCAPasp__PortType{ +static const u_int16_t c_infinite = 0xFFFF; +static bool logging = false; +static bool noFilter = false; +static Protocol_data p_data; +static PCAPasp__PT *act_pt; + + +INTEGER default_getMsgLen(const OCTETSTRING &stream, const BOOLEAN& /*conn__closed*/, + const Transport &/*stream__transport*/){ + return stream.lengthof(); +} + +INTEGER default_getMsgStartPos(const OCTETSTRING &/*stream*/, const BOOLEAN& /*conn__closed*/, + const Transport &/*stream__transport*/){ + return 0; +} + +static tf__getMsgLen def_getMsgLen_ref=default_getMsgLen; +static tf__getMsgStartPos def_getMsgStartPos_ref=default_getMsgStartPos; + +void genlog(const char *fmt, ...) { + TTCN_Logger::begin_event(TTCN_DEBUG); + va_list ap; + va_start(ap, fmt); + TTCN_Logger::log_event_va_list(fmt, ap); + va_end(ap); + TTCN_Logger::end_event(); +} + +INTEGER f_null_ICV(const OCTETSTRING& /*ipheader*/, const OCTETSTRING& /*ipdata*/, OCTETSTRING& /*user__data*/) +{ +return 0; +} + +BOOLEAN f_null_encryption(const OCTETSTRING& /*ipheader*/, const OCTETSTRING& ipdata, OCTETSTRING& decrypted__data, OCTETSTRING&/* user__data*/) +{ +int ret_len=ipdata.lengthof(); +int start_pos=0; +if(ret_len>9){ + int pad_len=((const unsigned char*)ipdata)[ret_len-1]; + ret_len-=9+pad_len; // spi_length+seq_no_length+pad_length_length = 4+4+1 + if(ret_len<0){ + ret_len=ipdata.lengthof(); + } else { + start_pos=8;// skip spi+seq_no + } +} +decrypted__data = OCTETSTRING(ret_len,(const unsigned char*)ipdata+start_pos); +return TRUE; +} + +ESP_obj::ESP_obj(PCAPasp__Types::ASP__PCAP__ESP__Setup data){ + spi=data.spi().get_long_long_val(); + if(data.icv__function().ispresent()){ + icv_fv=data.icv__function(); + } else { + icv_fv=f_null_ICV; + } + if(data.icv__function__user__data().ispresent()){ + icv_data=data.icv__function__user__data(); + } else { + icv_data=OCTETSTRING(0,NULL); + } + if(data.esp__decrypt__function().ispresent()){ + decrypt_fv=data.esp__decrypt__function(); + } else { + decrypt_fv=f_null_encryption; + } + if(data.esp__decrypt__function__user__data().ispresent()){ + decrypt_data=data.esp__decrypt__function__user__data(); + } else { + decrypt_data=OCTETSTRING(0,NULL); + } + struct in_addr tmp; + if(data.sourceIP().ispresent() && inet_aton((const char *)data.sourceIP()(),&tmp)){ + ip_port=std::string((const char *)&tmp,sizeof(tmp)); + } else { + ip_port=std::string(0,sizeof(tmp)); + } + if(data.destinationIP().ispresent() && inet_aton((const char *)data.destinationIP()(),&tmp)){ + ip_port+=std::string((const char *)&tmp,sizeof(tmp)); + } else { + ip_port+=std::string(0,sizeof(tmp)); + } + u_int16_t tmp2; + if(data.sourcePort().ispresent()){ + tmp2=htons(data.sourcePort()()); + } else { + tmp2=0; + } + ip_port+=std::string((const char *)&tmp2,sizeof(tmp2)); + if(data.destinationPort().ispresent()){ + tmp2=htons(data.destinationPort()()); + } else { + tmp2=0; + } + ip_port+=std::string((const char *)&tmp2,sizeof(tmp2)); + mode=data.mode(); +} +ESP_obj::~ESP_obj(){} + + +ESP_handler::ESP_handler(){} +ESP_handler::~ESP_handler(){ + clean_up(); +} +bool ESP_handler::setup_esp(PCAPasp__Types::ASP__PCAP__ESP__Setup data){ + std::map<unsigned int, ESP_obj*>::iterator it=spi_ESP_obj_map.find(data.spi().get_long_long_val()); + if(data.mode() == PCAPasp__Types::ESP__mode::ESP__DELETE){ + if(it==spi_ESP_obj_map.end()){ + return false; + } + address_ESP_obj_map.erase((it->second)->ip_port); + for (std::list<ESP_obj*>::iterator it2=ESP_OBJ_list.begin(); it2 != ESP_OBJ_list.end(); ++it2){ + if(*it2 == it->second) { + ESP_OBJ_list.erase(it2); + break; + } + } + delete it->second; + spi_ESP_obj_map.erase(it); + return true; + } else { + if(it!=spi_ESP_obj_map.end()){ + return false; + } + ESP_obj* new_esp=new ESP_obj(data); + if(address_ESP_obj_map.find(new_esp->ip_port)!=address_ESP_obj_map.end()){ + delete new_esp; + return false; + } + address_ESP_obj_map[new_esp->ip_port]=new_esp; + spi_ESP_obj_map[new_esp->spi]=new_esp; + ESP_OBJ_list.push_back(new_esp); + return true; + } + + return false; +} +bool ESP_handler::find_esp(unsigned int spi, ESP_obj *& esp){ + std::map<unsigned int, ESP_obj*>::iterator it=spi_ESP_obj_map.find(spi); + if(it==spi_ESP_obj_map.end()){ + return false; + } + esp=it->second; + return true; +} +bool ESP_handler::esp_exists(struct in_addr *ip_src, unsigned int port_src,struct in_addr *ip_dst,unsigned int port_dst,unsigned int /*proto*/){ + u_int16_t p_src=port_src; + u_int16_t p_dst=port_dst; + if(address_ESP_obj_map.find(std::string((const char *)ip_src,sizeof(ip_src))+ + std::string((const char *)ip_dst,sizeof(ip_dst))+ + std::string((const char *)&p_src,sizeof(p_src))+ + std::string((const char *)&p_dst,sizeof(p_dst)) + )!=address_ESP_obj_map.end()){ + return true; + } + if(address_ESP_obj_map.find(std::string((const char *)ip_src,sizeof(ip_src))+ + std::string((const char *)ip_dst,sizeof(ip_dst))+ + std::string(2,'\0')+ + std::string((const char *)&p_dst,sizeof(p_dst)) + )!=address_ESP_obj_map.end()){ + return true; + } + if(address_ESP_obj_map.find(std::string((const char *)ip_src,sizeof(ip_src))+ + std::string((const char *)ip_dst,sizeof(ip_dst))+ + std::string((const char *)&p_src,sizeof(p_src))+ + std::string(2,'\0') + )!=address_ESP_obj_map.end()){ + return true; + } + if(address_ESP_obj_map.find(std::string(4,'\0')+ + std::string((const char *)ip_dst,sizeof(ip_dst))+ + std::string(2,'\0')+ + std::string((const char *)&p_dst,sizeof(p_dst)) + )!=address_ESP_obj_map.end()){ + return true; + } + if(address_ESP_obj_map.find(std::string(4,'\0')+ + std::string((const char *)ip_dst,sizeof(ip_dst))+ + std::string(2,'\0')+ + std::string((const char *)&p_dst,sizeof(p_dst)) + )!=address_ESP_obj_map.end()){ + return true; + } + + return false; +} +void ESP_handler::clean_up(){ + spi_ESP_obj_map.clear(); + address_ESP_obj_map.clear(); + for (std::list<ESP_obj*>::iterator it=ESP_OBJ_list.begin(); it != ESP_OBJ_list.end(); ++it){ + delete *it; + } + ESP_OBJ_list.clear(); +} + +bool ESP_handler::match_esp(struct in_addr *ip_src, unsigned int port_src,struct in_addr *ip_dst,unsigned int port_dst,const ESP_obj *esp){ + u_int16_t p_src=port_src; + u_int16_t p_dst=port_dst; + return (esp->ip_port.substr(0,4)==std::string(4,'\0') || esp->ip_port.substr(0,4)==std::string((const char *)ip_src,sizeof(ip_src))) && + (esp->ip_port.substr(4,4)==std::string(4,'\0') || esp->ip_port.substr(4,4)==std::string((const char *)ip_dst,sizeof(ip_dst))) && + (esp->ip_port.substr(8,2)==std::string(2,'\0') || esp->ip_port.substr(8,2)==std::string((const char *)&p_src,sizeof(p_src))) && + (esp->ip_port.substr(8,2)==std::string(2,'\0') || esp->ip_port.substr(8,2)==std::string((const char *)&p_dst,sizeof(p_dst))); +} + +//////////////////////////////////////////////////////////// +// Constructor +//////////////////////////////////////////////////////////// +PCAPasp__PT::PCAPasp__PT(const char *par_port_name) + : PCAPasp__PT_BASE(par_port_name) +{ + capture_file = NULL; + packet_filter = new char[1]; packet_filter[0] = '\0'; + logging = false; + handle = NULL; + dumpfile = NULL; + + + //for debugging: + peer_list_tcp.setType(true); + peer_list_udp.setType(false); + peer_list_sctp.setType(false); +} + + +//////////////////////////////////////////////////////////// +// Destructor +//////////////////////////////////////////////////////////// +PCAPasp__PT::~PCAPasp__PT() +{ + if (capture_file) delete [] capture_file; + if (packet_filter) delete [] packet_filter; +} + + +//////////////////////////////////////////////////////////// +// set_parameter +//////////////////////////////////////////////////////////// +void PCAPasp__PT::set_parameter(const char *parameter_name, + const char *parameter_value) +{ + //Capturing related parameters + if (strcmp("capture_file", parameter_name) == 0) { + if (capture_file) delete [] capture_file; + capture_file = new char[strlen(parameter_value) + 1]; + strcpy(capture_file, parameter_value); + } + else if (strcmp("packet_filter", parameter_name) == 0) { + if (packet_filter) delete [] packet_filter; + packet_filter = new char[strlen(parameter_value) + 1]; + strcpy(packet_filter, parameter_value); + } + else if (strcmp("logging", parameter_name) == 0) { + + if (strcasecmp(parameter_value, "TRUE") == 0) { + logging = true; + } + else logging = false; + } + else if (strcmp("noFilter", parameter_name) == 0) { + + if (strcasecmp(parameter_value, "TRUE") == 0) { + noFilter = true; + } + else noFilter = false; + } + else + TTCN_warning("PCAP Test Port(%s): Invalid parameter: %s.",port_name ,parameter_name); +} + + +//////////////////////////////////////////////////////////// +// Event_Handler +//////////////////////////////////////////////////////////// +void PCAPasp__PT::Event_Handler(const fd_set */*read_fds*/, const fd_set */*write_fds*/, const fd_set */*error_fds*/, double /*time_since_last_call*/) +{ + const u_char* packet; + packet = pcap_next( handle, &header); + + if (packet == NULL) + TTCN_error("PCAP can't capture"); + if (capture) + pcap_dump((u_char*)dumpfile, &header, packet); +} + + +//////////////////////////////////////////////////////////// +// user_map +//////////////////////////////////////////////////////////// +void PCAPasp__PT::user_map(const char */*system_port*/) +{ + if( geteuid() != 0 ) TTCN_warning ( "You must be root to be able to use the test port in capturing mode!"); + capture = 0; + settings = 0; + + if (capture_file!=NULL){ + if (dump_reader.open(capture_file)==false) { + TTCN_error("Failed to open capture file \"%s\"", capture_file); + return; + } + else { + log("Capture file \"%s\" was opened", capture_file); + } + } + + if (packet_filter!=NULL){ + if (dump_reader.setFilter(packet_filter)==false) { + TTCN_error("Failed to set the packet filter %s", packet_filter); + return; + } else { + log("Filter \"%s\" was applied", packet_filter); + } + } +} + + +//////////////////////////////////////////////////////////// +// user_unmap +//////////////////////////////////////////////////////////// +void PCAPasp__PT::user_unmap(const char */*system_port*/) +{ + peer_list_tcp.log_stat(); + Uninstall_Handler(); + if (handle) pcap_close( handle); + if (dumpfile) pcap_dump_close( dumpfile); +} + +void PCAPasp__PT::user_start() +{ + +} + +void PCAPasp__PT::user_stop() +{ + +} + + +//////////////////////////////////////////////////////////// +// outgoing_send +//////////////////////////////////////////////////////////// +void PCAPasp__PT::outgoing_send(const PCAPasp__Types::ASP__PCAP__ESP__Setup& send_par){ + PCAPasp__Types::ASP__PCAP__ESP__Setup__Resp ret_val; + if(esp.setup_esp(send_par)){ + ret_val.status__code()=PCAPasp__Types::ESP__Status::ESP__OK; + ret_val.status__message()=OMIT_VALUE; + } else { + ret_val.status__code()=PCAPasp__Types::ESP__Status::ESP__SETUP__ERROR; + ret_val.status__message()="Failed to register the ESP"; + } + incoming_message(ret_val); +} + +void PCAPasp__PT::outgoing_send(const PCAPasp__Types::ASP__PCAP__Capture& send_par) +{ + PCAPasp__Types::ASP__PCAP__ConfigResp myStatus; + fd_set readfds; + + myStatus.status() = CommandStatus::INVALID; + myStatus.errorMessage() = OMIT_VALUE; + + if( send_par.command() == CaptureControl::START ) { + myStatus.command() = CommandId::STARTCMD; + if (settings) { + if (capture) { + myStatus.errorMessage() = CommandError::CAPTURING__HAS__ALREADY__STARTED; + } + else { + myStatus.status() = CommandStatus::VALID; + capture = 1; + FD_ZERO( &readfds); + FD_SET( pcap_fileno( handle), &readfds ); + Install_Handler( &readfds, NULL, NULL, 0.0 ); + } + } + else { + myStatus.errorMessage() = CommandError::THERE__IS__NO__FILTER__SET; + } + } + + else if( send_par.command() == CaptureControl::STOP ) { + myStatus.command() = CommandId::STOPCMD; + if (capture) { + myStatus.status() = CommandStatus::CommandStatus::VALID; + capture = 0; + } + else { + myStatus.errorMessage() = CommandError::CAPTURING__HAS__NOT__STARTED; + } + } + incoming_message( myStatus ); +} + +//////////////////////////////////////////////////////////// +// outgoing_send +//////////////////////////////////////////////////////////// +void PCAPasp__PT::outgoing_send(const PCAPasp__Types::ASP__PCAP__ConfigReq& send_par) { + + struct bpf_program filter; + char errbuf[PCAP_ERRBUF_SIZE]; + bpf_u_int32 mask; + bpf_u_int32 net; + + CHARSTRING myInterface; + CHARSTRING myFilter; + INTEGER myMask; + PCAPasp__Types::ASP__PCAP__ConfigResp myStatus; + + myStatus.command() = CommandId::FILTERCMD; + myStatus.status() = CommandStatus::INVALID; + myStatus.errorMessage() = OMIT_VALUE; + + if (capture) { + myStatus.errorMessage() = CommandError::PORT__IS__ALREADY__CAPTURING; + } + else { + settings = 0; + + Uninstall_Handler(); + if( dumpfile != NULL){ + pcap_dump_close( dumpfile); + dumpfile = NULL; + } + + if (send_par.interface().ispresent()) myInterface = send_par.interface(); + else myInterface = "eth0"; + + if (send_par.filter().ispresent()) myFilter = send_par.filter(); + else myFilter = ""; + + if (send_par.mask().ispresent()) myMask = send_par.mask(); + else myMask = 0xffffff; + + char* myFilterString = new char[strlen(myFilter) + 1]; + strcpy(myFilterString, (const char*)myFilter); + + if( pcap_lookupnet( myInterface, &net, &mask, errbuf) == -1){ + myStatus.errorMessage() = CommandError::ERROR__LOOKING__NET__UP; + } + else { + handle = pcap_open_live( myInterface, 1514, 1, 0, errbuf); + if( handle == NULL ){ + myStatus.errorMessage() = CommandError::ERROR__LIVE__OPENING; + } + else if( pcap_compile( handle, &filter, myFilterString, 0, myMask) == -1){ + myStatus.errorMessage() = CommandError::ERROR__COMPILING__FILTER; + } + else if( pcap_setfilter( handle, &filter) == -1 ){ + myStatus.errorMessage() = CommandError::ERROR__SETTING__FILTER; + } + else if( pcap_setnonblock( handle, 1, errbuf) == -1 ){ + myStatus.errorMessage() = CommandError::ERROR__SETTING__NONBLOCK__MODE; + } + else { + dumpfile = pcap_dump_open( handle, (const char*)send_par.filename()); + if( dumpfile == NULL) { + myStatus.errorMessage() = CommandError::ERROR__OPENING__OUTPUT__FILE; + } + else { + settings = 1; + myStatus.status() = CommandStatus::CommandStatus::VALID; + } + } + } + + if (myFilterString) delete [] myFilterString; + } + incoming_message( myStatus ); +} + +//////////////////////////////////////////////////////////// +// outgoing_send +//////////////////////////////////////////////////////////// +void PCAPasp__PT::outgoing_send(const PCAPasp__Types::ASP__PCAP__DumpReaderFilter& send_par) +{ + PCAPasp__Types::ASP__PCAP__DumpFilterResp respStatus; + struct in_addr srcIp; + struct in_addr destIp; + in_addr_t addr; + bool srcIpAll = false; + int trueFlag = 1; + memset(&srcIp, 0, sizeof(srcIp)); + respStatus.status() = CommandStatus::CommandStatus::VALID; + respStatus.errorMessage() = OMIT_VALUE; + + if (strcmp(send_par.localIp(), "*")==0) + { + srcIpAll = true; + } + else + { + addr = inet_addr( send_par.localIp() ); + + if (addr != (in_addr_t)-1) + memcpy(&(srcIp.s_addr), &addr, sizeof(addr)); + else{ + trueFlag = 0; + respStatus.status() = CommandStatus::INVALID; + respStatus.errorMessage() = DumpFilterError::WRONG__SOURCE__IP; + } + } + + addr = inet_addr( send_par.remoteIp() ); + + if (addr != (in_addr_t)-1) + memcpy(&(destIp.s_addr), &addr, sizeof(addr)); + else{ + trueFlag = 0; + respStatus.status() = CommandStatus::INVALID; + respStatus.errorMessage() = DumpFilterError::WRONG__DESTINATION__IP; + } + + if( trueFlag ){ + for (int i=0; i<send_par.remotePorts().size_of(); i++) { + int port = (int) ( send_par.remotePorts()[i] ); + filter_table.addEntry((int)send_par.messageType(), srcIp, srcIpAll, destIp, (unsigned int) port); + } + } + + incoming_message( respStatus ); +} + +//////////////////////////////////////////////////////////// +// outgoing_send +//////////////////////////////////////////////////////////// +void PCAPasp__PT::outgoing_send(const PCAPasp__Types::ASP__PCAP__MessageReq& send_par) +{ + PCAPasp__Types::ASP__PCAP__MessageResp incoming_msg; + TCPSegment* seg = NULL; + bool ready_message = false; + bool no_more_message = false; + + do { + Peer* act_peer; + for (int i=0; i<peer_list_tcp.length(); i++) { + act_peer = peer_list_tcp.elementAt(i); + int embedded_length=0; + if (act_peer->tcp_buf.length > 0) { // is there anything in the buffer? +log("peer_list_tcp, peer %d", i); + embedded_length=act_peer->get_msg_len(); +log("peer_list_tcp, embedded_length %d", embedded_length); + if(embedded_length>0){ + if (send_par.nextMessage() == act_peer->protocol_type || send_par.nextMessage() == -1) { + ready_message=true; + incoming_msg.status() = Status::VALID__MESSAGE; + incoming_msg.timeStamp() = act_peer->tcp_buf.timestamp; + incoming_msg.contentLength() = embedded_length; + incoming_msg.sourcePort() = act_peer->port_src; + incoming_msg.destinationPort() = act_peer->port_dst; + incoming_msg.sourceIP() = inet_ntoa(act_peer->ip_src); + incoming_msg.destinationIP() = inet_ntoa(act_peer->ip_dst); + incoming_msg.msgtype()=act_peer->protocol_type; + incoming_msg.nextMessage()=OCTETSTRING(embedded_length,act_peer->tcp_buf.get_read_data()); + incoming_message(incoming_msg); + } + act_peer->tcp_buf.set_pos(act_peer->tcp_buf.get_pos()+embedded_length); + act_peer->tcp_buf.cut(); + } + } // if peer's buffer > 0 + if(embedded_length<=0 && act_peer->tcp_buf.closed && !act_peer->tcp_buf.close_sent){ + act_peer->tcp_buf.close_sent=true; + ASP__PCAP__ConnectionClosed apcc_msg; + apcc_msg.protocol() = act_peer->protocol_type; + apcc_msg.destinationPort() = act_peer->port_dst; + apcc_msg.destinationIP() = inet_ntoa(act_peer->ip_dst); + apcc_msg.sourcePort() = act_peer->port_src; + apcc_msg.sourceIP() = inet_ntoa(act_peer->ip_src); + incoming_message(apcc_msg); + } + if(ready_message) break; + } // for each tcp peer + + + if (!ready_message) //if no requested message was found we iterate through the UDP streams as well. + for (int i=0; i<peer_list_udp.length(); i++) { + act_peer = peer_list_udp.elementAt(i); + + if (act_peer->tcp_buf.length > 0) { // is there anything in the buffer? + int embedded_length=act_peer->get_msg_len(); + if(embedded_length>0){ + if (send_par.nextMessage() == act_peer->protocol_type || send_par.nextMessage() == -1) { + ready_message=true; + incoming_msg.status() = Status::VALID__MESSAGE; + incoming_msg.timeStamp() = act_peer->tcp_buf.timestamp; + incoming_msg.contentLength() = embedded_length; + incoming_msg.sourcePort() = act_peer->port_src; + incoming_msg.destinationPort() = act_peer->port_dst; + incoming_msg.sourceIP() = inet_ntoa(act_peer->ip_src); + incoming_msg.destinationIP() = inet_ntoa(act_peer->ip_dst); + incoming_msg.msgtype()=act_peer->protocol_type; + incoming_msg.nextMessage()=OCTETSTRING(embedded_length,act_peer->tcp_buf.get_data()); + incoming_message(incoming_msg); + } + act_peer->tcp_buf.set_pos(embedded_length); + act_peer->tcp_buf.cut(); + } + } // if peer's buffer > 0 + if(ready_message) break; + } // for each udp peer + + if (!ready_message) //if no requested message was found we iterate through the SCTP streams as well. + for (int i=0; i<peer_list_sctp.length(); i++) { + act_peer = peer_list_sctp.elementAt(i); + + if (act_peer->has_message()) { // is there anything in the buffer? + int embedded_length=act_peer->get_first_sctp_data_len(); + if(embedded_length>0){ + if (send_par.nextMessage() == act_peer->protocol_type || send_par.nextMessage() == -1) { + ready_message=true; + incoming_msg.status() = Status::VALID__MESSAGE; + incoming_msg.timeStamp() = act_peer->get_first_sctp_timestamp(); + incoming_msg.contentLength() = embedded_length; + incoming_msg.sourcePort() = act_peer->port_src; + incoming_msg.destinationPort() = act_peer->port_dst; + incoming_msg.sourceIP() = inet_ntoa(act_peer->ip_src); + incoming_msg.destinationIP() = inet_ntoa(act_peer->ip_dst); + incoming_msg.msgtype()=act_peer->protocol_type; + incoming_msg.nextMessage()=OCTETSTRING(embedded_length,act_peer->get_first_sctp_data()); + incoming_message(incoming_msg); + } + act_peer->delete_first_sctp_message(); + } + } // if peer's buffer > 0 + if(ready_message) break; + } // for each sctp peer + + if (!ready_message) { + + act_pt=this; + seg = getNextFilteredSegment(); + if (seg) { + + seg->log(); + + if (seg->seg_type == SCTP_SEG && seg->fin) { //PCAP_ASP_ConnectionClosed must be sent to TTCN in case a TCP connection is terminated + ASP__PCAP__ConnectionClosed apcc_msg; + apcc_msg.protocol() = seg->protocol_type; + apcc_msg.destinationPort() = seg->port_dst; + apcc_msg.destinationIP() = inet_ntoa(seg->ip_dst); + apcc_msg.sourcePort() = seg->port_src; + apcc_msg.sourceIP() = inet_ntoa(seg->ip_src); + incoming_message(apcc_msg); + } + + if (seg->seg_type == TCP_SEG) { + INTEGER port_dst = seg->port_dst; + INTEGER port_src = seg->port_src; + CHARSTRING ip_src = inet_ntoa(seg->ip_src); + CHARSTRING ip_dst = inet_ntoa(seg->ip_dst); + if (!peer_list_tcp.sendSegmentToPeer(seg)) { + //We detected a lost segment. + //Note that, the source and destination directions are exchanged + //because the lost segment was in other direction than the acknowledgment + //via we detected it. + ASP__PCAP__Error ape; + ape.errorType() = PCAPError::LOST__SEGMENT; + ape.sourcePort() = port_dst; + ape.destinationPort() = port_src; + ape.sourceIP() = ip_dst; + ape.destinationIP() = ip_src; + incoming_message(ape); + } + } + else if (seg->seg_type == UDP_SEG) { + peer_list_udp.sendSegmentToPeer(seg); + } + else if (seg->seg_type == SCTP_SEG) { + INTEGER port_dst = seg->port_dst; + INTEGER port_src = seg->port_src; + CHARSTRING ip_src = inet_ntoa(seg->ip_src); + CHARSTRING ip_dst = inet_ntoa(seg->ip_dst); + if (!peer_list_sctp.sendSegmentToPeer(seg)) { + //We detected a lost segment. + //Note that, the source and destination directions are exchanged + //because the lost segment was in other direction than the acknowledgment + //via we detected it. + ASP__PCAP__Error ape; + ape.errorType() = PCAPError::LOST__SEGMENT; + ape.sourcePort() = port_dst; + ape.destinationPort() = port_src; + ape.sourceIP() = ip_dst; + ape.destinationIP() = ip_src; + incoming_message(ape); + } + } + } + else no_more_message = true; + } + + } while (!no_more_message && !ready_message); + + //No more messages in the dump file + if (no_more_message) { + incoming_msg.status() = Status::NO__MESSAGE; + incoming_msg.timeStamp() = OMIT_VALUE; + incoming_msg.contentLength() = OMIT_VALUE; + incoming_msg.sourcePort() = OMIT_VALUE; + incoming_msg.destinationPort() = OMIT_VALUE; + incoming_msg.sourceIP() = OMIT_VALUE; + incoming_msg.destinationIP() = OMIT_VALUE; + incoming_msg.msgtype()= OMIT_VALUE; + incoming_msg.nextMessage() = OMIT_VALUE; + incoming_message(incoming_msg); + if (logging){ + log("stream buffers:"); + peer_list_tcp.dump(); + peer_list_udp.dump(); + } + } +} + +void PCAPasp__PT::outgoing_send(const PCAPasp__Types::ASP__PACP__SetupProtocol& send_par){ + p_data.add_protocol(send_par.protocol__id(), + send_par.getMsgLen__function().ispresent()?send_par.getMsgLen__function()():default_getMsgLen, + send_par.getMsgStartPos__function().ispresent()?send_par.getMsgStartPos__function()():default_getMsgStartPos + + ); +} + + + +//////////////////////////////////////////////////////////// +// getNextFilteredSegment +//////////////////////////////////////////////////////////// +TCPSegment* PCAPasp__PT::getNextFilteredSegment() +{ + TCPSegment* seg = NULL; + int protocol_type; + bool found = false; + bool last = false; + + do { + seg = dump_reader.getNextSegment(); + if (seg) { + protocol_type = filter_table.filter(seg); + log("Check protocol"); + if (protocol_type != NO_PROTOCOL) { + log("found protocol"); + seg->protocol_type = protocol_type; + found = true; + } + else { + log("no protocol"); + delete seg; seg = NULL; + } + } + else { + last = true; + } + } + while (!found && !last); + return seg; +} + +void PCAPasp__PT::log(const char *fmt, ...) { + TTCN_Logger::begin_event(TTCN_DEBUG); + TTCN_Logger::log_event("PCAPasp_PT: "); + va_list ap; + va_start(ap, fmt); + TTCN_Logger::log_event_va_list(fmt, ap); + va_end(ap); + TTCN_Logger::end_event(); +} + + + +//////////////////////////////////////////////////////////// +// TCPSegment implementation +//////////////////////////////////////////////////////////// +TCPSegment::TCPSegment() +{ + payload = NULL; + length = 0; + syn = false; fin = false; + seq_num = 0; + ack_num = 0; + seg_type = TCP_SEG; + protocol_type = NO_PROTOCOL; + timestamp = 0.0; +} + +TCPSegment::~TCPSegment() +{ + if (payload) delete [] payload; +} + +void TCPSegment::put(char* buf, size_t size) +{ + if (size>0) { + if (payload) delete [] payload; + payload = new unsigned char[size]; + memcpy(payload, buf, size); + length = size; + } + else { + if (payload) delete [] payload; + payload = NULL; + length = 0; + } +} + +void TCPSegment::log(const char *fmt, ...) { + TTCN_Logger::begin_event(TTCN_DEBUG); + TTCN_Logger::log_event("TCPSegment: "); + va_list ap; + va_start(ap, fmt); + TTCN_Logger::log_event_va_list(fmt, ap); + va_end(ap); + TTCN_Logger::end_event(); +} + +void TCPSegment::log() { + TTCN_Logger::begin_event(TTCN_DEBUG); + TTCN_Logger::log_event("-=> TCPSegment object: \n"); + TTCN_Logger::log_event("address %p ¦n", this); + TTCN_Logger::log_event("IP src: %s ", inet_ntoa(ip_src)); + TTCN_Logger::log_event("dst: %s\n", inet_ntoa(ip_dst)); + TTCN_Logger::log_event("Port src: %d dst: %d\n", port_src, port_dst); + switch (seg_type) { + case UDP_SEG: + TTCN_Logger::log_event("UDP segment len:%zu\n", length); + break; + case TCP_SEG: + TTCN_Logger::log_event("TCP segment len:%zu seq:%lu ack:%lu", length, seq_num, ack_num); + if (syn) TTCN_Logger::log_event(" SYN\n"); else TTCN_Logger::log_event("\n"); + break; + } + u_char* dataptr = (u_char*) payload; + for (u_int i=1; i<length+1; i++) { + TTCN_Logger::log_event("%.2x ", dataptr[i-1]); + if (i%16==0) TTCN_Logger::log_event("\n"); + } + TTCN_Logger::log_event("\n"); + TTCN_Logger::end_event(); +} + + +//////////////////////////////////////////////////////////// +// DumpReader implementation +//////////////////////////////////////////////////////////// +DumpReader::DumpReader () { + fp = NULL; + frameCounter = 1; +} + +DumpReader::~DumpReader() { + if (fp) pcap_close(fp); +} + +bool DumpReader::open(char* fname) { + if ( (fp = pcap_open_offline(fname, errbuf) ) == NULL) { + TTCN_warning("Error message received: %s", errbuf); + return false; + } + return true; +} + +bool DumpReader::getNext() { + if (fp) { + genlog("DumpReader: Frame: %d", frameCounter); frameCounter++; + int res = pcap_next_ex( fp, &actHeader, &actData); + if ( res >= 0) return true; + else { + if(res == -1) { + TTCN_warning("Error reading the packets: %s", pcap_geterr(fp)); + } + return false; + } + } + else { + TTCN_warning("No capture file is set"); + return false; + } +} + +bool DumpReader::setFilter(char* filter_script, bpf_u_int32 netmask) { + if (strlen(filter_script) != 0) { + if(pcap_compile(fp, &BPFcode, filter_script, 1, netmask)<0) { + TTCN_warning("Unable to compile the filter. check the syntax: %s", filter_script); + return false; + } + if(pcap_setfilter(fp, &BPFcode)<0) { + TTCN_warning("Error setting the filter: %s", filter_script); + return false; + } + } + return true; +} + +bool DumpReader::getNextEthernet() { + bool ether_found = false; + while (!ether_found) { + if (this->getNext()) + { + struct ::ether_header* ether_ptr; + ether_ptr = (struct ::ether_header *) actData; + if ( ntohs (ether_ptr->ether_type) == ETHERTYPE_IP ) + { + ether_found = true; + actEthernetHeader = ether_ptr; + actEthernetData = (u_char*) actData; + actEthernetData += sizeof(struct ::ether_header); + return true; + } + else if ( ntohs (ether_ptr->ether_type) == PCAP_ETHERTYPE_VLAN8021Q ) + { + struct vlan_header* vlan_ptr; + vlan_ptr = (struct vlan_header*) ((u_char*)actData + sizeof(struct ::ether_header)); + if (ntohs(vlan_ptr->vlan_type) == ETHERTYPE_IP) + { + ether_found = true; + actEthernetHeader = ether_ptr; + actEthernetData = (u_char*) actData; + actEthernetData += sizeof(struct ::ether_header) + sizeof(vlan_header); + return true; + } + else TTCN_warning("Not an IP datagram in VLAN 802.1Q packet"); + } + else + { + TTCN_warning("Not an IP datagram or unknown Ethernet header"); + } + } + else break; + } + return false; +} + + +bool DumpReader::getNextIP() { + free_ptr = false; + while (true) { + u_int hlen,version; + unsigned int len; + if (this->getNextEthernet()) { + struct ip_header* ip_ptr; + ip_ptr = (struct ip_header *) actEthernetData; + len = ntohs(ip_ptr->ip_len); + hlen = IP_HL(ip_ptr); + version = IP_V(ip_ptr); + + /* It must be IPv4 */ + if (version == 4) { + /* make sure that the packet is at least as long as the min IP header */ + if (actHeader->caplen > sizeof(struct ip_header)) { + + /* check and see if we got everything. NOTE: we must use + * ip_total_len after this, because we may have captured bytes + * beyond the end of the packet (e.g. ethernet padding). */ + if (actHeader->caplen >= len) { + /* IP_sec decoding */ + if(ip_ptr->ip_p == IPPROTO_ESP){ + if((len-hlen*4)>10) { // minimum size of the ESP + unsigned int spi=(((unsigned int)actEthernetData[hlen*4])<<24)+(((unsigned int)actEthernetData[hlen*4+1])<<16)+(((unsigned int)actEthernetData[hlen*4+2])<<8)+actEthernetData[hlen*4+3]; + ESP_obj *espobj; + if(act_pt->esp.find_esp(spi,espobj)){ + int icv_len=espobj->icv_fv.invoke(OCTETSTRING(hlen*4,(const unsigned char*)actEthernetData), + OCTETSTRING(len-hlen*4,(const unsigned char*)actEthernetData+hlen*4), + espobj->icv_data); + if(icv_len>=0){ // valid ICV + int proto=*((const unsigned char*)actEthernetData+len-icv_len); + OCTETSTRING decrypted_data; + if(espobj->decrypt_fv.invoke(OCTETSTRING(hlen*4,(const unsigned char*)actEthernetData), + OCTETSTRING(len-hlen*4-icv_len-1,(const unsigned char*)actEthernetData+hlen*4), + decrypted_data, + espobj->decrypt_data)){ + ip_ptr->ip_p=proto; + ip_ptr->ip_len=htons(hlen*4+decrypted_data.lengthof()); + unsigned char* dataptr=(unsigned char*)actEthernetData+hlen*4; + memcpy(actEthernetData+hlen*4,(const unsigned char*)decrypted_data,decrypted_data.lengthof()); + if(!act_pt->esp.match_esp(&(ip_ptr->ip_src),(dataptr[0]<<8)+dataptr[1],&(ip_ptr->ip_dst),(dataptr[2]<<8)+dataptr[3],espobj)){ + // The ESP registered for different address + PCAPasp__Types::ASP__PCAP__ESP__Report ret_val; + ret_val.status__code()=PCAPasp__Types::ESP__Status::ESP__WRONG__SPI; + ret_val.spi().set_long_long_val(spi); + ret_val.destinationIP()=inet_ntoa(ip_ptr->ip_dst); + ret_val.destinationPort()=OMIT_VALUE; + ret_val.sourceIP()=inet_ntoa(ip_ptr->ip_src); + ret_val.sourcePort()=OMIT_VALUE; + ret_val.payload__transport()=-1; + act_pt->inc_msg(ret_val); + + } + } else { // decrypt failed + PCAPasp__Types::ASP__PCAP__ESP__Report ret_val; + ret_val.status__code()=PCAPasp__Types::ESP__Status::ESP__DECRYPT__ERROR; + ret_val.spi().set_long_long_val(spi); + ret_val.destinationIP()=inet_ntoa(ip_ptr->ip_dst); + ret_val.destinationPort()=OMIT_VALUE; + ret_val.sourceIP()=inet_ntoa(ip_ptr->ip_src); + ret_val.sourcePort()=OMIT_VALUE; + ret_val.payload__transport()=-1; + act_pt->inc_msg(ret_val); + } + } else { + PCAPasp__Types::ASP__PCAP__ESP__Report ret_val; + ret_val.status__code()=PCAPasp__Types::ESP__Status::ESP__ICV__ERROR; + ret_val.spi().set_long_long_val(spi); + ret_val.destinationIP()=inet_ntoa(ip_ptr->ip_dst); + ret_val.destinationPort()=OMIT_VALUE; + ret_val.sourceIP()=inet_ntoa(ip_ptr->ip_src); + ret_val.sourcePort()=OMIT_VALUE; + ret_val.payload__transport()=-1; + act_pt->inc_msg(ret_val); + } + } else { // no esp data found + PCAPasp__Types::ASP__PCAP__ESP__Report ret_val; + ret_val.status__code()=PCAPasp__Types::ESP__Status::ESP__NOT__DEFINED; + ret_val.spi().set_long_long_val(spi); + ret_val.destinationIP()=inet_ntoa(ip_ptr->ip_dst); + ret_val.destinationPort()=OMIT_VALUE; + ret_val.sourceIP()=inet_ntoa(ip_ptr->ip_src); + ret_val.sourcePort()=OMIT_VALUE; + ret_val.payload__transport()=-1; + act_pt->inc_msg(ret_val); + } + } + } else { +// Needs more test +/* unsigned char* dataptr=(unsigned char*)actEthernetData+hlen*4; + if(act_pt->esp.esp_exists( &(ip_ptr->ip_src),(dataptr[0]<<8)+dataptr[1],&(ip_ptr->ip_dst),(dataptr[2]<<8)+dataptr[3],ip_ptr->ip_p)){ + // ESP data registered for this address + PCAPasp__Types::ASP__PCAP__ESP__Report ret_val; + ret_val.status__code()=PCAPasp__Types::ESP__Status::NOT__ESP__PACKET; + ret_val.spi()=-1; + ret_val.destinationIP()=inet_ntoa(ip_ptr->ip_dst); + ret_val.destinationPort()=OMIT_VALUE; + ret_val.sourceIP()=inet_ntoa(ip_ptr->ip_src); + ret_val.sourcePort()=OMIT_VALUE; + ret_val.payload__transport()=-1; + act_pt->inc_msg(ret_val); + }*/ + } + + if (!(ntohs(ip_ptr->ip_off) & (IP_OFFMASK | IP_MF))) { // no fragmentation + + /* We found it! */ + actIPHeader = ip_ptr; + actIPData = (u_char*) actEthernetData+(hlen << 2); + TTCN_warning("whole IP datagramm"); + return true; + } + else{ + actIPHeader = ip_ptr; + actIPData = (u_char*) actEthernetData+(hlen << 2); + TTCN_warning("fragfmented IP datagramm"); + if(fragment_buffer.add_ip_fragment(&actIPHeader,&actIPData)){ + free_ptr = true; + TTCN_warning("last fragfment IP datagramm"); + return true; + } + } + } + else TTCN_warning("Captured only %d bytes of %d-byte IP datagram", actHeader->caplen, len); + } + else TTCN_warning("Received truncated IP datagram!"); + } + else TTCN_warning("Unknown IP version: %d",version); + } + else break; + } + return false; +} + +TCPSegment* DumpReader::getNextSegment() { + bool segment_found = false; + TCPSegment* ret_seg = NULL; + while (!segment_found) { + if (getNextIP()) { + /* we're only looking for TCP or UDP; throw away everything else */ + if (actIPHeader->ip_p == IPPROTO_TCP) { + segment_found = true; + actTCPHeader = (struct tcphdr *) actIPData; + + // calculate the total length of the TCP header including options + u_int tcp_header_len = TCP_OFF(actTCPHeader) * 4; + actTCPData = (u_char*) actTCPHeader + tcp_header_len; + + // compute the length of the TCP payload + u_int ip_total_len = ntohs(actIPHeader->ip_len); + u_int ip_header_len = IP_HL(actIPHeader) * 4; + u_int tcp_total_len = ip_total_len - ip_header_len; + u_int tcp_data_len = tcp_total_len - tcp_header_len; + + // we return with the needed information + ret_seg = new TCPSegment(); + ret_seg->seg_type = TCP_SEG; + ret_seg->ip_src = actIPHeader->ip_src; + ret_seg->ip_dst = actIPHeader->ip_dst; + ret_seg->port_src = ntohs(actTCPHeader->th_sport); + ret_seg->port_dst = ntohs(actTCPHeader->th_dport); + ret_seg->seq_num = ntohl(actTCPHeader->th_seq); + ret_seg->ack_num = ntohl(actTCPHeader->th_ack); + if (actTCPHeader->th_flags & TH_SYN) ret_seg->syn = true; + if (actTCPHeader->th_flags & TH_FIN) ret_seg->fin = true; + ret_seg->put((char*)actTCPData,(size_t)tcp_data_len); + ret_seg->timestamp = (double)actHeader->ts.tv_sec + (double)actHeader->ts.tv_usec/1000000.0; + if(free_ptr){ + free_ptr=false; + Free(actIPHeader); + Free(actIPData); + } + return ret_seg; + } + else if (actIPHeader->ip_p == IPPROTO_UDP) { + segment_found = true; + actUDPHeader = (struct udphdr *) actIPData; + actUDPData = ((u_char*) (actUDPHeader))+8; + + // compute the length of the UDP payload + u_int udp_total_len = ntohs(actUDPHeader->uh_ulen); + u_int udp_data_len = udp_total_len-8; + + // we return with the necessery information + ret_seg = new TCPSegment(); + ret_seg->seg_type = UDP_SEG; + ret_seg->ip_src = actIPHeader->ip_src; + ret_seg->ip_dst = actIPHeader->ip_dst; + ret_seg->port_src = ntohs(actUDPHeader->uh_sport); + ret_seg->port_dst = ntohs(actUDPHeader->uh_dport); + ret_seg->put((char*) actUDPData, (size_t) udp_data_len); + ret_seg->timestamp = (double)actHeader->ts.tv_sec + (double)actHeader->ts.tv_usec/1000000.0; + if(free_ptr){ + free_ptr=false; + Free(actIPHeader); + Free(actIPData); + } + return ret_seg; + } + else if (actIPHeader->ip_p == IPPROTO_SCTP) { + segment_found = true; + actSCTPHeader = (struct sctphdr *) actIPData; + + u_int sctp_header_len = 12; + actSCTPData = (u_char*) actSCTPHeader + sctp_header_len; + + // compute the length of the SCTP payload + u_int ip_total_len = ntohs(actIPHeader->ip_len); + u_int ip_header_len = IP_HL(actIPHeader) * 4; + u_int sctp_total_len = ip_total_len - ip_header_len; + u_int sctp_data_len = sctp_total_len - sctp_header_len; + + ret_seg = new TCPSegment(); + ret_seg->seg_type = SCTP_SEG; + ret_seg->ip_src = actIPHeader->ip_src; + ret_seg->ip_dst = actIPHeader->ip_dst; + ret_seg->port_src = ntohs(actSCTPHeader->sh_sport); + ret_seg->port_dst = ntohs(actSCTPHeader->sh_dport); + ret_seg->put((char*) actSCTPData, (size_t) sctp_data_len); + ret_seg->timestamp = (double)actHeader->ts.tv_sec + (double)actHeader->ts.tv_usec/1000000.0; + if(free_ptr){ + free_ptr=false; + Free(actIPHeader); + Free(actIPData); + } + return ret_seg; + } + } + else break; + } + return NULL; +} + + +//////////////////////////////////////////////////////////// +// TCPBuffer implementation +//////////////////////////////////////////////////////////// +TCPBuffer::TCPBuffer() +{ + length = 0; + buffer = NULL; + read_poi = buffer; + total_length = 0; + lost_length = 0; + closed = false; + close_sent = false; +} + +TCPBuffer::~TCPBuffer() +{ + if (buffer) delete [] buffer; +} + +void TCPBuffer::clear() +{ + log("cleared"); + length = 0; + if (buffer) delete [] buffer; + buffer = NULL; read_poi = buffer; + closed = false; + close_sent = false; +} + +bool TCPBuffer::ack(TCPSegment* segment) +{ + if (segment != NULL) { + if (segment->seg_type == TCP_SEG) { + //If an acknowledgement arrives that is larger than the last + //stored byte in the buffer, than a TCP segment must have been lost previously. + //Because it would prevent the assembly of the stream, + //we hop the gap - that was created by the lost segment - by clearing the buffer + //It doesn't contain a valid message, because it would have already been processed then. + //and setting it to sequence number of the acknowledgement. + if (segment->ack_num > (seq_num + length+ closed)?1:0) { + TTCN_warning("Lost TCP segment detected!: Current sequence number: %lu, size: %zu, acknowledgement number: %lu, closed: %s", seq_num, length, segment->ack_num, closed?"true":"false"); + // - The buffer should be cleared. + // - Saved segments must be dropped if their seq_num is smaller than the ack_num -> this one goes to the Peer class + clear(); + lost_length+=(segment->ack_num-seq_num-length); + total_length+=(segment->ack_num-seq_num-length); + seq_num = segment->ack_num; + return false; + } + } + } + return true; +} + +void TCPBuffer::log_stat(){ + log("Total processed octets, including lost octets: %lu",total_length); + log("Total lost octets: %lu",lost_length); + if(total_length>0){ + log("Quality of the recovered stream: %lu%%",(total_length-lost_length)*100/total_length); + } else { + log("Quality of the recovered stream: N/A"); + } +} + +bool TCPBuffer::put(TCPSegment* segment) +{ + if (segment != NULL) { + + switch (segment->seg_type) { + + case TCP_SEG: { + log("tcp segment is: seq:%lu size:%ld fin:%s\n", seq_num, length, segment->fin?"yes":"no"); + if (segment->fin) {closed=true;} + if ( (segment->seq_num <= seq_num + length ) && + (segment->seq_num >= seq_num) && + (segment->length > 0) ) { //The segment can be appended + size_t old_pos = get_pos(); + size_t new_size = segment->seq_num - seq_num + segment->length; + unsigned char* tmp_buf = new unsigned char[new_size]; + size_t from_orig_buffer = segment->seq_num - seq_num; + memcpy(tmp_buf, buffer, from_orig_buffer); + memcpy( (tmp_buf + from_orig_buffer), segment->payload, segment->length); + if (buffer) delete [] buffer; + buffer = tmp_buf; + length = new_size; + read_poi = buffer + old_pos; + timestamp = segment->timestamp; + total_length+=segment->length; + if (logging){ + log("tcp segment is appended: seq:%lu size:%ld old_pos:%lu from_orig:%lu\n", seq_num, length, old_pos, from_orig_buffer); + } + if (segment) { + delete segment; + segment = NULL; + } + return true; + } + else return false; + } + break; + + case UDP_SEG: { + if (segment->length>0) { + size_t old_pos = get_pos(); + size_t new_size = length + segment->length; + char* tmp_buf = new char[new_size]; + memcpy(tmp_buf, buffer, length); + memcpy(tmp_buf + length, segment->payload, segment->length); + delete [] buffer; + buffer = (unsigned char*) tmp_buf; + length = new_size; + read_poi = buffer + old_pos; + timestamp = segment->timestamp; + if (segment) { + delete segment; + segment = NULL; + } + if (logging) { + log("udp segment is appended: seq:%lu size:%ld\n", seq_num, length); + } + return true; + } + else return false; + } + break; + } + } + else return false; + return false; +} + +void TCPBuffer::rewind() +{ + read_poi = buffer; +} + +size_t TCPBuffer::get_pos() +{ + return read_poi - buffer; +} + +void TCPBuffer::set_pos(size_t pos) +{ + if (pos > length) read_poi = buffer + length; + else read_poi = buffer + pos; +} + +size_t TCPBuffer::get_len() +{ + return length; +} + +unsigned char* TCPBuffer::get_data() +{ + return buffer; +} + +size_t TCPBuffer::get_read_len() +{ + return length - get_pos(); +} + +unsigned char* TCPBuffer::get_read_data() +{ + return read_poi; +} + +void TCPBuffer::cut() +{ + size_t new_size = get_read_len(); + int seq_offset = get_pos(); + char* tmp_buf = new char[new_size]; + if (new_size) memcpy(tmp_buf, read_poi, new_size); + delete [] buffer; + seq_num = seq_num + seq_offset; + buffer = (unsigned char*) tmp_buf; + read_poi = buffer; + length = new_size; + if (logging) { + log("buffer is cut: seq:%lu size:%ld\n", seq_num, length); + dump(); + } +} + +void TCPBuffer::cut(size_t cut_bytes) { + if (cut_bytes > get_len()) cut_bytes = get_len(); + size_t new_size = get_len()-cut_bytes; + int seq_offset = cut_bytes; + char* tmp_buf = new char[new_size]; + if (new_size) memcpy(tmp_buf, buffer+cut_bytes, new_size); + delete [] buffer; + seq_num = seq_num + seq_offset; + buffer = (unsigned char*) tmp_buf; + read_poi = buffer; + length = new_size; + if (logging) { + log("buffer is cut: seq:%lu size:%ld\n", seq_num, length); + dump(); + } +} + +void TCPBuffer::log(const char *fmt, ...) { + TTCN_Logger::begin_event(TTCN_DEBUG); + TTCN_Logger::log_event("TCPBuffer: "); + va_list ap; + va_start(ap, fmt); + TTCN_Logger::log_event_va_list(fmt, ap); + va_end(ap); + TTCN_Logger::end_event(); +} + +void TCPBuffer::dump() { + TTCN_Logger::begin_event(TTCN_DEBUG); + TTCN_Logger::log_event("TCPBuffer: seq:%lu size:%d\n", seq_num, (int) length); + unsigned char* dataptr = buffer; + for (u_int i=1; i<length+1; i++) { + TTCN_Logger::log_event("%.2x ", dataptr[i-1]); + if (i%16==0) TTCN_Logger::log_event("\n"); + } + TTCN_Logger::log_event("\n"); + TTCN_Logger::end_event(); +} + + +//////////////////////////////////////////////////////////// +// Vector implementation +//////////////////////////////////////////////////////////// +template < class Type > Vector < Type >::Vector () { + size = 0; + actual = 0; + ar = NULL; +} + +template < class Type > Vector < Type >::Vector (Vector < Type > &a) { + ar = new Type *[size = a.size]; + for (int i = 0; i < a.size; i++) + ar[i] = a.ar[i]; +} + +template < class Type > Vector < Type >::~Vector () { + if (ar) + delete[]ar; +} + +template < class Type > Vector < Type > &Vector < Type >::operator = (Vector < Type > &a) { + if (this != &a) { + if (ar) + delete[]ar; + ar = new Type *[size = a.size]; + for (int i = 0; i < a.size; i++) + ar[i] = a.ar[i]; + } + return *this; +} + +template < class Type > Type & Vector < Type >::operator[](int idx) { + if (idx >= size) { + Type **nar = new Type *[idx + 1]; + if (ar) { + for (int i = 0; i < size; i++) + nar[i] = ar[i]; + for (int j = size; j <= idx; j++) + nar[j] = NULL; + delete[]ar; + } + size = idx + 1; + ar = nar; + } + actual = idx; + return *ar[idx]; +} + +template < class Type > Type * Vector < Type >::elementAt (int idx) { + actual = idx; + return &((*this)[idx]); +} + +template < class Type > bool Vector < Type >::removeElementAt (int idx) { + if ((idx < size) && size > 1) { + Type **nar = new Type *[size - 1]; + if (ar) { + for (int i = 0; i < idx; i++) + nar[i] = ar[i]; + for (int j = idx; j < size - 1; j++) + nar[j] = ar[j + 1]; + delete[]ar; + } + size = size - 1; + ar = nar; + actual = idx - 1; + return true; + } + else { + if (idx >= size) + return false; + if (size == 1) { + delete ar; + ar = NULL; + size = 0; + return true; + } + } + return false; +} + +template < class Type > bool Vector < Type >::remove (int idx) { + return removeElementAt (idx); +} + +template < class Type > int Vector < Type >::length () { + return size; +} + +template < class Type > void Vector < Type >::append (Type * ptr) { + Type **nar = new Type *[size + 1]; + if (ar) { + for (int i = 0; i < size; i++) + nar[i] = ar[i]; + delete[]ar; + } + ar = nar; + ar[size] = ptr; + actual = size; + size++; +} + +template < class Type > void Vector < Type >::addElement (Type * ptr) { + append (ptr); +} + +template < class Type > bool Vector < Type >::remove (Type * ptr) { + for (int i = 0; i < size; i++) { + if (ar[i] == ptr && ptr != NULL) { + return removeElementAt (i); + } + } + return false; +} + +template < class Type > bool Vector < Type >::removeElement (Type * ptr) { + return remove (ptr); +} + +template < class Type > bool Vector < Type >::removeCurrent () { + return removeElementAt (actual); +} + +template < class Type > bool Vector < Type >::removeRef (Type * ptr) { + return remove (ptr); +} + +template < class Type > int Vector < Type >::find (Type * ptr) { + for (int i = 0; i < size; i++) { + if (ar[i] == ptr) { + actual = i; + return i; + } + } + return -1; +} + +template < class Type > Type * Vector < Type >::first () { + if (ar) { + actual = 0; + return ar[0]; + } + else + return NULL; +} + +template < class Type > Type * Vector < Type >::last () { + if (ar) { + actual = size - 1; + return ar[size - 1]; + } + else + return NULL; +} + +template < class Type > Type * Vector < Type >::next () { + if (ar) { + actual++; + if (actual < size) + return ar[actual]; + else { + actual = 0; + return NULL; + } + } + else + return NULL; +} + +template < class Type > Type * Vector < Type >::current () { + if (ar) { + if (actual < size) { + return ar[actual]; + } else + return NULL; + } + else + return NULL; +} + +template < class Type > Type * Vector < Type >::prev () { + if (ar) { + if ((actual > 1) && (actual < size)) { + actual--; + return ar[actual]; + } + } + else + return NULL; +} + +template < class Type > bool Vector < Type >::isEmpty () { + if (size == 0){ + return true;} + return false; +} + +template < class Type > void Vector < Type >::destruct () { + if (ar) + for (int i = 0; i < size; i++) { + delete ar[i]; + } + if (ar) delete[]ar; + size = 0; actual = 0; + ar = NULL; +} + + +//////////////////////////////////////////////////////////// +// Peer implementation +//////////////////////////////////////////////////////////// +Peer::Peer() +{ + protocol_type = NO_PROTOCOL; + out_of_sync = true; + is_sctp = false; +} + +Peer::Peer(TCPSegment* segment) +{ + if (segment != NULL) { + port_src = segment->port_src; + port_dst = segment->port_dst; + ip_src = segment->ip_src; + ip_dst = segment->ip_dst; + if(segment->seg_type!=SCTP_SEG){ + is_sctp=false; + if (segment->syn) { + tcp_buf.seq_num = segment->seq_num + 1; + } + else { + tcp_buf.seq_num = segment->seq_num; + } + } else { +// stream_list.add_segment_to_stream(segment); + is_sctp= true; + } + protocol_type = segment->protocol_type; + transport_type = segment->seg_type; + out_of_sync = true; + log("created"); + } +} + +Peer::~Peer() +{ + seg_list.destruct(); +} + +void Peer::reset() +{ + tcp_buf.clear(); + seg_list.destruct(); + out_of_sync = true; + log("reset"); +} + +void Peer::init(TCPSegment* segment) +{ + if (segment != NULL) { + port_src = segment->port_src; + port_dst = segment->port_dst; + ip_src = segment->ip_src; + ip_dst = segment->ip_dst; + if(segment->seg_type!=SCTP_SEG){ + is_sctp=false; + if (segment->syn) { + tcp_buf.seq_num = segment->seq_num + 1; + } + else { + tcp_buf.seq_num = segment->seq_num; + } + } else { +// stream_list.add_segment_to_stream(segment); + is_sctp= true; + } + protocol_type = segment->protocol_type; + transport_type = segment->seg_type; + out_of_sync = true; + tcp_buf.put(segment); + log("initialized"); + } +} + +bool Peer::compare(TCPSegment* segment) +{ + if (segment != NULL) + if ( (port_src == segment->port_src) && (port_dst == segment->port_dst) ) + if (memcmp(&ip_src,&(segment->ip_src),sizeof(struct in_addr))==0) + if (memcmp(&ip_dst,&(segment->ip_dst),sizeof(struct in_addr))==0) + return true; + + + return false; +} + +bool Peer::sentBy(TCPSegment* segment) +{ + if (segment != NULL) + if ( (port_src == segment->port_dst) && (port_dst == segment->port_src) ) + if (memcmp(&ip_src,&(segment->ip_dst),sizeof(struct in_addr))==0) + if (memcmp(&ip_dst,&(segment->ip_src),sizeof(struct in_addr))==0) + return true; + + return false; +} + +int Peer::get_msg_len(){ + if(is_sctp){ + if(has_message()){ + return get_first_sctp_data_len(); + } + } else if(!out_of_sync){ + tf__getMsgLen getlen=p_data.get_f_getMsgLen(protocol_type); + return getlen.invoke( + OCTETSTRING(tcp_buf.get_read_len(),tcp_buf.get_read_data()), + tcp_buf.closed, + transport_type + ); + } + return -1; +} + +bool Peer::ack(TCPSegment* segment) +{ + if (segment != NULL) { + //In case the acknowledgment is larger than the last stored byte + //the tcp_buf.ack clears the buffer and sets the seq_num to the ack_num + if(!is_sctp){ + if (!tcp_buf.ack(segment)) { + TCPSegment* seg; + + log("acknowledgement recovering: out of sync"); + out_of_sync = true; + //First we locate that saved segment which has the smallest seq_num + TCPSegment* first_saved_segment = NULL; + int first_saved_segment_idx = 0; + + if (seg_list.length() > 0) { + + for (int i=0; i<seg_list.length(); i++) { + seg = seg_list.elementAt(i); + if (first_saved_segment) { + if (seg->seq_num < first_saved_segment->seq_num) { + first_saved_segment = seg; + first_saved_segment_idx = i; + } + } + else { + first_saved_segment = seg; + first_saved_segment_idx = i; + } + } + + //Then we put it into the buffer: + if (first_saved_segment) { + seg_list.removeElementAt(first_saved_segment_idx); + tcp_buf.seq_num = first_saved_segment->seq_num; + tcp_buf.put(first_saved_segment); + + //Next we try to put every other saved segments into the buffer + //if there is any + if (!seg_list.isEmpty()) { + log("number of unprocessed segments: %d", seg_list.length()); + //Let's try to put the unprocessed segments into the buffer: + bool successful_insertion = false; + do { + successful_insertion = false; + TCPSegment* seg = NULL; + for (int i=0; i<seg_list.length(); i++) { + seg = seg_list.elementAt(i); + log("Trying to insert: seq_num: %lu, length: %d", seg->seq_num, seg->length); + if (tcp_buf.put(seg)) { + log("unprocessed segment inserted"); + successful_insertion = true; + //delete seg; seg = NULL; + seg_list.removeElementAt(i); + } + // If we won't be able to put it, we drop it + else if (seg->seq_num < tcp_buf.seq_num) { + TTCN_warning("Unprocessed segment dropped during ack recovering: IP src: %s, dst: %s; Port src: %d, dst: %d seq: %lu", + inet_ntoa(seg->ip_src), + inet_ntoa(seg->ip_dst), + seg->port_src, + seg->port_dst, + seg->seq_num + ); + seg_list.removeElementAt(i); + delete seg; seg = NULL; + } + } + } + while (successful_insertion && seg_list.length()); + } + } + tcp_buf.dump(); + log("out_of_sync: %d",out_of_sync); + log("protocol_type: %d",protocol_type); + if (out_of_sync ) { + out_of_sync = !tryToResync(); + log("out_of_sync: %d",out_of_sync); + tcp_buf.dump(); + } + } + else { + tcp_buf.seq_num = segment->ack_num; + TCPSegment* seg = NULL; + for (int i=0; i<seg_list.length(); i++) { + seg = seg_list.elementAt(i); + if (seg->seq_num < tcp_buf.seq_num) { + TTCN_warning("Unprocessed segment dropped during ack recovering: IP src: %s, dst: %s; Port src: %d, dst: %d seq: %lu", + inet_ntoa(seg->ip_src), + inet_ntoa(seg->ip_dst), + seg->port_src, + seg->port_dst, + seg->seq_num + ); + seg_list.removeElementAt(i); + delete seg; seg = NULL; + } + } + } + return false; + } + } else { + stream_list.ack(segment); + } + } + return true; +} + +void Peer::put(TCPSegment* segment) +{ + if (segment != NULL) { + if (compare(segment) && ((segment->length > 0) || segment->fin)) { + if(is_sctp){ + stream_list.add_segment_to_stream(segment); + delete segment; + segment = NULL; + } else { + if (tcp_buf.put(segment)) { + log("segment inserted"); + if (!seg_list.isEmpty()) { + log("number of unprocessed segments: %d", seg_list.length()); + //Let's try to put the unprocessed segments into the buffer: + bool successful_insertion = false; + do { + successful_insertion = false; + TCPSegment* seg = NULL; + for (int i=0; i<seg_list.length(); i++) { + seg = seg_list.elementAt(i); + log("Trying to insert: seq_num: %lu, length: %d", seg->seq_num, seg->length); + if (tcp_buf.put(seg)) { + log("unprocessed segment inserted"); + successful_insertion = true; + seg_list.removeElementAt(i); + } + // If we won't be able to put it, we drop it + else if (seg->seq_num < tcp_buf.seq_num) { + TTCN_warning("Unprocessed TCP segment is dropped: IP src: %s, dst: %s; Port src: %d, dst: %d seq: %lu", + inet_ntoa(seg->ip_src), + inet_ntoa(seg->ip_dst), + seg->port_src, + seg->port_dst, + seg->seq_num + ); + seg_list.removeElementAt(i); + delete seg; seg = NULL; + } + } + } + while (successful_insertion && seg_list.length()); + } + tcp_buf.dump(); + log("out_of_sync: %d",out_of_sync); + log("protocol_type: %d",protocol_type); + if (out_of_sync ) { + out_of_sync = !tryToResync(); + log("out_of_sync: %d",out_of_sync); + tcp_buf.dump(); + } + } + else { + if (segment->seq_num < tcp_buf.seq_num) { + TTCN_warning("TCP segment is dropped: IP src: %s, dst: %s; Port src: %d, dst: %d seq: %lu", + inet_ntoa(segment->ip_src), + inet_ntoa(segment->ip_dst), + segment->port_src, + segment->port_dst, + segment->seq_num + ); + delete segment; segment = NULL; + } + else if (segment->length > 0) { + log("segment saved as unprocessed"); + seg_list.addElement(segment); + } else {delete segment; segment = NULL;} + } + } // is_sctp + } else {delete segment; segment = NULL;} // end of if (compare(segment) && (segment->length > 0)) + } // end of if (segment != NULL) +} + + + +bool Peer::tryToResync() +{ + log("trying to resync"); + bool found = false; + if (tcp_buf.get_read_len()>0) { + tf__getMsgStartPos getstartpos=p_data.get_f_getMsgStartPos(protocol_type); + int startpos=getstartpos.invoke( + OCTETSTRING(tcp_buf.get_read_len(),tcp_buf.get_read_data()), + tcp_buf.closed, + transport_type + ); + if(startpos>=0){ + found=true; + tcp_buf.set_pos(tcp_buf.get_pos()+startpos); + log("syncronized, Protocol: %d, position %d",protocol_type,startpos); + } else { + log("not syncronized, Protocol: %d, position %d",protocol_type,startpos); + } + } + return found; +} + +bool Peer::has_message(){ + return stream_list.has_message(); +} +unsigned char* Peer::get_first_sctp_data(){ + return stream_list.get_first_sctp_data(); +} +size_t Peer::get_first_sctp_data_len(){ + return stream_list.get_first_sctp_data_len(); +} +double Peer::get_first_sctp_timestamp(){ + return stream_list.get_first_sctp_timestamp(); +} +void Peer::delete_first_sctp_message(){ + return stream_list.delete_first_sctp_message(); +} + +void Peer::log_stat(){ + log("Connection statistic:"); + if(is_sctp){ + stream_list.log_stat(); + } else { + tcp_buf.log_stat(); + } +} + + +void Peer::dump() { + if(is_sctp){ + stream_list.dump(); + } else { + log("prot_type: %d, seg_list_length: %d", protocol_type, seg_list.length()); + tcp_buf.dump(); + } +} + +void Peer::log(const char *fmt, ...) { + if (logging) { + TTCN_Logger::begin_event(TTCN_DEBUG); + TTCN_Logger::log_event("Peer (%d->%d): ", port_src, port_dst); + va_list ap; + va_start(ap, fmt); + TTCN_Logger::log_event_va_list(fmt, ap); + va_end(ap); + TTCN_Logger::end_event(); + } +} + + +//////////////////////////////////////////////////////////// +// PeerList implementation +//////////////////////////////////////////////////////////// +PeerList::PeerList() +{ + tcp = true; +} + +PeerList::~PeerList() +{ + peer_list.destruct(); +} + +bool PeerList::sendSegmentToPeer(TCPSegment* segment) +{ + bool ret = true; + if (segment!=NULL) { + //If it is a TCP segment, we examine if the acknowledgement + //number of the segment in order to detect lost segments + if (segment->seg_type == TCP_SEG || segment->seg_type == SCTP_SEG) { + Peer* other_peer; + other_peer = getOtherPeer(segment); + if (other_peer) + if (!other_peer->ack(segment)) + ret = false; + } + + //Sending the segment to the destination peer + Peer* act_peer; + act_peer = getPeer(segment); + if (act_peer) { + if (segment->syn) { + // We have detected this stream already, and now it is + // re-initialized + log("sendSegmentToPeer: re-initialization"); + act_peer->reset(); + act_peer->init(segment); + } + else { + // We have already detected this stream and now we send it + // the actual segment + log("sendSegmentToPeer: sending to corresponding peer"); + act_peer->put(segment); + } + } + else { + // We haven't detected this stream yet, therefore we must create the + // Peer object that will handle this. + log("sendSegmentToPeer: creating new stream"); + addPeer(segment); + } + } + return ret; // return false if there is an acknoledgement mismatch +} + +void PeerList::addPeer(TCPSegment* segment) +{ + Peer* new_peer; + new_peer = new Peer(segment); + peer_list.addElement(new_peer); + new_peer->put(segment); +} + +Peer* PeerList::getPeer(TCPSegment* segment) +{ + Peer* peer_poi = NULL; + for (int i=0; i<peer_list.length(); i++) { + peer_poi = peer_list.elementAt(i); + if (peer_poi->compare(segment)) return peer_poi; + } + return NULL; +} + +void PeerList::log_stat(){ + Peer* peer_poi = NULL; + for (int i=0; i<peer_list.length(); i++) { + peer_poi = peer_list.elementAt(i); + peer_poi->log_stat(); + } +} + + +Peer* PeerList::getOtherPeer(TCPSegment* segment) +{ + Peer* peer_poi = NULL; + for (int i=0; i<peer_list.length(); i++) { + peer_poi = peer_list.elementAt(i); + if (peer_poi->sentBy(segment)) return peer_poi; + } + return NULL; +} + +void PeerList::setType(bool t) { + tcp = t; +} + +Peer* PeerList::elementAt(int i) { + return peer_list.elementAt(i); +} + +int PeerList::length() { + return peer_list.size; +} + +void PeerList::dump() { + log("#of peers: %d",peer_list.size); + Peer* peer_poi; + for (int i=0; i<peer_list.length(); i++) { + peer_poi = peer_list.elementAt(i); + peer_poi->dump(); + } +} + +void PeerList::log(const char *fmt, ...) { + + if (logging) { + TTCN_Logger::begin_event(TTCN_DEBUG); + TTCN_Logger::log_event("PeerList ("); + if (tcp) + TTCN_Logger::log_event("TCP"); + else + TTCN_Logger::log_event("UDP or SCTP"); + TTCN_Logger::log_event("): "); + va_list ap; + va_start(ap, fmt); + TTCN_Logger::log_event_va_list(fmt, ap); + va_end(ap); + TTCN_Logger::end_event(); + } +} + + +//////////////////////////////////////////////////////////// +// FilterEntry implementation +//////////////////////////////////////////////////////////// +FilterEntry::FilterEntry(int protocol, struct in_addr sip, bool sip_all, struct in_addr dip, unsigned int rport) +{ + protocol_type = protocol; + ip_src = sip; + ip_src_all = sip_all; + ip_dst = dip; + port_dst = rport; +} + +FilterEntry::~FilterEntry() { +} + +bool FilterEntry::compare(TCPSegment* seg) { + if (seg != NULL) { + if ( (port_dst == seg->port_dst) ) + { + if (ip_src_all) + if (memcmp(&ip_dst,&(seg->ip_dst),sizeof(struct in_addr))==0) + return true; + else return false; + else if (memcmp(&ip_src,&(seg->ip_src),sizeof(struct in_addr))==0) + if (memcmp(&ip_dst,&(seg->ip_dst),sizeof(struct in_addr))==0) + return true; + else return false; + else return false; + } + else if ( (port_dst == seg->port_src) ) + { + if (ip_src_all) + if (memcmp(&ip_dst,&(seg->ip_src),sizeof(struct in_addr))==0) + return true; + else return false; + else if (memcmp(&ip_src,&(seg->ip_dst),sizeof(struct in_addr))==0) + if (memcmp(&ip_dst,&(seg->ip_src),sizeof(struct in_addr))==0) + return true; + else return false; + else return false; + } + else return false; + } + else return false; +} + +void FilterEntry::log(const char *fmt, ...) { + TTCN_Logger::begin_event(TTCN_DEBUG); + TTCN_Logger::log_event("FilterEntry: "); + va_list ap; + va_start(ap, fmt); + TTCN_Logger::log_event_va_list(fmt, ap); + va_end(ap); + TTCN_Logger::end_event(); +} + + +//////////////////////////////////////////////////////////// +// FilterTable implementation +//////////////////////////////////////////////////////////// +FilterTable::FilterTable() {} + +FilterTable::~FilterTable() +{ + entry_list.destruct(); +} + +void FilterTable::addEntry(int protocol, struct in_addr sip, bool sip_all, struct in_addr dip, unsigned int rport) +{ + FilterEntry* new_entry; + new_entry = new FilterEntry(protocol, sip, sip_all, dip, rport); + entry_list.addElement(new_entry); + log("adding prot: %d, rPort:%d, sIP:%s", protocol, rport, inet_ntoa(sip)); + log(" dIP:%s", inet_ntoa(dip)); +} + +int FilterTable::filter(TCPSegment* segment) +{ + if (segment) { + if (noFilter) return 1; + FilterEntry* entry_poi = NULL; + for (int i=0; i<entry_list.length(); i++) { + entry_poi = entry_list.elementAt(i); + if (entry_poi->compare(segment)) return entry_poi->protocol_type; + } + } + return NO_PROTOCOL; +} + +void FilterTable::log(const char *fmt, ...) { + if (logging) { + TTCN_Logger::begin_event(TTCN_DEBUG); + TTCN_Logger::log_event("FilterTable: "); + va_list ap; + va_start(ap, fmt); + TTCN_Logger::log_event_va_list(fmt, ap); + va_end(ap); + TTCN_Logger::end_event(); + } +} + + +//////////////////////////////////////////////////////////// +// Fragment implementation +//////////////////////////////////////////////////////////// +IP_fragment::IP_fragment(){ + id=0; + buffer=NULL; + header=NULL; + buffer_len=0; + data_len=0; +} + +IP_fragment::~IP_fragment(){ + if(buffer) Free(buffer); + if(header) Free(header); + holes.destruct(); +} + +void IP_fragment::clear(){ + if(buffer) Free(buffer); + if(header) Free(header); + id=0; + buffer=NULL; + header=NULL; + buffer_len=0; + data_len=0; + holes.destruct(); +} + +bool IP_fragment::add_fragment(struct ip_header* IPHeader, + u_char* IPData){ + Holes_list new_holes; + u_int16_t fr_first=(ntohs(IPHeader->ip_off) & IP_OFFMASK)<<3; + u_int16_t fr_len=ntohs(IPHeader->ip_len)-(IP_HL(IPHeader)<<2); + u_int16_t fr_last=fr_first+fr_len-1; + + if(holes.isEmpty()){ // first arrived fragment + id=IPHeader->ip_id; + Hole* new_hole=new Hole(0,c_infinite); + holes.append(new_hole); + } + if(buffer_len<fr_last){ + buffer=(u_char*)Realloc(buffer,(fr_last+1)*sizeof(u_char)); + buffer_len=fr_last+1; + } + memcpy(buffer+fr_first,IPData,fr_len); + + if(!fr_first){ // first fragment, store header + header=(ip_header*)Malloc(sizeof(ip_header)); + memcpy(header,IPHeader,sizeof(ip_header)); + } + + Hole* hole_ptr=holes.first(); + while(hole_ptr){ + if(fr_first<=hole_ptr->last && fr_last>=hole_ptr->first){ + holes.removeCurrent(); + if(fr_first>hole_ptr->first){ + Hole* new_hole=new Hole(hole_ptr->first,fr_first-1); + new_holes.append(new_hole); + } + if(fr_last<hole_ptr->last && (ntohs(IPHeader->ip_off) & IP_MF)){ + Hole* new_hole=new Hole(fr_last+1,hole_ptr->last); + new_holes.append(new_hole); + } + delete hole_ptr; + } + hole_ptr=holes.next(); + } + if(!new_holes.isEmpty ()){ + for(int a=0;a<new_holes.length();a++) holes.append(&new_holes[a]); + } + + return holes.isEmpty (); +} + + +bool IP_fragment::get_fragment(struct ip_header** IPHeader, + u_char** IPData){ + if(header){ + *IPHeader=(ip_header*)Malloc(sizeof(ip_header)); + memcpy(*IPHeader,header,sizeof(ip_header)); + } + if(buffer_len){ + (*IPHeader)->ip_len=htons(buffer_len+(IP_HL(*IPHeader)<<2)); + *IPData=(u_char*)Malloc(buffer_len*sizeof(u_char)); + memcpy(*IPData,buffer,buffer_len*sizeof(u_char)); + } + return header && holes.isEmpty(); +} + +IP_fragments::IP_fragments(){ +} + +IP_fragments::~IP_fragments(){ + clear(); +} + +void IP_fragments::clear(){ + packet_list.destruct(); +} + +bool IP_fragments::check(){ + return !packet_list.isEmpty(); +} + +bool IP_fragments::add_ip_fragment(struct ip_header** IPHeader, + u_char** IPData){ + int packed_id; + for(packed_id=0;packed_id<packet_list.length();packed_id++){ + if(packet_list[packed_id].id==(*IPHeader)->ip_id) break; + } + + if(packed_id==packet_list.length()){ + IP_fragment* new_fr=new IP_fragment; + packet_list.append(new_fr); + } + + if(packet_list[packed_id].add_fragment(*IPHeader,*IPData)){ + packet_list[packed_id].get_fragment(IPHeader,IPData); + IP_fragment* fr= &packet_list[packed_id]; + packet_list.removeElementAt(packed_id); + delete fr; + return true; + } + return false; + +} + +void decode_sctp(TCPSegment* segment, SCTP_chunk_list& list){ + + unsigned char* payload=segment->payload; + int length=segment->length; + int type; + unsigned char flags; + int idx=0; + size_t chunk_length; + while(length>0){ + type=payload[0]; + flags=payload[1]; + chunk_length=((size_t)(payload[2])<<8)+payload[3]; + switch(type){ + case 0: // Data +genlog("Data segment"); + list.append(new SCTP_chunk); + list.current()->type=type; +genlog("Data segment flags %Xd",flags); + list.current()->flags=flags; + list.current()->length=chunk_length; + list.current()->data.data.tsn=(payload[4]<<24)+(payload[5]<<16)+(payload[6]<<8)+payload[7]; +genlog("Data segment tsn %Xd",list.current()->data.data.tsn); + list.current()->data.data.sid=(payload[8]<<8)+payload[9]; +genlog("Data segment sid %Xd",list.current()->data.data.sid); + list.current()->data.data.ssn=(payload[10]<<8)+payload[11]; +genlog("Data segment ssn %Xd",list.current()->data.data.ssn); + list.current()->data.data.ppid=(payload[12]<<24)+(payload[13]<<16)+(payload[14]<<8)+payload[15]; +genlog("Data segment ppid %Xd",list.current()->data.data.ppid); + list.current()->data.data.begin=(flags&0x02); +genlog("Data segment begin %Xd",list.current()->data.data.begin); + list.current()->data.data.end=(flags&0x01); +genlog("Data segment end %Xd",list.current()->data.data.end); + list.current()->data.data.length=chunk_length-16; +genlog("Data segment length %d",list.current()->data.data.length); + list.current()->data.data.data=payload+16; + idx++; + break; + case 3: // SACK + list.append(new SCTP_chunk); + list.current()->type=type; + list.current()->flags=flags; + list.current()->length=chunk_length; + list.current()->data.ack_tsn=(payload[4]<<24)+(payload[5]<<16)+(payload[6]<<8)+payload[7]; + idx++; + break; + default: + break; + } + chunk_length=((chunk_length+3)/4)*4; + length-=chunk_length; + payload+=chunk_length; + } +} + +SCTP_Stream_list::SCTP_Stream_list(){ +acked_tsn=0; +} + +SCTP_Stream_list::~SCTP_Stream_list(){ + streams.destruct(); +} + +void SCTP_Stream_list::add_segment_to_stream(TCPSegment* segment){ +genlog("SCTP_Stream_list::add_segment_to_stream"); + SCTP_chunk_list chunk_list; + decode_sctp(segment, chunk_list); + for(int i=0;i<chunk_list.length();i++){ + if(chunk_list[i].type==0){ // SCTP data chunk +genlog("SCTP_Stream_list::add_segment_to_stream SCTP data chunk"); + if(acked_tsn<chunk_list[i].data.data.tsn){ + if(!add_to_stream(chunk_list[i].data.data, segment->timestamp)){ + add_stream(chunk_list[i].data.data,segment->timestamp); + } + } + } + } + chunk_list.destruct(); +genlog("SCTP_Stream_list::add_segment_to_stream end"); +} + +void SCTP_Stream_list::ack(TCPSegment* segment){ +genlog("SCTP_Stream_list::ack"); + SCTP_chunk_list chunk_list; + decode_sctp(segment, chunk_list); + for(int i=0;i<chunk_list.length();i++){ + if(chunk_list[i].type==3){ + if(acked_tsn<chunk_list[i].data.ack_tsn){ + acked_tsn=chunk_list[i].data.ack_tsn; + for(int k=0;k<streams.length();k++){ + streams[k].ack_message(chunk_list[i].data.ack_tsn); + } + } + } + } + chunk_list.destruct(); +genlog("SCTP_Stream_list::ack end"); +} + + +bool SCTP_Stream_list::add_to_stream(SCTP_data_chunk &data, double timestamp){ +genlog("SCTP_Stream_list::add_to_stream"); + for(int i=0;i<streams.length();i++){ + if(streams[i].stream_id==data.sid){ + streams[i].add_segment(data,timestamp); +genlog("SCTP_Stream_list::add_to_stream true"); + return true; + } + } +genlog("SCTP_Stream_list::add_to_stream false"); + return false; +} + +bool SCTP_Stream_list::has_message(){ +genlog("SCTP_Stream_list::has_message"); + for(int i=0;i<streams.length();i++){ + if(streams[i].has_message()){ +genlog("SCTP_Stream_list::has_message true"); + return true; + } + } +genlog("SCTP_Stream_list::has_message false"); + return false; +} + +unsigned char* SCTP_Stream_list::get_first_sctp_data(){ + int idx=0; + double timestamp=-1.0; + for(int i=0;i<streams.length();i++){ + if(streams[i].has_message() && (timestamp==-1.0 || timestamp>streams[i].get_first_ts())){ + timestamp=streams[i].get_first_ts(); + idx=i; + } + } + return streams[idx].get_first_message_data(); +} + +size_t SCTP_Stream_list::get_first_sctp_data_len(){ + int idx=0; + double timestamp=-1.0; + for(int i=0;i<streams.length();i++){ + if(streams[i].has_message() && (timestamp==-1.0 || timestamp>streams[i].get_first_ts())){ + timestamp=streams[i].get_first_ts(); + idx=i; + } + } + return streams[idx].get_first_message_data_len(); +} + + +double SCTP_Stream_list::get_first_sctp_timestamp(){ +// int idx=0; + double timestamp=-1.0; + for(int i=0;i<streams.length();i++){ + if(streams[i].has_message() && (timestamp==-1.0 || timestamp>streams[i].get_first_ts())){ + timestamp=streams[i].get_first_ts(); +// idx=i; + } + } + return timestamp; +} + +void SCTP_Stream_list::delete_first_sctp_message(){ + int idx=0; + double timestamp=-1.0; + for(int i=0;i<streams.length();i++){ + if(streams[i].has_message() && (timestamp==-1.0 || timestamp>streams[i].get_first_ts())){ + timestamp=streams[i].get_first_ts(); + idx=i; + } + } + streams[idx].delete_first_message(); +} + +void SCTP_Stream_list::add_stream(SCTP_data_chunk &data, double timestamp){ + SCTP_stream *new_stream= new SCTP_stream; + streams.append(new_stream); + new_stream->add_segment(data,timestamp); +} + + +void SCTP_Stream_list::log_stat(){ +} + +void SCTP_Stream_list::dump(){ +} + +SCTP_stream::SCTP_stream(){ + stream_id=0; + + +} + +SCTP_stream::~SCTP_stream(){ + message_list.destruct(); +} + +void SCTP_stream::add_segment(SCTP_data_chunk &data, double timestamp){ +genlog("SCTP_stream::add_segment"); + int data_idx=get_idx(data.ssn); + SCTP_message *message; + if(data_idx==-1){ + message=new SCTP_message; + message_list.addElement(message); + } else { + message=message_list.elementAt(data_idx); + } + stream_id=data.sid; + message->add_segment(data,timestamp); +genlog("SCTP_stream::add_segment end"); +} + +bool SCTP_stream::has_message(){ + int data_idx=find_first_message(); + if(data_idx!=-1) return message_list[data_idx].complete; + return false; +} + +double SCTP_stream::get_first_ts(){ + int data_idx=find_first_message(); + if(data_idx!=-1) return message_list[data_idx].timestamp; + return 0.0; +} + +unsigned char* SCTP_stream::get_first_message_data(){ + int data_idx=find_first_message(); + if(data_idx!=-1) return message_list[data_idx].data; + return NULL; +} + +size_t SCTP_stream::get_first_message_data_len(){ + int data_idx=find_first_message(); + if(data_idx!=-1) return message_list[data_idx].length; + return 0; +} + +int SCTP_stream::find_first_message(){ + unsigned int ssn=0; + int idx=-1; + for(int i=0;i<message_list.length();i++){ + if(idx==-1 || ssn>message_list[i].ssn){ + ssn=message_list[i].ssn; + idx=i; + } + } + return idx; +} + +void SCTP_stream::delete_first_message(){ + int data_idx=find_first_message(); + if(data_idx!=-1) { + delete message_list.elementAt(data_idx); + message_list.remove(data_idx); + } +} + +void SCTP_stream::ack_message(unsigned int ack_tsn){ + SCTP_message *message=message_list.first(); + while(message){ + if(!message->complete && (message->last_cons_tsn<ack_tsn)){ + delete message; + message_list.removeCurrent(); + } + message=message_list.next(); + } +} + +int SCTP_stream::get_idx(unsigned int ssn){ +genlog("SCTP_stream::get_idx"); + + for(int i=0;i<message_list.length();i++){ + if(ssn==message_list[i].ssn){ +genlog("SCTP_stream::get_idx ret: %d",i); + return i; + } + } +genlog("SCTP_stream::get_idx ret -1"); + return -1; +} + +SCTP_message::SCTP_message(){ + complete=false; + first_rcvd=false; + last_rcvd=false; + ssn=0; + + last_cons_tsn=0; + ppid=0; + fragments=NULL; + length=0; + data=NULL; + timestamp=0.0; +} + +SCTP_message::~SCTP_message(){ + free_segments(); + Free(data); +} + +void SCTP_message::add_segment(SCTP_data_chunk &chunk, double time_stamp){ + if(complete) return; + if(fragments==NULL){ // first received segment + if(chunk.begin && chunk.end){ // complete + complete=true; + first_rcvd=true; + last_rcvd=true; + ssn=chunk.ssn; + ppid=chunk.ppid; + first_tsn=chunk.tsn; + last_tsn=chunk.tsn; + last_cons_tsn=chunk.tsn; + length=chunk.length; + timestamp=time_stamp; + data= (unsigned char*)Malloc(length*sizeof(unsigned char)); + memcpy(data,chunk.data,length); + return; + } else { + ssn=chunk.ssn; + ppid=chunk.ppid; + fragments=new SCTP_data_fragment_list; + } + } + if(chunk.begin){ + first_rcvd=true; + first_tsn=chunk.tsn; + } + if(chunk.end){ + last_rcvd=true; + last_tsn=chunk.tsn; + } + if(get_idx(chunk.tsn)==-1){ + SCTP_data_fragment *fragment=new SCTP_data_fragment(chunk); + fragments->addElement(fragment); + } + if(first_rcvd && last_cons_tsn<chunk.tsn){ + if(last_cons_tsn==0) last_cons_tsn=first_tsn; + while(get_idx(last_cons_tsn+1)!=-1){ + last_cons_tsn++; + } + } + if(first_rcvd && last_rcvd && last_cons_tsn==last_tsn){ + size_t curr_length=0; + for(unsigned int curr_tsn=first_tsn;curr_tsn<=last_tsn;curr_tsn++){ + int idx=get_idx(curr_tsn); + data= (unsigned char*)Realloc(data,(curr_length+fragments->elementAt(idx)->length)*sizeof(unsigned char)); + memcpy(data+curr_length,fragments->elementAt(idx)->data,fragments->elementAt(idx)->length); + curr_length+=fragments->elementAt(idx)->length; + } + length=curr_length; + complete=true; + timestamp=time_stamp; + free_segments(); + } +} + +void SCTP_message::free_segments(){ + if(fragments){ + fragments->destruct(); + delete fragments; + fragments=NULL; + } +} + +int SCTP_message::get_idx(unsigned int tsn){ + if(fragments){ + for(int i=0;i<fragments->length();i++){ + if(fragments->elementAt(i)->tsn==tsn) return i; + } + } + return -1; +} + +SCTP_data_fragment::SCTP_data_fragment(SCTP_data_chunk &chunk){ + begin=chunk.begin; + end=chunk.end; + tsn=chunk.tsn; + length=chunk.length; + data= (unsigned char*)Malloc(length*sizeof(unsigned char)); + memcpy(data,chunk.data,length); +} + +SCTP_data_fragment::~SCTP_data_fragment(){ +Free(data); +} + +Protocol_data::Protocol_data(){ +} + +Protocol_data::~Protocol_data(){ + data_list.destruct(); +} + +void Protocol_data::add_protocol(const int id, const tf__getMsgLen& f_getMsgLen, const tf__getMsgStartPos& f_getMsgStartPos){ + int idx=get_idx(id); + protocol_def *def; + if(idx==-1){ + def=new protocol_def; + data_list.append(def); + } else { + def=data_list.elementAt(idx); + } + def->id=id; + def->f_getMsgLen=f_getMsgLen; + def->f_getMsgStartPos=f_getMsgStartPos; +} + +int Protocol_data::get_idx(int id){ + protocol_def *def; + for(int i=0; i<data_list.length();i++){ + def=data_list.elementAt(i); + if(def->id==id) return i; + } + + return -1; +} + +const tf__getMsgLen& Protocol_data::get_f_getMsgLen(int id){ + int idx=get_idx(id); + if(idx==-1) return def_getMsgLen_ref; + protocol_def *def=data_list.elementAt(idx); + return def->f_getMsgLen; +} + +const tf__getMsgStartPos& Protocol_data::get_f_getMsgStartPos(int id){ + int idx=get_idx(id); + if(idx==-1) return def_getMsgStartPos_ref; + protocol_def *def=data_list.elementAt(idx); + return def->f_getMsgStartPos; +} + +}// namespace diff --git a/src/PCAPasp_PT.hh b/src/PCAPasp_PT.hh new file mode 100644 index 0000000..c407b83 --- /dev/null +++ b/src/PCAPasp_PT.hh @@ -0,0 +1,691 @@ +/****************************************************************************** +* Copyright (c) 2005, 2014 Ericsson AB +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Antal Wuh.Hen.Chang - initial implementation and initial documentation +* Adam Delic +* Andrea Darabos +* Endre Kulcsar +* Gabor Szalai +* Tibor Szabo +******************************************************************************/ +// +// File: PCAPasp_PT.hh +// Description: PCAP port header +// Rev: R7A +// Prodnr: CNL 113 443 + +#ifndef PCAPasp_PT_HH +#define PCAPasp_PT_HH + +#include "PCAPasp_PortType.hh" + +#include <pcap.h> +#include <netinet/in.h> +#include <list> +#include <string> +#include <map> + +#ifndef IPPROTO_SCTP +#define IPPROTO_SCTP 132 +#endif + + + +//////////////////////////////////////////////////////////// +// TCPSegment class +//////////////////////////////////////////////////////////// + +#define TCP_SEG 0 +#define UDP_SEG 1 +#define SCTP_SEG 2 + +#define NO_PROTOCOL 0 +#define DIAMETER_PROTOCOL 1 +#define LDAP_PROTOCOL 2 +#define RADIUS_PROTOCOL 3 +#define SIP_PROTOCOL 4 + +#define PCAP_ETHERTYPE_VLAN8021Q 0x8100 +struct ether_header; +class SCTP_Stream_list; + +namespace PCAPasp__PortType{ +class TCPSegment { + + public: + //Stream identifiers: + unsigned int port_src; + unsigned int port_dst; + struct in_addr ip_src; + struct in_addr ip_dst; + + int seg_type; // Can be TCP_SEG or UDP_SEG or SCTP_SEG + bool syn; // True if it's a SYN TCP segment + bool fin; // True it it's a FIN TCP segment + unsigned long int seq_num; // Sequence number for TCP_SEG + unsigned long int ack_num; // Acknowledgement number for TCP_SEG + + unsigned char* payload; // Payload buffer for TCP and UDP + size_t length; // Length of the payload buffer + + double timestamp; + int protocol_type; // DIA,LDAP,RADIUS, NONE + + public: + TCPSegment(); + ~TCPSegment(); + void put(char* buf, size_t size); + void log(const char *fmt, ...); + void log(); +}; + +class ESP_obj{ + public: + ESP_obj(PCAPasp__Types::ASP__PCAP__ESP__Setup); + ~ESP_obj(); + unsigned int spi; + int mode; // 1-transport, 2-tunnel + std::string ip_port; // concatenation of src_ip, dst_ip, src_port,dst_port + // converted to octet strem in netwotk byte order + // If any of them are not specified, represented as 0 + PCAPasp__Types::tf__ICV__check icv_fv; + OCTETSTRING icv_data; + PCAPasp__Types::tf__ESP__decrypt decrypt_fv; + OCTETSTRING decrypt_data; +}; + +class ESP_handler{ + public: + ESP_handler(); + ~ ESP_handler(); + + std::list<ESP_obj*> ESP_OBJ_list; + std::map<unsigned int, ESP_obj*> spi_ESP_obj_map; + std::map<std::string, ESP_obj*> address_ESP_obj_map; + + bool setup_esp(PCAPasp__Types::ASP__PCAP__ESP__Setup); // add or delete ESP + void clean_up(); + // Finds the ESP object for the spi + bool find_esp(unsigned int spi, ESP_obj *& esp); + + + // Retrun true if there is a registered SPI for the addresses + bool esp_exists(struct in_addr *ip_src, unsigned int port_src,struct in_addr *ip_dst,unsigned int port_dst,unsigned int proto); + + bool match_esp(struct in_addr *ip_src, unsigned int port_src,struct in_addr *ip_dst,unsigned int port_dst,const ESP_obj *esp); +}; + +//////////////////////////////////////////////////////////// +// DumpReader class +//////////////////////////////////////////////////////////// + +#ifndef ETHER_ADDR_LEN +#define ETHER_ADDR_LEN 6 +#endif + +#ifndef u_int8_t +#define u_int8_t uint8_t +#endif +#ifndef u_int16_t +#define u_int16_t uint16_t +#endif +#ifndef u_int32_t +#define u_int32_t uint32_t +#endif + +struct vlan_header { + u_int16_t vlan_head; + u_int16_t vlan_type; +}; + +struct ip_header { + u_int8_t ip_vhl; /* header length, version */ +#define IP_V(ip) (((ip)->ip_vhl & 0xf0) >> 4) +#define IP_HL(ip) ((ip)->ip_vhl & 0x0f) + u_int8_t ip_tos; /* type of service */ + u_int16_t ip_len; /* total length */ + u_int16_t ip_id; /* identification */ + u_int16_t ip_off; /* fragment offset field */ +#define IP_DF 0x4000 /* dont fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + u_int8_t ip_ttl; /* time to live */ + u_int8_t ip_p; /* protocol */ + u_int16_t ip_sum; /* checksum */ + struct in_addr ip_src; + struct in_addr ip_dst; /* source and dest address */ +}; + +/* + * TCP header. + * Per RFC 793, September, 1981. + */ +struct tcphdr { + u_int16_t th_sport; /* source port */ + u_int16_t th_dport; /* destination port */ + u_int32_t th_seq; /* sequence number */ + u_int32_t th_ack; /* acknowledgement number */ + u_int8_t th_off; /* data offset + reserved */ + #define TCP_OFF(tcp) (((tcp)->th_off & 0xf0) >> 4) + + u_char th_flags; + #define TH_FIN 0x01 + #define TH_SYN 0x02 + #define TH_RST 0x04 + #define TH_PUSH 0x08 + #define TH_ACK 0x10 + #define TH_URG 0x20 + #define TH_ECE 0x40 + #define TH_CWR 0x80 + #define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR) + + u_int16_t th_win; /* window */ + u_int16_t th_sum; /* checksum */ + u_int16_t th_urp; /* urgent pointer */ +}; + +struct udphdr { + u_short uh_sport; /* source port */ + u_short uh_dport; /* destination port */ + u_short uh_ulen; /* udp length */ + u_short uh_sum; /* udp checksum */ +}; + +struct sctphdr { + u_short sh_sport; /* source port */ + u_short sh_dport; /* destination port */ + u_int32_t sh_vtag; /* verification tag */ + u_int32_t sh_sum; /* checksum */ +}; +//////////////////////////////////////////////////////////// +// Vector template +//////////////////////////////////////////////////////////// + +template < class Type > class Vector +{ + +public: + int size; + int actual; + Type **ar; + +public: + Vector (); + Vector (Vector & a); + ~Vector (); + Vector & operator = (Vector & a); + Type & operator[](int idx); + Type *elementAt (int idx); + bool removeElementAt (int idx); + int length (); + void append (Type * ptr); + void addElement (Type * ptr); + bool remove (Type * ptr); + bool removeCurrent(); + bool remove (int idx); + bool removeElement (Type * ptr); + bool removeRef (Type * ptr); + int find (Type * ptr); + Type *first (); + Type *last (); + Type *next (); + Type *current (); + Type *prev (); + bool isEmpty (); + void destruct (); +}; + +class protocol_def{ +public: + int id; + PCAPasp__Types::tf__getMsgLen f_getMsgLen; + PCAPasp__Types::tf__getMsgStartPos f_getMsgStartPos; + protocol_def(){}; + ~protocol_def(){}; +}; + +typedef Vector <protocol_def> protocol_def_list; + +class Protocol_data { + public: + protocol_def_list data_list; + Protocol_data(); + ~Protocol_data(); + + void add_protocol(const int id, const PCAPasp__Types::tf__getMsgLen& f_getMsgLen, const PCAPasp__Types::tf__getMsgStartPos& f_getMsgStartPos); + int get_idx(int id); + const PCAPasp__Types::tf__getMsgLen& get_f_getMsgLen(int id); + const PCAPasp__Types::tf__getMsgStartPos& get_f_getMsgStartPos(int id); +}; + +class Hole { +public: + u_int16_t first; + u_int16_t last; + + inline Hole() {}; + inline Hole(u_int16_t f, u_int16_t l){first=f;last=l;}; + inline ~Hole() {}; +}; + +typedef Vector <Hole> Holes_list; + +class IP_fragment { +public: + + IP_fragment(); + ~IP_fragment(); + void clear(); + bool add_fragment(struct ip_header* IPHeader, + u_char* IPData); + bool get_fragment(struct ip_header** IPHeader, + u_char** IPData); + u_int16_t id; + +private: + u_char* buffer; + ip_header* header; + u_int16_t buffer_len; + u_int16_t data_len; + Holes_list holes; +}; + +typedef Vector <IP_fragment> IP_fragment_list; + +class IP_fragments { +public: + IP_fragments(); + ~IP_fragments(); + + void clear(); // clear the IP fragments + bool check(); // check wheter IP fragments exist + bool add_ip_fragment(struct ip_header** IPHeader, + u_char** IPData); + // put the IP fragment in the buffer + // if an IP packet completly reassembled with + // the fragment returns true and set the + // pointers. They must be freed later!! +private: + IP_fragment_list packet_list; +}; + + +class DumpReader { + +public: + DumpReader(); + ~DumpReader(); + + bool open(char* fname); // Tries to open a capture file + bool setFilter(char* filter_script, bpf_u_int32 netmask = 0xffffff); + TCPSegment* getNextSegment(); + +private: + char errbuf[PCAP_ERRBUF_SIZE]; // Buffer for PCAP error messages + pcap_t *fp; // Descriptor of an open capture instance. + char* captureFilter; // filter script + struct bpf_program BPFcode; // compiled filter + + // pointers to the read packets + struct pcap_pkthdr* actHeader; + const u_char* actData; + struct ether_header* actEthernetHeader; + u_char* actEthernetData; + struct ip_header* actIPHeader; + u_char* actIPData; + struct tcphdr* actTCPHeader; + u_char* actTCPData; + struct udphdr* actUDPHeader; + u_char* actUDPData; + + struct sctphdr* actSCTPHeader; + u_char* actSCTPData; + + bool free_ptr; + IP_fragments fragment_buffer; + + int frameCounter; + + bool getNext(); // Next PCAP packet from + bool getNextEthernet(); // Next ethernet packet containing IP + bool getNextIP(); // Next non fragmented IP datagram + +}; + +//////////////////////////////////////////////////////////// +// TCPBuffer class +//////////////////////////////////////////////////////////// + +class TCPBuffer { + + public: + + unsigned long int seq_num; + size_t length; + unsigned char* buffer; + unsigned char* read_poi; + double timestamp; + unsigned long int total_length; + unsigned long int lost_length; + bool closed; + bool close_sent; + + + public: + TCPBuffer(); + ~TCPBuffer(); + void clear(); + bool put(TCPSegment* segment); + bool ack(TCPSegment* segment); + + void rewind(); + size_t get_pos(); + void set_pos(size_t pos); + size_t get_len(); + unsigned char* get_data(); + size_t get_read_len(); + unsigned char* get_read_data(); + void cut(); + void cut(size_t cut_bytes); + void log(const char *fmt, ...); + void dump(); + void log_stat(); + +}; + + +struct SCTP_data_chunk{ + unsigned int tsn; + unsigned int sid; + unsigned int ssn; + unsigned int ppid; + bool begin; + bool end; + unsigned int length; + unsigned char* data; +}; + + +struct SCTP_chunk{ + unsigned int type; + unsigned int flags; + unsigned int length; + union{ + SCTP_data_chunk data; + unsigned int ack_tsn; + } data; +}; + +typedef Vector <SCTP_chunk> SCTP_chunk_list; + +class SCTP_data_fragment{ +public: + bool begin; + bool end; + unsigned int tsn; + unsigned int length; + unsigned char* data; + + SCTP_data_fragment(SCTP_data_chunk &); + ~SCTP_data_fragment(); +}; + +typedef Vector <SCTP_data_fragment> SCTP_data_fragment_list; + +class SCTP_message{ +public: + SCTP_message(); + ~SCTP_message(); + + bool complete; + bool first_rcvd; + bool last_rcvd; + unsigned int ssn; + unsigned int first_tsn; + unsigned int last_tsn; + unsigned int last_cons_tsn; + unsigned int ppid; + SCTP_data_fragment_list *fragments; + size_t length; + unsigned char* data; + double timestamp; + + void add_segment(SCTP_data_chunk &chunk, double time_stamp); + void free_segments(); + int get_idx(unsigned int tsn); +}; + +typedef Vector <SCTP_message> SCTP_message_list; + +class SCTP_stream{ +public: + SCTP_stream(); + ~SCTP_stream(); + + unsigned int stream_id; +// unsigned int low_ssn; +// unsigned int high_ssn; + SCTP_message_list message_list; + + void add_segment(SCTP_data_chunk &data, double timestamp); + bool has_message(); + double get_first_ts(); + unsigned char* get_first_message_data(); + size_t get_first_message_data_len(); + void delete_first_message(); + void ack_message(unsigned int ack_tsn); + + int get_idx(unsigned int ssn); + int find_first_message(); +}; + +typedef Vector <SCTP_stream> list_of_SCTP_stream; + +class SCTP_Stream_list{ +public: + + list_of_SCTP_stream streams; + unsigned int acked_tsn; + + SCTP_Stream_list(); + ~SCTP_Stream_list(); + + + bool has_message(); + unsigned char* get_first_sctp_data(); + size_t get_first_sctp_data_len(); + double get_first_sctp_timestamp(); + void delete_first_sctp_message(); + bool add_to_stream(SCTP_data_chunk &data, double timestamp); + void add_stream(SCTP_data_chunk &data, double timestamp); + void add_segment_to_stream(TCPSegment* segment); + void ack(TCPSegment* segment); + void log_stat(); + void dump(); +}; + + +//////////////////////////////////////////////////////////// +// Peer class +//////////////////////////////////////////////////////////// + +typedef Vector <TCPSegment> SegmentList; + +class Peer { + + public: + //Data that idientify the stream: + u_short port_src; + u_short port_dst; + struct in_addr ip_src; + struct in_addr ip_dst; + + TCPBuffer tcp_buf; //Buffer for the processed bytes + SegmentList seg_list; //List for the icoming segments that haven't been processed yet. + int protocol_type; //Protocol type of the peer. Type are defined in TCPSegment.hh + bool out_of_sync; + int transport_type; + + // SCTP stuff + bool is_sctp; + SCTP_Stream_list stream_list; + + public: + Peer(); + Peer(TCPSegment* segment); + ~Peer(); + void reset(); + void init(TCPSegment* segment); + bool compare(TCPSegment* segment); + bool sentBy(TCPSegment* segment); //Returns true if the segment was sent by this Peer + void put(TCPSegment* segment); + bool ack(TCPSegment* segment); + + + int get_msg_len(); + bool tryToResync(); + + // SCTP stuff + bool has_message(); + unsigned char* get_first_sctp_data(); + size_t get_first_sctp_data_len(); + double get_first_sctp_timestamp(); + void delete_first_sctp_message(); + + + void log(const char *fmt, ...); + void dump(); + void log_stat(); +}; + +//////////////////////////////////////////////////////////// +// PeerList class +//////////////////////////////////////////////////////////// + +typedef Vector <Peer> Peers; + +class PeerList { + + public: + Peers peer_list; + bool tcp; + + public: + PeerList(); + ~PeerList(); + void addPeer(TCPSegment* segment); + Peer* getPeer(TCPSegment* segment); + Peer* getOtherPeer(TCPSegment* segment); + bool sendSegmentToPeer(TCPSegment* segment); //Returns false if lost segment is detected + void setType(bool t); + Peer* elementAt(int i); + int length(); + void log(const char *fmt, ...); + void dump(); + void log_stat(); +}; + +//////////////////////////////////////////////////////////// +// FilterEntry class +//////////////////////////////////////////////////////////// + +class FilterEntry { + + public: + int protocol_type; + struct in_addr ip_src; + struct in_addr ip_dst; + unsigned int port_dst; + bool ip_src_all; + + public: + FilterEntry(int protocol, struct in_addr sip, bool sip_all, struct in_addr dip, unsigned int rport); + ~FilterEntry(); + bool compare(TCPSegment* seg); + void log(const char *fmt, ...); +}; + +//////////////////////////////////////////////////////////// +// FilterTable class +//////////////////////////////////////////////////////////// + +typedef Vector <FilterEntry> FilterEntries; + +class FilterTable { + + public: + FilterEntries entry_list; + + public: + FilterTable(); + ~FilterTable(); + void addEntry(int protocol, struct in_addr sip, bool sip_all, struct in_addr dip, unsigned int rport); + int filter(TCPSegment* segment); + void log(const char *fmt, ...); +}; + + + + +//////////////////////////////////////////////////////////// +// PCAPasp_PT class +//////////////////////////////////////////////////////////// +class PCAPasp__PT : public PCAPasp__PT_BASE { + +public: + PCAPasp__PT(const char *par_port_name = NULL); + ~PCAPasp__PT(); + + void set_parameter(const char *parameter_name, + const char *parameter_value); + + void Event_Handler(const fd_set *read_fds, + const fd_set *write_fds, const fd_set *error_fds, + double time_since_last_call); + ESP_handler esp; + void inc_msg(const PCAPasp__Types::ASP__PCAP__ESP__Report& data) {incoming_message(data);}; + +protected: + void user_map(const char *system_port); + void user_unmap(const char *system_port); + + void user_start(); + void user_stop(); + + void outgoing_send(const PCAPasp__Types::ASP__PCAP__Capture& send_par); + void outgoing_send(const PCAPasp__Types::ASP__PCAP__ConfigReq& send_par); + void outgoing_send(const PCAPasp__Types::ASP__PCAP__MessageReq& send_par); + void outgoing_send(const PCAPasp__Types::ASP__PCAP__DumpReaderFilter& send_par); + void outgoing_send(const PCAPasp__Types::ASP__PACP__SetupProtocol& send_par); + void outgoing_send(const PCAPasp__Types::ASP__PCAP__ESP__Setup& send_par); + + TCPSegment* getNextFilteredSegment(); + void log(const char *fmt, ...); + + // Variables for capturing mode + int capture; + int settings; + pcap_t *handle; + struct pcap_pkthdr header; + pcap_dumper_t *dumpfile; + // For test port parameters + char* capture_file; + char* packet_filter; + + + // Inner components + DumpReader dump_reader; // I/f for PCAP dump files + PeerList peer_list_tcp; // Peer buffer list for TCP streams + PeerList peer_list_udp; // Peer buffer list for UDP streams + PeerList peer_list_sctp; // Peer buffer list for SCTP streams + FilterTable filter_table; // Stream filter table + +}; + +} +#endif diff --git a/src/PCAPasp_PortType.ttcn b/src/PCAPasp_PortType.ttcn new file mode 100644 index 0000000..a53e5a8 --- /dev/null +++ b/src/PCAPasp_PortType.ttcn @@ -0,0 +1,44 @@ +/****************************************************************************** +* Copyright (c) 2005, 2014 Ericsson AB +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Antal Wuh.Hen.Chang - initial implementation and initial documentation +* Adam Delic +* Andrea Darabos +* Endre Kulcsar +* Gabor Szalai +* Tibor Szabo +******************************************************************************/ +// +// File: PCAPasp_PortType.ttcn +// Description: PCAP testport port definition file +// Rev: R7A +// Prodnr: CNL 113 443 +// + +module PCAPasp_PortType +{ + import from PCAPasp_Types all; +// import from RADIUSmsg_Types all; +// import from SIPmsg_Types all; + + type port PCAPasp_PT message { + out ASP_PCAP_Capture; + out ASP_PCAP_ConfigReq; + in ASP_PCAP_ConfigResp; + out ASP_PCAP_MessageReq; + in ASP_PCAP_MessageResp; + out ASP_PCAP_DumpReaderFilter; + in ASP_PCAP_DumpFilterResp; + out ASP_PACP_SetupProtocol; + in ASP_PCAP_ConnectionClosed; + in ASP_PCAP_Error; + out ASP_PCAP_ESP_Setup; + in ASP_PCAP_ESP_Setup_Resp; + in ASP_PCAP_ESP_Report; + } +} diff --git a/src/PCAPasp_Types.ttcn b/src/PCAPasp_Types.ttcn new file mode 100644 index 0000000..cf01958 --- /dev/null +++ b/src/PCAPasp_Types.ttcn @@ -0,0 +1,174 @@ +/****************************************************************************** +* Copyright (c) 2005, 2014 Ericsson AB +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Antal Wuh.Hen.Chang - initial implementation and initial documentation +* Adam Delic +* Andrea Darabos +* Endre Kulcsar +* Gabor Szalai +* Tibor Szabo +******************************************************************************/ +// +// File: PCAPasp_Types.ttcn +// Description: PCAP testport type definition file +// Rev: R7A +// Prodnr: CNL 113 443 +// + +module PCAPasp_Types +{ + + + //******************************* for Capture Mode ********************************* + + type enumerated CaptureControl { START, STOP }; + + type record ASP_PCAP_Capture { + + CaptureControl command + } + + type record ASP_PCAP_ConfigReq { + + charstring interface optional, + integer mask optional, + charstring filter optional, + charstring filename + } + + type enumerated CommandId { FILTERCMD, STARTCMD, STOPCMD }; + type enumerated CommandStatus { VALID, INVALID }; + type enumerated CommandError { CAPTURING_HAS_ALREADY_STARTED, THERE_IS_NO_FILTER_SET, CAPTURING_HAS_NOT_STARTED, PORT_IS_ALREADY_CAPTURING, ERROR_LOOKING_NET_UP, ERROR_LIVE_OPENING, ERROR_COMPILING_FILTER, ERROR_SETTING_FILTER, ERROR_SETTING_NONBLOCK_MODE, ERROR_OPENING_OUTPUT_FILE }; + + type record ASP_PCAP_ConfigResp { + + CommandId command, + CommandStatus status, + CommandError errorMessage optional + } + + //******************************* for DumpReader Mode ********************************* + + type integer RequestedProtocol; // -1 Any protocol or greater than 0 + + type record ASP_PCAP_MessageReq { + RequestedProtocol nextMessage + } + + + type enumerated Status { VALID_MESSAGE, NO_MESSAGE }; + type record ASP_PCAP_MessageResp { + Status status, + float timeStamp optional, + integer contentLength optional, + charstring destinationIP optional, + integer destinationPort optional, + charstring sourceIP optional, + integer sourcePort optional, + ProtocolMsgType msgtype optional, + octetstring nextMessage optional + } + + type integer ProtocolMsgType; // greater than 0 + type record of integer IntegerArrayType; + type record ASP_PCAP_DumpReaderFilter { + + ProtocolMsgType messageType, + charstring remoteIp, + charstring localIp, + IntegerArrayType remotePorts + } + + type enumerated DumpFilterError { WRONG_SOURCE_IP, WRONG_DESTINATION_IP }; + type record ASP_PCAP_DumpFilterResp { + + CommandStatus status, + DumpFilterError errorMessage optional + } + + type record ASP_PCAP_ConnectionClosed { + + RequestedProtocol protocol, + charstring destinationIP, + integer destinationPort, + charstring sourceIP, + integer sourcePort + } + + type enumerated PCAPError { LOST_SEGMENT } + type record ASP_PCAP_Error { + PCAPError errorType, + charstring destinationIP, + integer destinationPort, + charstring sourceIP, + integer sourcePort + } + + type integer Transport // TCP_Transport(0), UDP_Transport(1), SCTP_Transport(2) + + type function tf_getMsgLen(in octetstring stream, in boolean conn_closed, + in Transport stream_transport) return integer; + type function tf_getMsgStartPos(in octetstring stream, boolean conn_closed, + in Transport stream_transport) return integer; + + type record ASP_PACP_SetupProtocol { + ProtocolMsgType protocol_id, + tf_getMsgLen getMsgLen_function optional, + tf_getMsgStartPos getMsgStartPos_function optional + } + + // ESP related stuff + + // function intended to check the Integrity Check Value. + // Return value: + // ICV length in octets + // -1 if the check failed + type function tf_ICV_check(in octetstring ipheader, in octetstring ipdata, inout octetstring user_data) return integer; + + // Decryption function + type function tf_ESP_decrypt(in octetstring ipheader, in octetstring ipdata, + out octetstring decrypted_data, inout octetstring user_data) return boolean; + + + type enumerated ESP_mode { ESP_DELETE (0), // Remove the associated ESP mode + ESP_TRANSPORT_MODE (1), + ESP_ENCAPSULATION (2) } // The encapsulation node is not implemented yet. + + + // The SA lookup will be done based on SPI value + // The IP and port filed used only for checking. + type record ASP_PCAP_ESP_Setup { + integer spi, + ESP_mode mode, + charstring destinationIP optional, + integer destinationPort optional, + charstring sourceIP optional, + integer sourcePort optional, + tf_ICV_check icv_function optional, // if omit, no ICV present + octetstring icv_function_user_data optional, + tf_ESP_decrypt esp_decrypt_function optional, // if omit, NULL encription + octetstring esp_decrypt_function_user_data optional + } + + type record ASP_PCAP_ESP_Setup_Resp{ + ESP_Status status_code, + charstring status_message optional + } + + type enumerated ESP_Status { ESP_OK, ESP_NOT_DEFINED, NOT_ESP_PACKET, ESP_WRONG_SPI, ESP_ICV_ERROR, ESP_DECRYPT_ERROR, ESP_SETUP_ERROR} + + type record ASP_PCAP_ESP_Report{ + ESP_Status status_code, + integer spi, + charstring destinationIP, + integer destinationPort optional, // The port number can be encrypted + charstring sourceIP, + integer sourcePort optional, + Transport payload_transport + } +} -- GitLab