1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624
|
/* packet-tcp.c
* Routines for TCP packet disassembly
*
* $Id: packet-tcp.c 43840 2012-07-20 00:36:09Z gerald $
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* 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.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include <string.h>
#include <glib.h>
#include <epan/in_cksum.h>
#include <epan/packet.h>
#include <epan/addr_resolv.h>
#include <epan/ipproto.h>
#include <epan/ip_opts.h>
#include <epan/follow.h>
#include <epan/prefs.h>
#include <epan/emem.h>
#include "packet-tcp.h"
#include "packet-frame.h"
#include <epan/conversation.h>
#include <epan/reassemble.h>
#include <epan/tap.h>
#include <epan/slab.h>
#include <epan/expert.h>
static int tcp_tap = -1;
/* Place TCP summary in proto tree */
static gboolean tcp_summary_in_tree = TRUE;
/*
* Flag to control whether to check the TCP checksum.
*
* In at least some Solaris network traces, there are packets with bad
* TCP checksums, but the traffic appears to indicate that the packets
* *were* received; the packets were probably sent by the host on which
* the capture was being done, on a network interface to which
* checksumming was offloaded, so that DLPI supplied an un-checksummed
* packet to the capture program but a checksummed packet got put onto
* the wire.
*/
static gboolean tcp_check_checksum = FALSE;
extern FILE* data_out_file;
static int proto_tcp = -1;
static int hf_tcp_srcport = -1;
static int hf_tcp_dstport = -1;
static int hf_tcp_port = -1;
static int hf_tcp_stream = -1;
static int hf_tcp_seq = -1;
static int hf_tcp_nxtseq = -1;
static int hf_tcp_ack = -1;
static int hf_tcp_hdr_len = -1;
static int hf_tcp_flags = -1;
static int hf_tcp_flags_res = -1;
static int hf_tcp_flags_ns = -1;
static int hf_tcp_flags_cwr = -1;
static int hf_tcp_flags_ecn = -1;
static int hf_tcp_flags_urg = -1;
static int hf_tcp_flags_ack = -1;
static int hf_tcp_flags_push = -1;
static int hf_tcp_flags_reset = -1;
static int hf_tcp_flags_syn = -1;
static int hf_tcp_flags_fin = -1;
static int hf_tcp_window_size_value = -1;
static int hf_tcp_window_size = -1;
static int hf_tcp_window_size_scalefactor = -1;
static int hf_tcp_checksum = -1;
static int hf_tcp_checksum_bad = -1;
static int hf_tcp_checksum_good = -1;
static int hf_tcp_len = -1;
static int hf_tcp_urgent_pointer = -1;
static int hf_tcp_analysis = -1;
static int hf_tcp_analysis_flags = -1;
static int hf_tcp_analysis_bytes_in_flight = -1;
static int hf_tcp_analysis_acks_frame = -1;
static int hf_tcp_analysis_ack_rtt = -1;
static int hf_tcp_analysis_rto = -1;
static int hf_tcp_analysis_rto_frame = -1;
static int hf_tcp_analysis_retransmission = -1;
static int hf_tcp_analysis_fast_retransmission = -1;
static int hf_tcp_analysis_out_of_order = -1;
static int hf_tcp_analysis_reused_ports = -1;
static int hf_tcp_analysis_lost_packet = -1;
static int hf_tcp_analysis_ack_lost_packet = -1;
static int hf_tcp_analysis_window_update = -1;
static int hf_tcp_analysis_window_full = -1;
static int hf_tcp_analysis_keep_alive = -1;
static int hf_tcp_analysis_keep_alive_ack = -1;
static int hf_tcp_analysis_duplicate_ack = -1;
static int hf_tcp_analysis_duplicate_ack_num = -1;
static int hf_tcp_analysis_duplicate_ack_frame = -1;
static int hf_tcp_analysis_zero_window = -1;
static int hf_tcp_analysis_zero_window_probe = -1;
static int hf_tcp_analysis_zero_window_probe_ack = -1;
static int hf_tcp_continuation_to = -1;
static int hf_tcp_pdu_time = -1;
static int hf_tcp_pdu_size = -1;
static int hf_tcp_pdu_last_frame = -1;
static int hf_tcp_reassembled_in = -1;
static int hf_tcp_reassembled_length = -1;
static int hf_tcp_segments = -1;
static int hf_tcp_segment = -1;
static int hf_tcp_segment_overlap = -1;
static int hf_tcp_segment_overlap_conflict = -1;
static int hf_tcp_segment_multiple_tails = -1;
static int hf_tcp_segment_too_long_fragment = -1;
static int hf_tcp_segment_error = -1;
static int hf_tcp_segment_count = -1;
static int hf_tcp_options = -1;
static int hf_tcp_option_kind = -1;
static int hf_tcp_option_len = -1;
static int hf_tcp_option_mss = -1;
static int hf_tcp_option_mss_val = -1;
static int hf_tcp_option_wscale_shift = -1;
static int hf_tcp_option_wscale_multiplier = -1;
static int hf_tcp_option_sack_perm = -1;
static int hf_tcp_option_sack = -1;
static int hf_tcp_option_sack_sle = -1;
static int hf_tcp_option_sack_sre = -1;
static int hf_tcp_option_echo = -1;
static int hf_tcp_option_echo_reply = -1;
static int hf_tcp_option_timestamp_tsval = -1;
static int hf_tcp_option_timestamp_tsecr = -1;
static int hf_tcp_option_cc = -1;
static int hf_tcp_option_ccnew = -1;
static int hf_tcp_option_ccecho = -1;
static int hf_tcp_option_md5 = -1;
static int hf_tcp_option_qs = -1;
static int hf_tcp_option_exp = -1;
static int hf_tcp_option_exp_data = -1;
static int hf_tcp_option_rvbd_probe = -1;
static int hf_tcp_option_rvbd_probe_version1 = -1;
static int hf_tcp_option_rvbd_probe_version2 = -1;
static int hf_tcp_option_rvbd_probe_type1 = -1;
static int hf_tcp_option_rvbd_probe_type2 = -1;
static int hf_tcp_option_rvbd_probe_optlen = -1;
static int hf_tcp_option_rvbd_probe_prober = -1;
static int hf_tcp_option_rvbd_probe_proxy = -1;
static int hf_tcp_option_rvbd_probe_client = -1;
static int hf_tcp_option_rvbd_probe_proxy_port = -1;
static int hf_tcp_option_rvbd_probe_appli_ver = -1;
static int hf_tcp_option_rvbd_probe_storeid = -1;
static int hf_tcp_option_rvbd_probe_flags = -1;
static int hf_tcp_option_rvbd_probe_flag_last_notify = -1;
static int hf_tcp_option_rvbd_probe_flag_server_connected = -1;
static int hf_tcp_option_rvbd_probe_flag_not_cfe = -1;
static int hf_tcp_option_rvbd_probe_flag_sslcert = -1;
static int hf_tcp_option_rvbd_probe_flag_probe_cache = -1;
static int hf_tcp_option_rvbd_trpy = -1;
static int hf_tcp_option_rvbd_trpy_flags = -1;
static int hf_tcp_option_rvbd_trpy_flag_mode = -1;
static int hf_tcp_option_rvbd_trpy_flag_oob = -1;
static int hf_tcp_option_rvbd_trpy_flag_chksum = -1;
static int hf_tcp_option_rvbd_trpy_flag_fw_rst = -1;
static int hf_tcp_option_rvbd_trpy_flag_fw_rst_inner = -1;
static int hf_tcp_option_rvbd_trpy_flag_fw_rst_probe = -1;
static int hf_tcp_option_rvbd_trpy_src = -1;
static int hf_tcp_option_rvbd_trpy_dst = -1;
static int hf_tcp_option_rvbd_trpy_src_port = -1;
static int hf_tcp_option_rvbd_trpy_dst_port = -1;
static int hf_tcp_option_rvbd_trpy_client_port = -1;
static int hf_tcp_option_mptcp_flags = -1;
static int hf_tcp_option_mptcp_B_flag = -1;
static int hf_tcp_option_mptcp_C_flag = -1;
static int hf_tcp_option_mptcp_S_flag = -1;
static int hf_tcp_option_mptcp_F_flag = -1;
static int hf_tcp_option_mptcp_m_flag = -1;
static int hf_tcp_option_mptcp_M_flag = -1;
static int hf_tcp_option_mptcp_a_flag = -1;
static int hf_tcp_option_mptcp_A_flag = -1;
static int hf_tcp_option_mptcp_subtype = -1;
static int hf_tcp_option_mptcp_version = -1;
static int hf_tcp_option_mptcp_address_id = -1;
static int hf_tcp_option_mptcp_recv_token = -1;
static int hf_tcp_option_mptcp_sender_key = -1;
static int hf_tcp_option_mptcp_recv_key = -1;
static int hf_tcp_option_mptcp_sender_rand = -1;
static int hf_tcp_option_mptcp_sender_trunc_mac = -1;
static int hf_tcp_option_mptcp_sender_mac = -1;
static int hf_tcp_option_mptcp_data_ack = -1;
static int hf_tcp_option_mptcp_data_seq_no = -1;
static int hf_tcp_option_mptcp_subflow_seq_no = -1;
static int hf_tcp_option_mptcp_data_lvl_len = -1;
static int hf_tcp_option_mptcp_checksum = -1;
static int hf_tcp_option_mptcp_ipver = -1;
static int hf_tcp_option_mptcp_ipv4 = -1;
static int hf_tcp_option_mptcp_ipv6 = -1;
static int hf_tcp_option_mptcp_port = -1;
static int hf_tcp_ts_relative = -1;
static int hf_tcp_ts_delta = -1;
static int hf_tcp_option_scps = -1;
static int hf_tcp_option_scps_vector = -1;
static int hf_tcp_option_scps_binding = -1;
static int hf_tcp_option_scps_binding_len = -1;
static int hf_tcp_scpsoption_flags_bets = -1;
static int hf_tcp_scpsoption_flags_snack1 = -1;
static int hf_tcp_scpsoption_flags_snack2 = -1;
static int hf_tcp_scpsoption_flags_compress = -1;
static int hf_tcp_scpsoption_flags_nlts = -1;
static int hf_tcp_scpsoption_flags_reserved = -1;
static int hf_tcp_scpsoption_connection_id = -1;
static int hf_tcp_option_snack = -1;
static int hf_tcp_option_snack_offset = -1;
static int hf_tcp_option_snack_size = -1;
static int hf_tcp_option_snack_le = -1;
static int hf_tcp_option_snack_re = -1;
static int hf_tcp_option_user_to = -1;
static int hf_tcp_option_user_to_granularity = -1;
static int hf_tcp_option_user_to_val = -1;
static int hf_tcp_proc_src_uid = -1;
static int hf_tcp_proc_src_pid = -1;
static int hf_tcp_proc_src_uname = -1;
static int hf_tcp_proc_src_cmd = -1;
static int hf_tcp_proc_dst_uid = -1;
static int hf_tcp_proc_dst_pid = -1;
static int hf_tcp_proc_dst_uname = -1;
static int hf_tcp_proc_dst_cmd = -1;
static int hf_tcp_data = -1;
static gint ett_tcp = -1;
static gint ett_tcp_flags = -1;
static gint ett_tcp_options = -1;
static gint ett_tcp_option_timestamp = -1;
static gint ett_tcp_option_mss = -1;
static gint ett_tcp_option_wscale = -1;
static gint ett_tcp_option_sack = -1;
static gint ett_tcp_option_scps = -1;
static gint ett_tcp_option_scps_extended = -1;
static gint ett_tcp_option_user_to = -1;
static gint ett_tcp_option_exp = -1;
static gint ett_tcp_option_sack_perm = -1;
static gint ett_tcp_analysis = -1;
static gint ett_tcp_analysis_faults = -1;
static gint ett_tcp_timestamps = -1;
static gint ett_tcp_segments = -1;
static gint ett_tcp_segment = -1;
static gint ett_tcp_checksum = -1;
static gint ett_tcp_process_info = -1;
static gint ett_tcp_option_mptcp = -1;
static gint ett_tcp_opt_rvbd_probe = -1;
static gint ett_tcp_opt_rvbd_probe_flags = -1;
static gint ett_tcp_opt_rvbd_trpy = -1;
static gint ett_tcp_opt_rvbd_trpy_flags = -1;
/* Some protocols such as encrypted DCE/RPCoverHTTP have dependencies
* from one PDU to the next PDU and require that they are called in sequence.
* These protocols would not be able to handle PDUs coming out of order
* or for example when a PDU is seen twice, like for retransmissions.
* This preference can be set for such protocols to make sure that we dont invoke
* the subdissectors for retransmitted or out-of-order segments.
*/
static gboolean tcp_no_subdissector_on_error = FALSE;
/*
* TCP option
*/
#define TCPOPT_NOP 1 /* Padding */
#define TCPOPT_EOL 0 /* End of options */
#define TCPOPT_MSS 2 /* Segment size negotiating */
#define TCPOPT_WINDOW 3 /* Window scaling */
#define TCPOPT_SACK_PERM 4 /* SACK Permitted */
#define TCPOPT_SACK 5 /* SACK Block */
#define TCPOPT_ECHO 6
#define TCPOPT_ECHOREPLY 7
#define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */
#define TCPOPT_CC 11
#define TCPOPT_CCNEW 12
#define TCPOPT_CCECHO 13
#define TCPOPT_MD5 19 /* RFC2385 */
#define TCPOPT_MPTCP 0x1e /* Multipath TCP */
#define TCPOPT_SCPS 20 /* SCPS Capabilities */
#define TCPOPT_SNACK 21 /* SCPS SNACK */
#define TCPOPT_RECBOUND 22 /* SCPS Record Boundary */
#define TCPOPT_CORREXP 23 /* SCPS Corruption Experienced */
#define TCPOPT_QS 27 /* RFC4782 */
#define TCPOPT_USER_TO 28 /* RFC5482 */
#define TCPOPT_EXP_FD 0xfd /* Experimental, reserved */
#define TCPOPT_EXP_FE 0xfe /* Experimental, reserved */
/* Non IANA registered option numbers */
#define TCPOPT_RVBD_PROBE 76 /* Riverbed probe option */
#define TCPOPT_RVBD_TRPY 78 /* Riverbed transparency option */
/*
* TCP option lengths
*/
#define TCPOLEN_MSS 4
#define TCPOLEN_WINDOW 3
#define TCPOLEN_SACK_PERM 2
#define TCPOLEN_SACK_MIN 2
#define TCPOLEN_ECHO 6
#define TCPOLEN_ECHOREPLY 6
#define TCPOLEN_TIMESTAMP 10
#define TCPOLEN_CC 6
#define TCPOLEN_CCNEW 6
#define TCPOLEN_CCECHO 6
#define TCPOLEN_MD5 18
#define TCPOLEN_MPTCP_MIN 8
#define TCPOLEN_SCPS 4
#define TCPOLEN_SNACK 6
#define TCPOLEN_RECBOUND 2
#define TCPOLEN_CORREXP 2
#define TCPOLEN_QS 8
#define TCPOLEN_USER_TO 4
#define TCPOLEN_RVBD_PROBE_MIN 3
#define TCPOLEN_RVBD_TRPY_MIN 16
#define TCPOLEN_EXP_MIN 2
/*
* Multipath TCP subtypes
*/
#define TCPOPT_MPTCP_MP_CAPABLE 0x0 /* Multipath TCP Multipath Capable */
#define TCPOPT_MPTCP_MP_JOIN 0x1 /* Multipath TCP Join Connection */
#define TCPOPT_MPTCP_DSS 0x2 /* Multipath TCP Data Sequence Signal */
#define TCPOPT_MPTCP_ADD_ADDR 0x3 /* Multipath TCP Add Address */
#define TCPOPT_MPTCP_REMOVE_ADDR 0x4 /* Multipath TCP Remove Address */
#define TCPOPT_MPTCP_MP_PRIO 0x5 /* Multipath TCP Change Subflow Priority */
#define TCPOPT_MPTCP_MP_FAIL 0x6 /* Multipath TCP Fallback */
static const true_false_string tcp_option_user_to_granularity = {
"Minutes", "Seconds"
};
static const value_string tcp_option_kind_vs[] = {
{ TCPOPT_EXP_FD, "Experimental 0xFD" },
{ TCPOPT_EXP_FE, "Experimental 0xFE" },
{ TCPOPT_WINDOW, "Window Scale" },
{ TCPOPT_SACK_PERM, "SACK Permission" },
{ TCPOPT_MSS, "MSS size" },
{ TCPOPT_TIMESTAMP, "Timestamp" },
{ TCPOPT_MPTCP, "Multipath TCP" },
{ 0, NULL }
};
/* not all of the hf_fields below make sense for TCP but we have to provide
them anyways to comply with the api (which was aimed for ip fragment
reassembly) */
static const fragment_items tcp_segment_items = {
&ett_tcp_segment,
&ett_tcp_segments,
&hf_tcp_segments,
&hf_tcp_segment,
&hf_tcp_segment_overlap,
&hf_tcp_segment_overlap_conflict,
&hf_tcp_segment_multiple_tails,
&hf_tcp_segment_too_long_fragment,
&hf_tcp_segment_error,
&hf_tcp_segment_count,
&hf_tcp_reassembled_in,
&hf_tcp_reassembled_length,
"Segments"
};
static const value_string mptcp_subtype_vs[] = {
{ TCPOPT_MPTCP_MP_CAPABLE, "Multipath Capable" },
{ TCPOPT_MPTCP_MP_JOIN, "Join Connection" },
{ TCPOPT_MPTCP_DSS, "Data Sequence Signal" },
{ TCPOPT_MPTCP_ADD_ADDR, "Add Address"},
{ TCPOPT_MPTCP_REMOVE_ADDR, "Remove Address" },
{ TCPOPT_MPTCP_MP_PRIO, "Change Subflow Priority" },
{ TCPOPT_MPTCP_MP_FAIL, "TCP Fallback" },
{ 0, NULL }
};
static dissector_table_t subdissector_table;
static heur_dissector_list_t heur_subdissector_list;
static dissector_handle_t data_handle;
static guint32 tcp_stream_index;
/* TCP structs and definitions */
/* **************************************************************************
* RTT, relative sequence numbers, window scaling & etc.
* **************************************************************************/
static gboolean tcp_analyze_seq = TRUE;
static gboolean tcp_relative_seq = TRUE;
static gboolean tcp_track_bytes_in_flight = TRUE;
static gboolean tcp_calculate_ts = FALSE;
/* SLAB allocator for tcp_unacked structures
*/
SLAB_ITEM_TYPE_DEFINE(tcp_unacked_t)
static SLAB_FREE_LIST_DEFINE(tcp_unacked_t)
#define TCP_UNACKED_NEW(fi) \
SLAB_ALLOC(fi, tcp_unacked_t)
#define TCP_UNACKED_FREE(fi) \
SLAB_FREE(fi, tcp_unacked_t)
#define TCP_A_RETRANSMISSION 0x0001
#define TCP_A_LOST_PACKET 0x0002
#define TCP_A_ACK_LOST_PACKET 0x0004
#define TCP_A_KEEP_ALIVE 0x0008
#define TCP_A_DUPLICATE_ACK 0x0010
#define TCP_A_ZERO_WINDOW 0x0020
#define TCP_A_ZERO_WINDOW_PROBE 0x0040
#define TCP_A_ZERO_WINDOW_PROBE_ACK 0x0080
#define TCP_A_KEEP_ALIVE_ACK 0x0100
#define TCP_A_OUT_OF_ORDER 0x0200
#define TCP_A_FAST_RETRANSMISSION 0x0400
#define TCP_A_WINDOW_UPDATE 0x0800
#define TCP_A_WINDOW_FULL 0x1000
#define TCP_A_REUSED_PORTS 0x2000
static void
process_tcp_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
proto_tree *tree, proto_tree *tcp_tree, int src_port, int dst_port,
guint32 seq, guint32 nxtseq, gboolean is_tcp_segment,
struct tcp_analysis *tcpd);
struct tcp_analysis *
init_tcp_conversation_data(packet_info *pinfo)
{
struct tcp_analysis *tcpd;
/* Initialize the tcp protocol data structure to add to the tcp conversation */
tcpd=se_alloc0(sizeof(struct tcp_analysis));
tcpd->flow1.win_scale=-1;
tcpd->flow1.window = G_MAXUINT32;
tcpd->flow1.multisegment_pdus=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "tcp_multisegment_pdus");
/*
tcpd->flow1.username = NULL;
tcpd->flow1.command = NULL;
*/
tcpd->flow2.window = G_MAXUINT32;
tcpd->flow2.win_scale=-1;
tcpd->flow2.multisegment_pdus=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "tcp_multisegment_pdus");
/*
tcpd->flow2.username = NULL;
tcpd->flow2.command = NULL;
*/
tcpd->acked_table=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "tcp_analyze_acked_table");
tcpd->ts_first.secs=pinfo->fd->abs_ts.secs;
tcpd->ts_first.nsecs=pinfo->fd->abs_ts.nsecs;
tcpd->ts_prev.secs=pinfo->fd->abs_ts.secs;
tcpd->ts_prev.nsecs=pinfo->fd->abs_ts.nsecs;
tcpd->flow1.valid_bif = 1;
tcpd->flow2.valid_bif = 1;
tcpd->stream = tcp_stream_index++;
tcpd->server_port = 0;
return tcpd;
}
struct tcp_analysis *
get_tcp_conversation_data(conversation_t *conv, packet_info *pinfo)
{
int direction;
struct tcp_analysis *tcpd;
/* Did the caller supply the conversation pointer? */
if( conv==NULL )
conv = find_or_create_conversation(pinfo);
/* Get the data for this conversation */
tcpd=conversation_get_proto_data(conv, proto_tcp);
/* If the conversation was just created or it matched a
* conversation with template options, tcpd will not
* have been initialized. So, initialize
* a new tcpd structure for the conversation.
*/
if (!tcpd) {
tcpd = init_tcp_conversation_data(pinfo);
conversation_add_proto_data(conv, proto_tcp, tcpd);
}
if (!tcpd) {
return NULL;
}
/* check direction and get ua lists */
direction=CMP_ADDRESS(&pinfo->src, &pinfo->dst);
/* if the addresses are equal, match the ports instead */
if(direction==0) {
direction= (pinfo->srcport > pinfo->destport) ? 1 : -1;
}
if(direction>=0){
tcpd->fwd=&(tcpd->flow1);
tcpd->rev=&(tcpd->flow2);
} else {
tcpd->fwd=&(tcpd->flow2);
tcpd->rev=&(tcpd->flow1);
}
tcpd->ta=NULL;
return tcpd;
}
/* Attach process info to a flow */
/* XXX - We depend on the TCP dissector finding the conversation first */
void
add_tcp_process_info(guint32 frame_num, address *local_addr, address *remote_addr, guint16 local_port, guint16 remote_port, guint32 uid, guint32 pid, gchar *username, gchar *command) {
conversation_t *conv;
struct tcp_analysis *tcpd;
tcp_flow_t *flow = NULL;
conv = find_conversation(frame_num, local_addr, remote_addr, PT_TCP, local_port, remote_port, 0);
if (!conv) {
return;
}
tcpd = conversation_get_proto_data(conv, proto_tcp);
if (!tcpd) {
return;
}
if (CMP_ADDRESS(local_addr, &conv->key_ptr->addr1) == 0 && local_port == conv->key_ptr->port1) {
flow = &tcpd->flow1;
} else if (CMP_ADDRESS(remote_addr, &conv->key_ptr->addr1) == 0 && remote_port == conv->key_ptr->port1) {
flow = &tcpd->flow2;
}
if (!flow || flow->command) {
return;
}
flow->process_uid = uid;
flow->process_pid = pid;
flow->username = se_strdup(username);
flow->command = se_strdup(command);
}
/* Calculate the timestamps relative to this conversation */
static void
tcp_calculate_timestamps(packet_info *pinfo, struct tcp_analysis *tcpd,
struct tcp_per_packet_data_t *tcppd)
{
if( !tcppd ) {
tcppd = se_alloc(sizeof(struct tcp_per_packet_data_t));
p_add_proto_data(pinfo->fd, proto_tcp, tcppd);
}
if (!tcpd)
return;
nstime_delta(&tcppd->ts_del, &pinfo->fd->abs_ts, &tcpd->ts_prev);
tcpd->ts_prev.secs=pinfo->fd->abs_ts.secs;
tcpd->ts_prev.nsecs=pinfo->fd->abs_ts.nsecs;
}
/* Add a subtree with the timestamps relative to this conversation */
static void
tcp_print_timestamps(packet_info *pinfo, tvbuff_t *tvb, proto_tree *parent_tree, struct tcp_analysis *tcpd, struct tcp_per_packet_data_t *tcppd)
{
proto_item *item;
proto_tree *tree;
nstime_t ts;
if (!tcpd)
return;
item=proto_tree_add_text(parent_tree, tvb, 0, 0, "Timestamps");
PROTO_ITEM_SET_GENERATED(item);
tree=proto_item_add_subtree(item, ett_tcp_timestamps);
nstime_delta(&ts, &pinfo->fd->abs_ts, &tcpd->ts_first);
item = proto_tree_add_time(tree, hf_tcp_ts_relative, tvb, 0, 0, &ts);
PROTO_ITEM_SET_GENERATED(item);
if( !tcppd )
tcppd = p_get_proto_data(pinfo->fd, proto_tcp);
if( tcppd ) {
item = proto_tree_add_time(tree, hf_tcp_ts_delta, tvb, 0, 0,
&tcppd->ts_del);
PROTO_ITEM_SET_GENERATED(item);
}
}
static void
print_pdu_tracking_data(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tcp_tree, struct tcp_multisegment_pdu *msp)
{
proto_item *item;
col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[Continuation to #%u] ", msp->first_frame);
item=proto_tree_add_uint(tcp_tree, hf_tcp_continuation_to,
tvb, 0, 0, msp->first_frame);
PROTO_ITEM_SET_GENERATED(item);
}
/* if we know that a PDU starts inside this segment, return the adjusted
offset to where that PDU starts or just return offset back
and let TCP try to find out what it can about this segment
*/
static int
scan_for_next_pdu(tvbuff_t *tvb, proto_tree *tcp_tree, packet_info *pinfo, int offset, guint32 seq, guint32 nxtseq, emem_tree_t *multisegment_pdus)
{
struct tcp_multisegment_pdu *msp=NULL;
if(!pinfo->fd->flags.visited){
msp=se_tree_lookup32_le(multisegment_pdus, seq-1);
if(msp){
/* If this is a continuation of a PDU started in a
* previous segment we need to update the last_frame
* variables.
*/
if(seq>msp->seq && seq<msp->nxtpdu){
msp->last_frame=pinfo->fd->num;
msp->last_frame_time=pinfo->fd->abs_ts;
print_pdu_tracking_data(pinfo, tvb, tcp_tree, msp);
}
/* If this segment is completely within a previous PDU
* then we just skip this packet
*/
if(seq>msp->seq && nxtseq<=msp->nxtpdu){
return -1;
}
if(seq<msp->nxtpdu && nxtseq>msp->nxtpdu){
offset+=msp->nxtpdu-seq;
return offset;
}
}
} else {
/* First we try to find the start and transfer time for a PDU.
* We only print this for the very first segment of a PDU
* and only for PDUs spanning multiple segments.
* Se we look for if there was any multisegment PDU started
* just BEFORE the end of this segment. I.e. either inside this
* segment or in a previous segment.
* Since this might also match PDUs that are completely within
* this segment we also verify that the found PDU does span
* beyond the end of this segment.
*/
msp=se_tree_lookup32_le(multisegment_pdus, nxtseq-1);
if(msp){
if(pinfo->fd->num==msp->first_frame) {
proto_item *item;
nstime_t ns;
item=proto_tree_add_uint(tcp_tree, hf_tcp_pdu_last_frame, tvb, 0, 0, msp->last_frame);
PROTO_ITEM_SET_GENERATED(item);
nstime_delta(&ns, &msp->last_frame_time, &pinfo->fd->abs_ts);
item = proto_tree_add_time(tcp_tree, hf_tcp_pdu_time,
tvb, 0, 0, &ns);
PROTO_ITEM_SET_GENERATED(item);
}
}
/* Second we check if this segment is part of a PDU started
* prior to the segment (seq-1)
*/
msp=se_tree_lookup32_le(multisegment_pdus, seq-1);
if(msp){
/* If this segment is completely within a previous PDU
* then we just skip this packet
*/
if(seq>msp->seq && nxtseq<=msp->nxtpdu){
print_pdu_tracking_data(pinfo, tvb, tcp_tree, msp);
return -1;
}
if(seq<msp->nxtpdu && nxtseq>msp->nxtpdu){
offset+=msp->nxtpdu-seq;
return offset;
}
}
}
return offset;
}
/* if we saw a PDU that extended beyond the end of the segment,
use this function to remember where the next pdu starts
*/
struct tcp_multisegment_pdu *
pdu_store_sequencenumber_of_next_pdu(packet_info *pinfo, guint32 seq, guint32 nxtpdu, emem_tree_t *multisegment_pdus)
{
struct tcp_multisegment_pdu *msp;
msp=se_alloc(sizeof(struct tcp_multisegment_pdu));
msp->nxtpdu=nxtpdu;
msp->seq=seq;
msp->first_frame=pinfo->fd->num;
msp->last_frame=pinfo->fd->num;
msp->last_frame_time=pinfo->fd->abs_ts;
msp->flags=0;
se_tree_insert32(multisegment_pdus, seq, (void *)msp);
return msp;
}
/* This is called for SYN and SYN+ACK packets and the purpose is to verify
* that we have seen window scaling in both directions.
* If we cant find window scaling being set in both directions
* that means it was present in the SYN but not in the SYN+ACK
* (or the SYN was missing) and then we disable the window scaling
* for this tcp session.
*/
static void
verify_tcp_window_scaling(gboolean is_synack, struct tcp_analysis *tcpd)
{
if( tcpd->fwd->win_scale==-1 ) {
/* We know window scaling will not be used as:
* a) this is the SYN and it does not have the WS option
* (we set the reverse win_scale also in case we miss
* the SYN/ACK)
* b) this is the SYN/ACK and either the SYN packet has not
* been seen or it did have the WS option. As the SYN/ACK
* does not have the WS option, window scaling will not be used.
*
* Setting win_scale to -2 to indicate that we can
* trust the window_size value in the TCP header.
*/
tcpd->fwd->win_scale = -2;
tcpd->rev->win_scale = -2;
} else if( is_synack && tcpd->rev->win_scale==-2 ) {
/* The SYN/ACK has the WS option, while the SYN did not,
* this should not happen, but the endpoints will not
* have used window scaling, so we will neither
*/
tcpd->fwd->win_scale = -2;
}
}
/* if we saw a window scaling option, store it for future reference
*/
static void
pdu_store_window_scale_option(guint8 ws, struct tcp_analysis *tcpd)
{
if (tcpd)
tcpd->fwd->win_scale=ws;
}
/* when this function returns, it will (if createflag) populate the ta pointer.
*/
static void
tcp_analyze_get_acked_struct(guint32 frame, guint32 seq, guint32 ack, gboolean createflag, struct tcp_analysis *tcpd)
{
emem_tree_key_t key[] = {{1, &frame}, {1, &seq}, {1, &ack}, {0, NULL}};
if (!tcpd) {
return;
}
tcpd->ta = se_tree_lookup32_array(tcpd->acked_table, key);
if((!tcpd->ta) && createflag){
tcpd->ta = se_alloc0(sizeof(struct tcp_acked));
se_tree_insert32_array(tcpd->acked_table, key, (void *)tcpd->ta);
}
}
/* fwd contains a list of all segments processed but not yet ACKed in the
* same direction as the current segment.
* rev contains a list of all segments received but not yet ACKed in the
* opposite direction to the current segment.
*
* New segments are always added to the head of the fwd/rev lists.
*
*/
static void
tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint32 seglen, guint16 flags, guint32 window, struct tcp_analysis *tcpd)
{
tcp_unacked_t *ual=NULL;
tcp_unacked_t *prevual=NULL;
guint32 nextseq;
int ackcount;
#ifdef REMOVED
printf("analyze_sequence numbers frame:%u direction:%s\n",pinfo->fd->num,direction>=0?"FWD":"REW");
printf("FWD list lastflags:0x%04x base_seq:0x%08x:\n",tcpd->fwd->lastsegmentflags,tcpd->fwd->base_seq);for(ual=tcpd->fwd->segments;ual;ual=ual->next)printf("Frame:%d Seq:%d Nextseq:%d\n",ual->frame,ual->seq,ual->nextseq);
printf("REV list lastflags:0x%04x base_seq:0x%08x:\n",tcpd->rev->lastsegmentflags,tcpd->rev->base_seq);for(ual=tcpd->rev->segments;ual;ual=ual->next)printf("Frame:%d Seq:%d Nextseq:%d\n",ual->frame,ual->seq,ual->nextseq);
#endif
if (!tcpd) {
return;
}
/* if this is the first segment for this list we need to store the
* base_seq
*
* Start relative seq and ack numbers at 1 if this
* is not a SYN packet. This makes the relative
* seq/ack numbers to be displayed correctly in the
* event that the SYN or SYN/ACK packet is not seen
* (this solves bug 1542)
*/
if(tcpd->fwd->base_seq==0){
tcpd->fwd->base_seq = (flags & TH_SYN) ? seq : seq-1;
}
/* Only store reverse sequence if this isn't the SYN
* There's no guarantee that the ACK field of a SYN
* contains zeros; get the ISN from the first segment
* with the ACK bit set instead (usually the SYN/ACK).
*/
if( (tcpd->rev->base_seq==0) && (flags & TH_ACK) ){
tcpd->rev->base_seq = (flags & TH_SYN) ? ack : ack-1;
}
if( flags & TH_ACK ){
tcpd->rev->valid_bif = 1;
}
/* ZERO WINDOW PROBE
* it is a zero window probe if
* the sequence number is the next expected one
* the window in the other direction is 0
* the segment is exactly 1 byte
*/
/*QQQ tested*/
if( seglen==1
&& seq==tcpd->fwd->nextseq
&& tcpd->rev->window==0 ){
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, seq, ack, TRUE, tcpd);
}
tcpd->ta->flags|=TCP_A_ZERO_WINDOW_PROBE;
goto finished_fwd;
}
/* ZERO WINDOW
* a zero window packet has window == 0 but none of the SYN/FIN/RST set
*/
/*QQQ tested*/
if( window==0
&& (flags&(TH_RST|TH_FIN|TH_SYN))==0 ){
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, seq, ack, TRUE, tcpd);
}
tcpd->ta->flags|=TCP_A_ZERO_WINDOW;
}
/* LOST PACKET
* If this segment is beyond the last seen nextseq we must
* have missed some previous segment
*
* We only check for this if we have actually seen segments prior to this
* one.
* RST packets are not checked for this.
*/
if( tcpd->fwd->nextseq
&& GT_SEQ(seq, tcpd->fwd->nextseq)
&& (flags&(TH_RST))==0 ){
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, seq, ack, TRUE, tcpd);
}
tcpd->ta->flags|=TCP_A_LOST_PACKET;
/* Disable BiF until an ACK is seen in the other direction */
tcpd->fwd->valid_bif = 0;
}
/* KEEP ALIVE
* a keepalive contains 0 or 1 bytes of data and starts one byte prior
* to what should be the next sequence number.
* SYN/FIN/RST segments are never keepalives
*/
/*QQQ tested */
if( (seglen==0||seglen==1)
&& seq==(tcpd->fwd->nextseq-1)
&& (flags&(TH_SYN|TH_FIN|TH_RST))==0 ){
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, seq, ack, TRUE, tcpd);
}
tcpd->ta->flags|=TCP_A_KEEP_ALIVE;
}
/* WINDOW UPDATE
* A window update is a 0 byte segment with the same SEQ/ACK numbers as
* the previous seen segment and with a new window value
*/
if( seglen==0
&& window
&& window!=tcpd->fwd->window
&& seq==tcpd->fwd->nextseq
&& ack==tcpd->fwd->lastack
&& (flags&(TH_SYN|TH_FIN|TH_RST))==0 ){
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, seq, ack, TRUE, tcpd);
}
tcpd->ta->flags|=TCP_A_WINDOW_UPDATE;
}
/* WINDOW FULL
* If we know the window scaling
* and if this segment contains data and goes all the way to the
* edge of the advertised window
* then we mark it as WINDOW FULL
* SYN/RST/FIN packets are never WINDOW FULL
*/
/*QQQ tested*/
if( seglen>0
&& tcpd->rev->win_scale!=-1
&& (seq+seglen)==(tcpd->rev->lastack+(tcpd->rev->window<<(tcpd->rev->win_scale==-2?0:tcpd->rev->win_scale)))
&& (flags&(TH_SYN|TH_FIN|TH_RST))==0 ){
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, seq, ack, TRUE, tcpd);
}
tcpd->ta->flags|=TCP_A_WINDOW_FULL;
}
/* KEEP ALIVE ACK
* It is a keepalive ack if it repeats the previous ACK and if
* the last segment in the reverse direction was a keepalive
*/
/*QQQ tested*/
if( seglen==0
&& window
&& window==tcpd->fwd->window
&& seq==tcpd->fwd->nextseq
&& ack==tcpd->fwd->lastack
&& (tcpd->rev->lastsegmentflags&TCP_A_KEEP_ALIVE)
&& (flags&(TH_SYN|TH_FIN|TH_RST))==0 ){
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, seq, ack, TRUE, tcpd);
}
tcpd->ta->flags|=TCP_A_KEEP_ALIVE_ACK;
goto finished_fwd;
}
/* ZERO WINDOW PROBE ACK
* It is a zerowindowprobe ack if it repeats the previous ACK and if
* the last segment in the reverse direction was a zerowindowprobe
* It also repeats the previous zero window indication
*/
/*QQQ tested*/
if( seglen==0
&& window==0
&& window==tcpd->fwd->window
&& seq==tcpd->fwd->nextseq
&& ack==tcpd->fwd->lastack
&& (tcpd->rev->lastsegmentflags&TCP_A_ZERO_WINDOW_PROBE)
&& (flags&(TH_SYN|TH_FIN|TH_RST))==0 ){
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, seq, ack, TRUE, tcpd);
}
tcpd->ta->flags|=TCP_A_ZERO_WINDOW_PROBE_ACK;
goto finished_fwd;
}
/* DUPLICATE ACK
* It is a duplicate ack if window/seq/ack is the same as the previous
* segment and if the segment length is 0
*/
if( seglen==0
&& window
&& window==tcpd->fwd->window
&& seq==tcpd->fwd->nextseq
&& ack==tcpd->fwd->lastack
&& (flags&(TH_SYN|TH_FIN|TH_RST))==0 ){
tcpd->fwd->dupacknum++;
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, seq, ack, TRUE, tcpd);
}
tcpd->ta->flags|=TCP_A_DUPLICATE_ACK;
tcpd->ta->dupack_num=tcpd->fwd->dupacknum;
tcpd->ta->dupack_frame=tcpd->fwd->lastnondupack;
}
finished_fwd:
/* If this was NOT a dupack we must reset the dupack counters */
if( (!tcpd->ta) || !(tcpd->ta->flags&TCP_A_DUPLICATE_ACK) ){
tcpd->fwd->lastnondupack=pinfo->fd->num;
tcpd->fwd->dupacknum=0;
}
/* ACKED LOST PACKET
* If this segment acks beyond the 'max seq to be acked' in the other direction
* then that means we have missed packets going in the
* other direction
*
* We only check this if we have actually seen some seq numbers
* in the other direction.
*/
if( tcpd->rev->maxseqtobeacked
&& GT_SEQ(ack, tcpd->rev->maxseqtobeacked )
&& (flags&(TH_ACK))!=0 ){
/*QQQ tested*/
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, seq, ack, TRUE, tcpd);
}
tcpd->ta->flags|=TCP_A_ACK_LOST_PACKET;
/* update 'max seq to be acked' in the other direction so we don't get
* this indication again.
*/
tcpd->rev->maxseqtobeacked=tcpd->rev->nextseq;
}
/* RETRANSMISSION/FAST RETRANSMISSION/OUT-OF-ORDER
* If the segments contains data and if it does not advance
* sequence number it must be either of these three.
* Only test for this if we know what the seq number should be
* (tcpd->fwd->nextseq)
*
* Note that a simple KeepAlive is not a retransmission
*/
if( seglen>0
&& tcpd->fwd->nextseq
&& (LT_SEQ(seq, tcpd->fwd->nextseq)) ){
guint64 t;
if(tcpd->ta && (tcpd->ta->flags&TCP_A_KEEP_ALIVE) ){
goto finished_checking_retransmission_type;
}
/* If there were >=2 duplicate ACKs in the reverse direction
* (there might be duplicate acks missing from the trace)
* and if this sequence number matches those ACKs
* and if the packet occurs within 20ms of the last
* duplicate ack
* then this is a fast retransmission
*/
t=(pinfo->fd->abs_ts.secs-tcpd->rev->lastacktime.secs)*1000000000;
t=t+(pinfo->fd->abs_ts.nsecs)-tcpd->rev->lastacktime.nsecs;
if( tcpd->rev->dupacknum>=2
&& tcpd->rev->lastack==seq
&& t<20000000 ){
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, seq, ack, TRUE, tcpd);
}
tcpd->ta->flags|=TCP_A_FAST_RETRANSMISSION;
goto finished_checking_retransmission_type;
}
/* If the segment came <3ms since the segment with the highest
* seen sequence number and it doesn't look like a retransmission
* then it is an OUT-OF-ORDER segment.
* (3ms is an arbitrary number)
*/
t=(pinfo->fd->abs_ts.secs-tcpd->fwd->nextseqtime.secs)*1000000000;
t=t+(pinfo->fd->abs_ts.nsecs)-tcpd->fwd->nextseqtime.nsecs;
if( t<3000000
&& tcpd->fwd->nextseq != seq + seglen ){
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, seq, ack, TRUE, tcpd);
}
tcpd->ta->flags|=TCP_A_OUT_OF_ORDER;
goto finished_checking_retransmission_type;
}
/* Then it has to be a generic retransmission */
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, seq, ack, TRUE, tcpd);
}
tcpd->ta->flags|=TCP_A_RETRANSMISSION;
nstime_delta(&tcpd->ta->rto_ts, &pinfo->fd->abs_ts, &tcpd->fwd->nextseqtime);
tcpd->ta->rto_frame=tcpd->fwd->nextseqframe;
}
finished_checking_retransmission_type:
nextseq = seq+seglen;
if (seglen || flags&(TH_SYN|TH_FIN)) {
/* add this new sequence number to the fwd list */
TCP_UNACKED_NEW(ual);
ual->next=tcpd->fwd->segments;
tcpd->fwd->segments=ual;
ual->frame=pinfo->fd->num;
ual->seq=seq;
ual->ts=pinfo->fd->abs_ts;
/* next sequence number is seglen bytes away, plus SYN/FIN which counts as one byte */
if( (flags&(TH_SYN|TH_FIN)) ){
nextseq+=1;
}
ual->nextseq=nextseq;
}
/* Store the highest number seen so far for nextseq so we can detect
* when we receive segments that arrive with a "hole"
* If we don't have anything since before, just store what we got.
* ZeroWindowProbes are special and don't really advance the nextseq
*/
if(GT_SEQ(nextseq, tcpd->fwd->nextseq) || !tcpd->fwd->nextseq) {
if( !tcpd->ta || !(tcpd->ta->flags&TCP_A_ZERO_WINDOW_PROBE) ){
tcpd->fwd->nextseq=nextseq;
tcpd->fwd->nextseqframe=pinfo->fd->num;
tcpd->fwd->nextseqtime.secs=pinfo->fd->abs_ts.secs;
tcpd->fwd->nextseqtime.nsecs=pinfo->fd->abs_ts.nsecs;
}
}
/* Store the highest continuous seq number seen so far for 'max seq to be acked',
so we can detect TCP_A_ACK_LOST_PACKET condition
*/
if(EQ_SEQ(seq, tcpd->fwd->maxseqtobeacked) || !tcpd->fwd->maxseqtobeacked) {
if( !tcpd->ta || !(tcpd->ta->flags&TCP_A_ZERO_WINDOW_PROBE) ){
tcpd->fwd->maxseqtobeacked=tcpd->fwd->nextseq;
}
}
/* remember what the ack/window is so we can track window updates and retransmissions */
tcpd->fwd->window=window;
tcpd->fwd->lastack=ack;
tcpd->fwd->lastacktime.secs=pinfo->fd->abs_ts.secs;
tcpd->fwd->lastacktime.nsecs=pinfo->fd->abs_ts.nsecs;
/* if there were any flags set for this segment we need to remember them
* we only remember the flags for the very last segment though.
*/
if(tcpd->ta){
tcpd->fwd->lastsegmentflags=tcpd->ta->flags;
} else {
tcpd->fwd->lastsegmentflags=0;
}
/* remove all segments this ACKs and we don't need to keep around any more
*/
ackcount=0;
prevual = NULL;
ual = tcpd->rev->segments;
while(ual){
tcp_unacked_t *tmpual;
/* If this ack matches the segment, process accordingly */
if(ack==ual->nextseq){
tcp_analyze_get_acked_struct(pinfo->fd->num, seq, ack, TRUE, tcpd);
tcpd->ta->frame_acked=ual->frame;
nstime_delta(&tcpd->ta->ts, &pinfo->fd->abs_ts, &ual->ts);
}
/* If this acknowledges part of the segment, adjust the segment info for the acked part */
else if (GT_SEQ(ack, ual->seq) && LE_SEQ(ack, ual->nextseq)) {
ual->seq = ack;
continue;
}
/* If this acknowledges a segment prior to this one, leave this segment alone and move on */
else if (GT_SEQ(ual->nextseq,ack)){
prevual = ual;
ual = ual->next;
continue;
}
/* This segment is old, or an exact match. Delete the segment from the list */
ackcount++;
tmpual=ual->next;
if (tcpd->rev->scps_capable) {
/* Track largest segment successfully sent for SNACK analysis*/
if ((ual->nextseq - ual->seq) > tcpd->fwd->maxsizeacked){
tcpd->fwd->maxsizeacked = (ual->nextseq - ual->seq);
}
}
if (!prevual){
tcpd->rev->segments = tmpual;
TCP_UNACKED_FREE(ual);
ual = tmpual;
}
else{
prevual->next = tmpual;
TCP_UNACKED_FREE(ual);
ual = tmpual;
}
}
/* how many bytes of data are there in flight after this frame
* was sent
*/
ual=tcpd->fwd->segments;
if (tcp_track_bytes_in_flight && seglen!=0 && ual && tcpd->fwd->valid_bif) {
guint32 first_seq, last_seq, in_flight;
first_seq = ual->seq - tcpd->fwd->base_seq;
last_seq = ual->nextseq - tcpd->fwd->base_seq;
while (ual) {
if ((ual->nextseq-tcpd->fwd->base_seq)>last_seq) {
last_seq = ual->nextseq-tcpd->fwd->base_seq;
}
if ((ual->seq-tcpd->fwd->base_seq)<first_seq) {
first_seq = ual->seq-tcpd->fwd->base_seq;
}
ual = ual->next;
}
in_flight = last_seq-first_seq;
if (in_flight>0 && in_flight<2000000000) {
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, seq, ack, TRUE, tcpd);
}
tcpd->ta->bytes_in_flight = in_flight;
}
}
}
/*
* Prints results of the sequence number analysis concerning tcp segments
* retransmitted or out-of-order
*/
static void
tcp_sequence_number_analysis_print_retransmission(packet_info * pinfo,
tvbuff_t * tvb,
proto_tree * flags_tree,
struct tcp_acked *ta
)
{
proto_item * flags_item;
/* TCP Retransmission */
if (ta->flags & TCP_A_RETRANSMISSION) {
flags_item=proto_tree_add_none_format(flags_tree,
hf_tcp_analysis_retransmission,
tvb, 0, 0,
"This frame is a (suspected) "
"retransmission"
);
PROTO_ITEM_SET_GENERATED(flags_item);
expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE,
"Retransmission (suspected)");
col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP Retransmission] ");
if (ta->rto_ts.secs || ta->rto_ts.nsecs) {
flags_item = proto_tree_add_time(flags_tree, hf_tcp_analysis_rto,
tvb, 0, 0, &ta->rto_ts);
PROTO_ITEM_SET_GENERATED(flags_item);
flags_item=proto_tree_add_uint(flags_tree, hf_tcp_analysis_rto_frame,
tvb, 0, 0, ta->rto_frame);
PROTO_ITEM_SET_GENERATED(flags_item);
}
}
/* TCP Fast Retransmission */
if (ta->flags & TCP_A_FAST_RETRANSMISSION) {
flags_item=proto_tree_add_none_format(flags_tree,
hf_tcp_analysis_fast_retransmission,
tvb, 0, 0,
"This frame is a (suspected) fast"
" retransmission"
);
PROTO_ITEM_SET_GENERATED(flags_item);
expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE,
"Fast retransmission (suspected)");
flags_item=proto_tree_add_none_format(flags_tree,
hf_tcp_analysis_retransmission,
tvb, 0, 0,
"This frame is a (suspected) "
"retransmission"
);
PROTO_ITEM_SET_GENERATED(flags_item);
col_prepend_fence_fstr(pinfo->cinfo, COL_INFO,
"[TCP Fast Retransmission] ");
}
/* TCP Out-Of-Order */
if (ta->flags & TCP_A_OUT_OF_ORDER) {
flags_item=proto_tree_add_none_format(flags_tree,
hf_tcp_analysis_out_of_order,
tvb, 0, 0,
"This frame is a (suspected) "
"out-of-order segment"
);
PROTO_ITEM_SET_GENERATED(flags_item);
expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_WARN,
"Out-Of-Order segment");
col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP Out-Of-Order] ");
}
}
/* Prints results of the sequence number analysis concerning reused ports */
static void
tcp_sequence_number_analysis_print_reused(packet_info * pinfo,
tvbuff_t * tvb,
proto_tree * flags_tree,
struct tcp_acked *ta
)
{
proto_item * flags_item;
/* TCP Ports Reused */
if (ta->flags & TCP_A_REUSED_PORTS) {
flags_item=proto_tree_add_none_format(flags_tree,
hf_tcp_analysis_reused_ports,
tvb, 0, 0,
"A new tcp session is started with the same "
"ports as an earlier session in this trace"
);
PROTO_ITEM_SET_GENERATED(flags_item);
expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE,
"TCP Port numbers reused for new session");
col_prepend_fence_fstr(pinfo->cinfo, COL_INFO,
"[TCP Port numbers reused] ");
}
}
/* Prints results of the sequence number analysis concerning lost tcp segments */
static void
tcp_sequence_number_analysis_print_lost(packet_info * pinfo,
tvbuff_t * tvb,
proto_tree * flags_tree,
struct tcp_acked *ta
)
{
proto_item * flags_item;
/* TCP Lost Segment */
if (ta->flags & TCP_A_LOST_PACKET) {
flags_item=proto_tree_add_none_format(flags_tree,
hf_tcp_analysis_lost_packet,
tvb, 0, 0,
"A segment before this frame "
"wasn't captured"
);
PROTO_ITEM_SET_GENERATED(flags_item);
expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_WARN,
"Previous segment not captured (common at capture start)");
col_prepend_fence_fstr(pinfo->cinfo, COL_INFO,
"[TCP Previous segment not captured] ");
}
/* TCP Ack lost segment */
if (ta->flags & TCP_A_ACK_LOST_PACKET) {
flags_item=proto_tree_add_none_format(flags_tree,
hf_tcp_analysis_ack_lost_packet,
tvb, 0, 0,
"This frame ACKs a segment we have "
"not seen"
);
PROTO_ITEM_SET_GENERATED(flags_item);
expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_WARN,
"ACKed segment that wasn't captured (common at capture start)");
col_prepend_fence_fstr(pinfo->cinfo, COL_INFO,
"[TCP ACKed unseen segment] ");
}
}
/* Prints results of the sequence number analysis concerning tcp window */
static void
tcp_sequence_number_analysis_print_window(packet_info * pinfo,
tvbuff_t * tvb,
proto_tree * flags_tree,
struct tcp_acked *ta
)
{
proto_item * flags_item;
/* TCP Window Update */
if (ta->flags & TCP_A_WINDOW_UPDATE) {
flags_item=proto_tree_add_none_format(flags_tree,
hf_tcp_analysis_window_update,
tvb, 0, 0,
"This is a tcp window update"
);
PROTO_ITEM_SET_GENERATED(flags_item);
expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_CHAT,
"Window update");
col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP Window Update] ");
}
/* TCP Full Window */
if (ta->flags & TCP_A_WINDOW_FULL) {
flags_item=proto_tree_add_none_format(flags_tree,
hf_tcp_analysis_window_full,
tvb, 0, 0,
"The transmission window is now "
"completely full"
);
PROTO_ITEM_SET_GENERATED(flags_item);
expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_WARN,
"Window is full");
col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP Window Full] ");
}
}
/* Prints results of the sequence number analysis concerning tcp keepalive */
static void
tcp_sequence_number_analysis_print_keepalive(packet_info * pinfo,
tvbuff_t * tvb,
proto_tree * flags_tree,
struct tcp_acked *ta
)
{
proto_item * flags_item;
/*TCP Keep Alive */
if (ta->flags & TCP_A_KEEP_ALIVE){
flags_item=proto_tree_add_none_format(flags_tree,
hf_tcp_analysis_keep_alive,
tvb, 0, 0,
"This is a TCP keep-alive segment"
);
PROTO_ITEM_SET_GENERATED(flags_item);
expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE,
"Keep-Alive");
col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP Keep-Alive] ");
}
/* TCP Ack Keep Alive */
if (ta->flags & TCP_A_KEEP_ALIVE_ACK) {
flags_item=proto_tree_add_none_format(flags_tree,
hf_tcp_analysis_keep_alive_ack,
tvb, 0, 0,
"This is an ACK to a TCP keep-alive "
"segment"
);
PROTO_ITEM_SET_GENERATED(flags_item);
expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE,
"Keep-Alive ACK");
col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP Keep-Alive ACK] ");
}
}
/* Prints results of the sequence number analysis concerning tcp duplicate ack */
static void
tcp_sequence_number_analysis_print_duplicate(packet_info * pinfo,
tvbuff_t * tvb,
proto_tree * flags_tree,
struct tcp_acked *ta,
proto_tree * tree
)
{
proto_item * flags_item;
/* TCP Duplicate ACK */
if (ta->dupack_num) {
if (ta->flags & TCP_A_DUPLICATE_ACK ) {
flags_item=proto_tree_add_none_format(flags_tree,
hf_tcp_analysis_duplicate_ack,
tvb, 0, 0,
"This is a TCP duplicate ack"
);
PROTO_ITEM_SET_GENERATED(flags_item);
col_prepend_fence_fstr(pinfo->cinfo, COL_INFO,
"[TCP Dup ACK %u#%u] ",
ta->dupack_frame,
ta->dupack_num
);
}
flags_item=proto_tree_add_uint(tree, hf_tcp_analysis_duplicate_ack_num,
tvb, 0, 0, ta->dupack_num);
PROTO_ITEM_SET_GENERATED(flags_item);
flags_item=proto_tree_add_uint(tree, hf_tcp_analysis_duplicate_ack_frame,
tvb, 0, 0, ta->dupack_frame);
PROTO_ITEM_SET_GENERATED(flags_item);
expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE,
"Duplicate ACK (#%u)",
ta->dupack_num
);
}
}
/* Prints results of the sequence number analysis concerning tcp zero window */
static void
tcp_sequence_number_analysis_print_zero_window(packet_info * pinfo,
tvbuff_t * tvb,
proto_tree * flags_tree,
struct tcp_acked *ta
)
{
proto_item * flags_item;
/* TCP Zero Window Probe */
if (ta->flags & TCP_A_ZERO_WINDOW_PROBE) {
flags_item=proto_tree_add_none_format(flags_tree,
hf_tcp_analysis_zero_window_probe,
tvb, 0, 0,
"This is a TCP zero-window-probe"
);
PROTO_ITEM_SET_GENERATED(flags_item);
expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE,
"Zero window probe");
col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP ZeroWindowProbe] ");
}
/* TCP Zero Window */
if (ta->flags&TCP_A_ZERO_WINDOW) {
flags_item=proto_tree_add_none_format(flags_tree,
hf_tcp_analysis_zero_window,
tvb, 0, 0,
"This is a ZeroWindow segment"
);
PROTO_ITEM_SET_GENERATED(flags_item);
expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_WARN,
"Zero window");
col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP ZeroWindow] ");
}
/* TCP Zero Window Probe Ack */
if (ta->flags & TCP_A_ZERO_WINDOW_PROBE_ACK) {
flags_item=proto_tree_add_none_format(flags_tree,
hf_tcp_analysis_zero_window_probe_ack,
tvb, 0, 0,
"This is an ACK to a TCP zero-window-probe"
);
PROTO_ITEM_SET_GENERATED(flags_item);
expert_add_info_format(pinfo, flags_item, PI_SEQUENCE, PI_NOTE,
"Zero window probe ACK");
col_prepend_fence_fstr(pinfo->cinfo, COL_INFO,
"[TCP ZeroWindowProbeAck] ");
}
}
/* Prints results of the sequence number analysis concerning how many bytes of data are in flight */
static void
tcp_sequence_number_analysis_print_bytes_in_flight(packet_info * pinfo _U_,
tvbuff_t * tvb _U_,
proto_tree * flags_tree _U_,
struct tcp_acked *ta
)
{
proto_item * flags_item;
if (tcp_track_bytes_in_flight) {
flags_item=proto_tree_add_uint(flags_tree,
hf_tcp_analysis_bytes_in_flight,
tvb, 0, 0, ta->bytes_in_flight);
PROTO_ITEM_SET_GENERATED(flags_item);
}
}
static void
tcp_print_sequence_number_analysis(packet_info *pinfo, tvbuff_t *tvb, proto_tree *parent_tree,
struct tcp_analysis *tcpd, guint32 seq, guint32 ack)
{
struct tcp_acked *ta = NULL;
proto_item *item;
proto_tree *tree;
proto_tree *flags_tree=NULL;
if (!tcpd) {
return;
}
if(!tcpd->ta){
tcp_analyze_get_acked_struct(pinfo->fd->num, seq, ack, FALSE, tcpd);
}
ta=tcpd->ta;
if(!ta){
return;
}
item=proto_tree_add_item(parent_tree, hf_tcp_analysis, tvb, 0, 0, ENC_NA);
PROTO_ITEM_SET_GENERATED(item);
tree=proto_item_add_subtree(item, ett_tcp_analysis);
/* encapsulate all proto_tree_add_xxx in ifs so we only print what
data we actually have */
if(ta->frame_acked){
item = proto_tree_add_uint(tree, hf_tcp_analysis_acks_frame,
tvb, 0, 0, ta->frame_acked);
PROTO_ITEM_SET_GENERATED(item);
/* only display RTT if we actually have something we are acking */
if( ta->ts.secs || ta->ts.nsecs ){
item = proto_tree_add_time(tree, hf_tcp_analysis_ack_rtt,
tvb, 0, 0, &ta->ts);
PROTO_ITEM_SET_GENERATED(item);
}
}
if(ta->bytes_in_flight) {
/* print results for amount of data in flight */
tcp_sequence_number_analysis_print_bytes_in_flight(pinfo, tvb, tree, ta);
}
if(ta->flags){
item = proto_tree_add_item(tree, hf_tcp_analysis_flags, tvb, 0, 0, ENC_NA);
PROTO_ITEM_SET_GENERATED(item);
flags_tree=proto_item_add_subtree(item, ett_tcp_analysis);
/* print results for reused tcp ports */
tcp_sequence_number_analysis_print_reused(pinfo, tvb, flags_tree, ta);
/* print results for retransmission and out-of-order segments */
tcp_sequence_number_analysis_print_retransmission(pinfo, tvb, flags_tree, ta);
/* print results for lost tcp segments */
tcp_sequence_number_analysis_print_lost(pinfo, tvb, flags_tree, ta);
/* print results for tcp window information */
tcp_sequence_number_analysis_print_window(pinfo, tvb, flags_tree, ta);
/* print results for tcp keep alive information */
tcp_sequence_number_analysis_print_keepalive(pinfo, tvb, flags_tree, ta);
/* print results for tcp duplicate acks */
tcp_sequence_number_analysis_print_duplicate(pinfo, tvb, flags_tree, ta, tree);
/* print results for tcp zero window */
tcp_sequence_number_analysis_print_zero_window(pinfo, tvb, flags_tree, ta);
}
}
static void
print_tcp_fragment_tree(fragment_data *ipfd_head, proto_tree *tree, proto_tree *tcp_tree, packet_info *pinfo, tvbuff_t *next_tvb)
{
proto_item *tcp_tree_item, *frag_tree_item;
/*
* The subdissector thought it was completely
* desegmented (although the stuff at the
* end may, in turn, require desegmentation),
* so we show a tree with all segments.
*/
show_fragment_tree(ipfd_head, &tcp_segment_items,
tree, pinfo, next_tvb, &frag_tree_item);
/*
* The toplevel fragment subtree is now
* behind all desegmented data; move it
* right behind the TCP tree.
*/
tcp_tree_item = proto_tree_get_parent(tcp_tree);
if(frag_tree_item && tcp_tree_item) {
proto_tree_move_item(tree, tcp_tree_item, frag_tree_item);
}
}
/* **************************************************************************
* End of tcp sequence number analysis
* **************************************************************************/
/* Minimum TCP header length. */
#define TCPH_MIN_LEN 20
/* Desegmentation of TCP streams */
/* table to hold defragmented TCP streams */
static GHashTable *tcp_fragment_table = NULL;
/* functions to trace tcp segments */
/* Enable desegmenting of TCP streams */
static gboolean tcp_desegment = TRUE;
static void
desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
guint32 seq, guint32 nxtseq,
guint32 sport, guint32 dport,
proto_tree *tree, proto_tree *tcp_tree,
struct tcp_analysis *tcpd)
{
struct tcpinfo *tcpinfo = pinfo->private_data;
fragment_data *ipfd_head;
int last_fragment_len;
gboolean must_desegment;
gboolean called_dissector;
int another_pdu_follows;
int deseg_offset;
guint32 deseg_seq;
gint nbytes;
proto_item *item;
struct tcp_multisegment_pdu *msp;
gboolean cleared_writable = col_get_writable(pinfo->cinfo);
again:
ipfd_head = NULL;
last_fragment_len = 0;
must_desegment = FALSE;
called_dissector = FALSE;
another_pdu_follows = 0;
msp = NULL;
/*
* Initialize these to assume no desegmentation.
* If that's not the case, these will be set appropriately
* by the subdissector.
*/
pinfo->desegment_offset = 0;
pinfo->desegment_len = 0;
/*
* Initialize this to assume that this segment will just be
* added to the middle of a desegmented chunk of data, so
* that we should show it all as data.
* If that's not the case, it will be set appropriately.
*/
deseg_offset = offset;
if (tcpd) {
/* Have we seen this PDU before (and is it the start of a multi-
* segment PDU)?
*/
if ((msp = se_tree_lookup32(tcpd->fwd->multisegment_pdus, seq))) {
const char* str;
/* Yes. This could be because we've dissected this frame before
* or because this is a retransmission of a previously-seen
* segment. Either way, we don't need to hand it off to the
* subdissector and we certainly don't want to re-add it to the
* multisegment_pdus list: if we did, subsequent lookups would
* find this retransmission instead of the original transmission
* (breaking desegmentation if we'd already linked other segments
* to the original transmission's entry).
*/
if (msp->first_frame == PINFO_FD_NUM(pinfo)) {
str = "";
col_set_str(pinfo->cinfo, COL_INFO, "[TCP segment of a reassembled PDU]");
} else {
str = "Retransmitted ";
/* TCP analysis already flags this (in COL_INFO) as a retransmission--if it's enabled */
}
nbytes = MAX(0, tvb_reported_length_remaining(tvb, offset));
proto_tree_add_bytes_format(tcp_tree, hf_tcp_data, tvb, offset,
nbytes, NULL, "%sTCP segment data (%u byte%s)", str, nbytes,
plurality(nbytes, "", "s"));
return;
}
/* Else, find the most previous PDU starting before this sequence number */
msp = se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq-1);
}
if (msp && msp->seq <= seq && msp->nxtpdu > seq) {
int len;
if (!PINFO_FD_VISITED(pinfo)) {
msp->last_frame=pinfo->fd->num;
msp->last_frame_time=pinfo->fd->abs_ts;
}
/* OK, this PDU was found, which means the segment continues
* a higher-level PDU and that we must desegment it.
*/
if (msp->flags&MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT) {
/* The dissector asked for the entire segment */
len = MAX(0, tvb_length_remaining(tvb, offset));
} else {
len = MIN(nxtseq, msp->nxtpdu) - seq;
}
last_fragment_len = len;
ipfd_head = fragment_add(tvb, offset, pinfo, msp->first_frame,
tcp_fragment_table, seq - msp->seq, len,
(LT_SEQ (nxtseq,msp->nxtpdu)) );
if (msp->flags & MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT) {
msp->flags &= (~MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT);
/* If we consumed the entire segment there is no
* other pdu starting anywhere inside this segment.
* So update nxtpdu to point at least to the start
* of the next segment.
* (If the subdissector asks for even more data we
* will advance nxtpdu even further later down in
* the code.)
*/
msp->nxtpdu = nxtseq;
}
if( (msp->nxtpdu < nxtseq)
&& (msp->nxtpdu >= seq)
&& (len > 0)) {
another_pdu_follows=msp->nxtpdu - seq;
}
} else {
/* This segment was not found in our table, so it doesn't
* contain a continuation of a higher-level PDU.
* Call the normal subdissector.
*/
/*
* Supply the sequence number of this segment. We set this here
* because this segment could be after another in the same packet,
* in which case seq was incremented at the end of the loop.
*/
tcpinfo->seq = seq;
process_tcp_payload(tvb, offset, pinfo, tree, tcp_tree,
sport, dport, 0, 0, FALSE, tcpd);
called_dissector = TRUE;
/* Did the subdissector ask us to desegment some more data
* before it could handle the packet?
* If so we have to create some structures in our table but
* this is something we only do the first time we see this
* packet.
*/
if(pinfo->desegment_len) {
if (!PINFO_FD_VISITED(pinfo))
must_desegment = TRUE;
/*
* Set "deseg_offset" to the offset in "tvb"
* of the first byte of data that the
* subdissector didn't process.
*/
deseg_offset = offset + pinfo->desegment_offset;
}
/* Either no desegmentation is necessary, or this is
* segment contains the beginning but not the end of
* a higher-level PDU and thus isn't completely
* desegmented.
*/
ipfd_head = NULL;
}
/* is it completely desegmented? */
if (ipfd_head) {
/*
* Yes, we think it is.
* We only call subdissector for the last segment.
* Note that the last segment may include more than what
* we needed.
*/
if(ipfd_head->reassembled_in == pinfo->fd->num) {
/*
* OK, this is the last segment.
* Let's call the subdissector with the desegmented
* data.
*/
tvbuff_t *next_tvb;
int old_len;
/* create a new TVB structure for desegmented data */
next_tvb = tvb_new_child_real_data(tvb, ipfd_head->data,
ipfd_head->datalen,
ipfd_head->datalen);
/* add desegmented data to the data source list */
add_new_data_source(pinfo, next_tvb, "Reassembled TCP");
/*
* Supply the sequence number of the first of the
* reassembled bytes.
*/
tcpinfo->seq = msp->seq;
/* indicate that this is reassembled data */
tcpinfo->is_reassembled = TRUE;
/* call subdissector */
process_tcp_payload(next_tvb, 0, pinfo, tree, tcp_tree, sport,
dport, 0, 0, FALSE, tcpd);
called_dissector = TRUE;
/*
* OK, did the subdissector think it was completely
* desegmented, or does it think we need even more
* data?
*/
old_len = (int)(tvb_reported_length(next_tvb) - last_fragment_len);
if (pinfo->desegment_len &&
pinfo->desegment_offset<=old_len) {
/*
* "desegment_len" isn't 0, so it needs more
* data for something - and "desegment_offset"
* is before "old_len", so it needs more data
* to dissect the stuff we thought was
* completely desegmented (as opposed to the
* stuff at the beginning being completely
* desegmented, but the stuff at the end
* being a new higher-level PDU that also
* needs desegmentation).
*/
fragment_set_partial_reassembly(pinfo,msp->first_frame, tcp_fragment_table);
/* Update msp->nxtpdu to point to the new next
* pdu boundary.
*/
if (pinfo->desegment_len == DESEGMENT_ONE_MORE_SEGMENT) {
/* We want reassembly of at least one
* more segment so set the nxtpdu
* boundary to one byte into the next
* segment.
* This means that the next segment
* will complete reassembly even if it
* is only one single byte in length.
*/
msp->nxtpdu = seq + MAX(0, tvb_reported_length_remaining(tvb, offset)) + 1;
msp->flags |= MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT;
} else if (pinfo->desegment_len == DESEGMENT_UNTIL_FIN) {
tcpd->fwd->flags |= TCP_FLOW_REASSEMBLE_UNTIL_FIN;
} else {
msp->nxtpdu=seq + last_fragment_len + pinfo->desegment_len;
}
/* Since we need at least some more data
* there can be no pdu following in the
* tail of this segment.
*/
another_pdu_follows = 0;
offset += last_fragment_len;
seq += last_fragment_len;
if (tvb_length_remaining(tvb, offset) > 0)
goto again;
} else {
/*
* Show the stuff in this TCP segment as
* just raw TCP segment data.
*/
nbytes = another_pdu_follows > 0
? another_pdu_follows
: MAX(0, tvb_reported_length_remaining(tvb, offset));
proto_tree_add_bytes_format(tcp_tree, hf_tcp_data, tvb, offset,
nbytes, NULL, "TCP segment data (%u byte%s)", nbytes,
plurality(nbytes, "", "s"));
print_tcp_fragment_tree(ipfd_head, tree, tcp_tree, pinfo, next_tvb);
/* Did the subdissector ask us to desegment
* some more data? This means that the data
* at the beginning of this segment completed
* a higher-level PDU, but the data at the
* end of this segment started a higher-level
* PDU but didn't complete it.
*
* If so, we have to create some structures
* in our table, but this is something we
* only do the first time we see this packet.
*/
if(pinfo->desegment_len) {
if (!PINFO_FD_VISITED(pinfo))
must_desegment = TRUE;
/* The stuff we couldn't dissect
* must have come from this segment,
* so it's all in "tvb".
*
* "pinfo->desegment_offset" is
* relative to the beginning of
* "next_tvb"; we want an offset
* relative to the beginning of "tvb".
*
* First, compute the offset relative
* to the *end* of "next_tvb" - i.e.,
* the number of bytes before the end
* of "next_tvb" at which the
* subdissector stopped. That's the
* length of "next_tvb" minus the
* offset, relative to the beginning
* of "next_tvb, at which the
* subdissector stopped.
*/
deseg_offset = ipfd_head->datalen - pinfo->desegment_offset;
/* "tvb" and "next_tvb" end at the
* same byte of data, so the offset
* relative to the end of "next_tvb"
* of the byte at which we stopped
* is also the offset relative to
* the end of "tvb" of the byte at
* which we stopped.
*
* Convert that back into an offset
* relative to the beginning of
* "tvb", by taking the length of
* "tvb" and subtracting the offset
* relative to the end.
*/
deseg_offset = tvb_reported_length(tvb) - deseg_offset;
}
}
}
}
if (must_desegment) {
/* If the dissector requested "reassemble until FIN"
* just set this flag for the flow and let reassembly
* proceed at normal. We will check/pick up these
* reassembled PDUs later down in dissect_tcp() when checking
* for the FIN flag.
*/
if (tcpd && pinfo->desegment_len == DESEGMENT_UNTIL_FIN) {
tcpd->fwd->flags |= TCP_FLOW_REASSEMBLE_UNTIL_FIN;
}
/*
* The sequence number at which the stuff to be desegmented
* starts is the sequence number of the byte at an offset
* of "deseg_offset" into "tvb".
*
* The sequence number of the byte at an offset of "offset"
* is "seq", i.e. the starting sequence number of this
* segment, so the sequence number of the byte at
* "deseg_offset" is "seq + (deseg_offset - offset)".
*/
deseg_seq = seq + (deseg_offset - offset);
if (tcpd && ((nxtseq - deseg_seq) <= 1024*1024)
&& (!PINFO_FD_VISITED(pinfo))) {
if(pinfo->desegment_len == DESEGMENT_ONE_MORE_SEGMENT) {
/* The subdissector asked to reassemble using the
* entire next segment.
* Just ask reassembly for one more byte
* but set this msp flag so we can pick it up
* above.
*/
msp = pdu_store_sequencenumber_of_next_pdu(pinfo, deseg_seq,
nxtseq+1, tcpd->fwd->multisegment_pdus);
msp->flags |= MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT;
} else {
msp = pdu_store_sequencenumber_of_next_pdu(pinfo,
deseg_seq, nxtseq+pinfo->desegment_len, tcpd->fwd->multisegment_pdus);
}
/* add this segment as the first one for this new pdu */
fragment_add(tvb, deseg_offset, pinfo, msp->first_frame,
tcp_fragment_table, 0, nxtseq - deseg_seq,
LT_SEQ(nxtseq, msp->nxtpdu));
}
}
if (!called_dissector || pinfo->desegment_len != 0) {
if (ipfd_head != NULL && ipfd_head->reassembled_in != 0 &&
!(ipfd_head->flags & FD_PARTIAL_REASSEMBLY)) {
/*
* We know what frame this PDU is reassembled in;
* let the user know.
*/
item = proto_tree_add_uint(tcp_tree, hf_tcp_reassembled_in, tvb, 0,
0, ipfd_head->reassembled_in);
PROTO_ITEM_SET_GENERATED(item);
}
/*
* Either we didn't call the subdissector at all (i.e.,
* this is a segment that contains the middle of a
* higher-level PDU, but contains neither the beginning
* nor the end), or the subdissector couldn't dissect it
* all, as some data was missing (i.e., it set
* "pinfo->desegment_len" to the amount of additional
* data it needs).
*/
if (pinfo->desegment_offset == 0) {
/*
* It couldn't, in fact, dissect any of it (the
* first byte it couldn't dissect is at an offset
* of "pinfo->desegment_offset" from the beginning
* of the payload, and that's 0).
* Just mark this as TCP.
*/
col_set_str(pinfo->cinfo, COL_PROTOCOL, "TCP");
col_set_str(pinfo->cinfo, COL_INFO, "[TCP segment of a reassembled PDU]");
}
/*
* Show what's left in the packet as just raw TCP segment
* data.
* XXX - remember what protocol the last subdissector
* was, and report it as a continuation of that, instead?
*/
nbytes = MAX(0, tvb_reported_length_remaining(tvb, deseg_offset));
proto_tree_add_bytes_format(tcp_tree, hf_tcp_data, tvb, deseg_offset,
-1, NULL, "TCP segment data (%u byte%s)", nbytes,
plurality(nbytes, "", "s"));
}
pinfo->can_desegment = 0;
pinfo->desegment_offset = 0;
pinfo->desegment_len = 0;
if(another_pdu_follows) {
/* there was another pdu following this one. */
pinfo->can_desegment = 2;
/* we also have to prevent the dissector from changing the
* PROTOCOL and INFO columns since what follows may be an
* incomplete PDU and we don't want it be changed back from
* <Protocol> to <TCP>
* XXX There is no good way to block the PROTOCOL column
* from being changed yet so we set the entire row unwritable.
* The flag cleared_writable stores the initial state.
*/
col_set_fence(pinfo->cinfo, COL_INFO);
cleared_writable |= col_get_writable(pinfo->cinfo);
col_set_writable(pinfo->cinfo, FALSE);
offset += another_pdu_follows;
seq += another_pdu_follows;
goto again;
} else {
/* remove any blocking set above otherwise the
* proto,colinfo tap will break
*/
if(cleared_writable) {
col_set_writable(pinfo->cinfo, TRUE);
}
}
}
/*
* Loop for dissecting PDUs within a TCP stream; assumes that a PDU
* consists of a fixed-length chunk of data that contains enough information
* to determine the length of the PDU, followed by rest of the PDU.
*
* The first three arguments are the arguments passed to the dissector
* that calls this routine.
*
* "proto_desegment" is the dissector's flag controlling whether it should
* desegment PDUs that cross TCP segment boundaries.
*
* "fixed_len" is the length of the fixed-length part of the PDU.
*
* "get_pdu_len()" is a routine called to get the length of the PDU from
* the fixed-length part of the PDU; it's passed "pinfo", "tvb" and "offset".
*
* "dissect_pdu()" is the routine to dissect a PDU.
*/
void
tcp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
gboolean proto_desegment, guint fixed_len,
guint (*get_pdu_len)(packet_info *, tvbuff_t *, int),
dissector_t dissect_pdu)
{
volatile int offset = 0;
int offset_before;
guint length_remaining;
guint plen;
guint length;
tvbuff_t *next_tvb;
proto_item *item=NULL;
void *pd_save;
while (tvb_reported_length_remaining(tvb, offset) != 0) {
/*
* We use "tvb_ensure_length_remaining()" to make sure there actually
* *is* data remaining. The protocol we're handling could conceivably
* consists of a sequence of fixed-length PDUs, and therefore the
* "get_pdu_len" routine might not actually fetch anything from
* the tvbuff, and thus might not cause an exception to be thrown if
* we've run past the end of the tvbuff.
*
* This means we're guaranteed that "length_remaining" is positive.
*/
length_remaining = tvb_ensure_length_remaining(tvb, offset);
/*
* Can we do reassembly?
*/
if (proto_desegment && pinfo->can_desegment) {
/*
* Yes - is the fixed-length part of the PDU split across segment
* boundaries?
*/
if (length_remaining < fixed_len) {
/*
* Yes. Tell the TCP dissector where the data for this message
* starts in the data it handed us and that we need "some more
* data." Don't tell it exactly how many bytes we need because
* if/when we ask for even more (after the header) that will
* break reassembly.
*/
pinfo->desegment_offset = offset;
pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
return;
}
}
/*
* Get the length of the PDU.
*/
plen = (*get_pdu_len)(pinfo, tvb, offset);
if (plen < fixed_len) {
/*
* Either:
*
* 1) the length value extracted from the fixed-length portion
* doesn't include the fixed-length portion's length, and
* was so large that, when the fixed-length portion's
* length was added to it, the total length overflowed;
*
* 2) the length value extracted from the fixed-length portion
* includes the fixed-length portion's length, and the value
* was less than the fixed-length portion's length, i.e. it
* was bogus.
*
* Report this as a bounds error.
*/
show_reported_bounds_error(tvb, pinfo, tree);
return;
}
/*
* Do not display the the PDU length if it crosses the boundary of the
* packet and no more packets are available.
*
* XXX - we don't necessarily know whether more packets are
* available; we might be doing a one-pass read through the
* capture in TShark, or we might be doing a live capture in
* Wireshark.
*/
#if 0
if (length_remaining >= plen || there are more packets)
{
#endif
/*
* Display the PDU length as a field
*/
item=proto_tree_add_uint(pinfo->tcp_tree, hf_tcp_pdu_size,
tvb, offset, plen, plen);
PROTO_ITEM_SET_GENERATED(item);
#if 0
} else {
item = proto_tree_add_text(pinfo->tcp_tree, tvb, offset, -1,
"PDU Size: %u cut short at %u",plen,length_remaining);
PROTO_ITEM_SET_GENERATED(item);
}
#endif
/* give a hint to TCP where the next PDU starts
* so that it can attempt to find it in case it starts
* somewhere in the middle of a segment.
*/
if(!pinfo->fd->flags.visited && tcp_analyze_seq){
guint remaining_bytes;
remaining_bytes = MAX(0, tvb_reported_length_remaining(tvb, offset));
if(plen>remaining_bytes){
pinfo->want_pdu_tracking=2;
pinfo->bytes_until_next_pdu=plen-remaining_bytes;
}
}
/*
* Can we do reassembly?
*/
if (proto_desegment && pinfo->can_desegment) {
/*
* Yes - is the PDU split across segment boundaries?
*/
if (length_remaining < plen) {
/*
* Yes. Tell the TCP dissector where the data for this message
* starts in the data it handed us, and how many more bytes we
* need, and return.
*/
pinfo->desegment_offset = offset;
pinfo->desegment_len = plen - length_remaining;
return;
}
}
/*
* Construct a tvbuff containing the amount of the payload we have
* available. Make its reported length the amount of data in the PDU.
*
* XXX - if reassembly isn't enabled. the subdissector will throw a
* BoundsError exception, rather than a ReportedBoundsError exception.
* We really want a tvbuff where the length is "length", the reported
* length is "plen", and the "if the snapshot length were infinite"
* length is the minimum of the reported length of the tvbuff handed
* to us and "plen", with a new type of exception thrown if the offset
* is within the reported length but beyond that third length, with
* that exception getting the "Unreassembled Packet" error.
*/
length = length_remaining;
if (length > plen)
length = plen;
next_tvb = tvb_new_subset(tvb, offset, length, plen);
/*
* Dissect the PDU.
*
* Catch the ReportedBoundsError exception; if this particular message
* happens to get a ReportedBoundsError exception, that doesn't mean
* that we should stop dissecting PDUs within this frame or chunk of
* reassembled data.
*
* If it gets a BoundsError, we can stop, as there's nothing more to
* see, so we just re-throw it.
*/
pd_save = pinfo->private_data;
TRY {
(*dissect_pdu)(next_tvb, pinfo, tree);
}
CATCH(BoundsError) {
RETHROW;
}
CATCH(ReportedBoundsError) {
/* Restore the private_data structure in case one of the
* called dissectors modified it (and, due to the exception,
* was unable to restore it).
*/
pinfo->private_data = pd_save;
show_reported_bounds_error(tvb, pinfo, tree);
}
ENDTRY;
/*
* Step to the next PDU.
* Make sure we don't overflow.
*/
offset_before = offset;
offset += plen;
if (offset <= offset_before)
break;
}
}
static void
tcp_info_append_uint(packet_info *pinfo, const char *abbrev, guint32 val)
{
col_append_fstr(pinfo->cinfo, COL_INFO, " %s=%u", abbrev, val);
}
static void
dissect_tcpopt_exp(const ip_tcp_opt *optp _U_, tvbuff_t *tvb,
int offset, guint optlen, packet_info *pinfo, proto_tree *opt_tree)
{
proto_item *item;
proto_tree *exp_tree;
item = proto_tree_add_item(opt_tree, hf_tcp_option_exp, tvb,
offset, optlen, ENC_NA);
exp_tree = proto_item_add_subtree(item, ett_tcp_option_exp);
proto_tree_add_item(exp_tree, hf_tcp_option_kind, tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(exp_tree, hf_tcp_option_len, tvb, offset + 1, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(exp_tree, hf_tcp_option_exp_data, tvb,
offset + 2, optlen - 2, ENC_NA);
tcp_info_append_uint(pinfo, "Expxx", TRUE);
}
static void
dissect_tcpopt_sack_perm(const ip_tcp_opt *optp _U_, tvbuff_t *tvb,
int offset, guint optlen, packet_info *pinfo, proto_tree *opt_tree)
{
proto_item *item;
proto_tree *exp_tree;
item = proto_tree_add_boolean(opt_tree, hf_tcp_option_sack_perm, tvb, offset,
optlen, TRUE);
exp_tree = proto_item_add_subtree(item, ett_tcp_option_sack_perm);
proto_tree_add_item(exp_tree, hf_tcp_option_kind, tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(exp_tree, hf_tcp_option_len, tvb, offset + 1, 1, ENC_BIG_ENDIAN);
tcp_info_append_uint(pinfo, "SACK_PERM", TRUE);
}
static void
dissect_tcpopt_mss(const ip_tcp_opt *optp, tvbuff_t *tvb,
int offset, guint optlen, packet_info *pinfo, proto_tree *opt_tree)
{
proto_item *item;
proto_tree *exp_tree;
guint16 mss;
mss = tvb_get_ntohs(tvb, offset + 2);
item = proto_tree_add_none_format(opt_tree, hf_tcp_option_mss, tvb, offset,
optlen, "%s: %u bytes", optp->name, mss);
exp_tree = proto_item_add_subtree(item, ett_tcp_option_mss);
proto_tree_add_item(exp_tree, hf_tcp_option_kind, tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(exp_tree, hf_tcp_option_len, tvb, offset + 1, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(exp_tree, hf_tcp_option_mss_val, tvb, offset + 2, 2, ENC_BIG_ENDIAN);
tcp_info_append_uint(pinfo, "MSS", mss);
}
/* The window scale extension is defined in RFC 1323 */
static void
dissect_tcpopt_wscale(const ip_tcp_opt *optp _U_, tvbuff_t *tvb,
int offset, guint optlen _U_, packet_info *pinfo, proto_tree *opt_tree)
{
guint8 val, shift;
proto_item *wscale_pi, *gen_pi;
proto_tree *wscale_tree;
struct tcp_analysis *tcpd=NULL;
tcpd=get_tcp_conversation_data(NULL,pinfo);
wscale_pi = proto_tree_add_text(opt_tree, tvb, offset, 3, "Window scale: ");
wscale_tree = proto_item_add_subtree(wscale_pi, ett_tcp_option_wscale);
proto_tree_add_item(wscale_tree, hf_tcp_option_kind, tvb, offset, 1, ENC_NA);
offset += 1;
proto_tree_add_item(wscale_tree, hf_tcp_option_len, tvb, offset, 1, ENC_NA);
offset += 1;
proto_tree_add_item(wscale_tree, hf_tcp_option_wscale_shift, tvb, offset, 1,
ENC_NA);
shift = tvb_get_guint8(tvb, offset);
gen_pi = proto_tree_add_uint(wscale_tree, hf_tcp_option_wscale_multiplier, tvb,
offset, 1, 1 << shift);
PROTO_ITEM_SET_GENERATED(gen_pi);
val = tvb_get_guint8(tvb, offset);
proto_item_append_text(wscale_pi, "%u (multiply by %u)", val, 1 << shift);
tcp_info_append_uint(pinfo, "WS", 1 << shift);
if(!pinfo->fd->flags.visited){
pdu_store_window_scale_option(shift, tcpd);
}
}
static void
dissect_tcpopt_sack(const ip_tcp_opt *optp, tvbuff_t *tvb,
int offset, guint optlen, packet_info *pinfo, proto_tree *opt_tree)
{
proto_tree *field_tree = NULL;
proto_item *tf=NULL;
proto_item *hidden_item;
guint32 leftedge, rightedge;
struct tcp_analysis *tcpd=NULL;
guint32 base_ack=0;
if(tcp_analyze_seq && tcp_relative_seq){
/* find(or create if needed) the conversation for this tcp session */
tcpd=get_tcp_conversation_data(NULL,pinfo);
if (tcpd) {
base_ack=tcpd->rev->base_seq;
}
}
hidden_item = proto_tree_add_item(opt_tree, hf_tcp_option_kind, tvb,
offset, 1, ENC_BIG_ENDIAN);
PROTO_ITEM_SET_HIDDEN(hidden_item);
hidden_item = proto_tree_add_item(opt_tree, hf_tcp_option_len, tvb,
offset + 1, 1, ENC_BIG_ENDIAN);
PROTO_ITEM_SET_HIDDEN(hidden_item);
tf = proto_tree_add_text(opt_tree, tvb, offset, optlen, "%s:", optp->name);
offset += 2; /* skip past type and length */
optlen -= 2; /* subtract size of type and length */
while (optlen > 0) {
if (field_tree == NULL) {
/* Haven't yet made a subtree out of this option. Do so. */
field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
hidden_item = proto_tree_add_boolean(field_tree, hf_tcp_option_sack, tvb,
offset, optlen, TRUE);
PROTO_ITEM_SET_HIDDEN(hidden_item);
}
if (optlen < 4) {
proto_tree_add_text(field_tree, tvb, offset, optlen,
"(suboption would go past end of option)");
break;
}
leftedge = tvb_get_ntohl(tvb, offset)-base_ack;
proto_tree_add_uint_format(field_tree, hf_tcp_option_sack_sle, tvb,
offset, 4, leftedge,
"left edge = %u%s", leftedge,
tcp_relative_seq ? " (relative)" : "");
optlen -= 4;
if (optlen < 4) {
proto_tree_add_text(field_tree, tvb, offset, optlen,
"(suboption would go past end of option)");
break;
}
/* XXX - check whether it goes past end of packet */
rightedge = tvb_get_ntohl(tvb, offset + 4)-base_ack;
optlen -= 4;
proto_tree_add_uint_format(field_tree, hf_tcp_option_sack_sre, tvb,
offset+4, 4, rightedge,
"right edge = %u%s", rightedge,
tcp_relative_seq ? " (relative)" : "");
tcp_info_append_uint(pinfo, "SLE", leftedge);
tcp_info_append_uint(pinfo, "SRE", rightedge);
proto_item_append_text(field_tree, " %u-%u", leftedge, rightedge);
offset += 8;
}
}
static void
dissect_tcpopt_echo(const ip_tcp_opt *optp, tvbuff_t *tvb,
int offset, guint optlen, packet_info *pinfo, proto_tree *opt_tree)
{
proto_item *hidden_item;
guint32 echo;
hidden_item = proto_tree_add_item(opt_tree, hf_tcp_option_kind, tvb,
offset, 1, ENC_BIG_ENDIAN);
PROTO_ITEM_SET_HIDDEN(hidden_item);
hidden_item = proto_tree_add_item(opt_tree, hf_tcp_option_len, tvb,
offset + 1, 1, ENC_BIG_ENDIAN);
PROTO_ITEM_SET_HIDDEN(hidden_item);
echo = tvb_get_ntohl(tvb, offset + 2);
hidden_item = proto_tree_add_boolean(opt_tree, hf_tcp_option_echo, tvb, offset,
optlen, TRUE);
PROTO_ITEM_SET_HIDDEN(hidden_item);
proto_tree_add_text(opt_tree, tvb, offset, optlen,
"%s: %u", optp->name, echo);
tcp_info_append_uint(pinfo, "ECHO", echo);
}
/* If set, do not put the TCP timestamp information on the summary line */
static gboolean tcp_ignore_timestamps = FALSE;
static void
dissect_tcpopt_timestamp(const ip_tcp_opt *optp _U_, tvbuff_t *tvb,
int offset, guint optlen _U_, packet_info *pinfo, proto_tree *opt_tree)
{
proto_item *ti;
proto_tree *ts_tree;
guint32 ts_val, ts_ecr;
ti = proto_tree_add_text(opt_tree, tvb, offset, 10, "Timestamps: ");
ts_tree = proto_item_add_subtree(ti, ett_tcp_option_timestamp);
proto_tree_add_item(ts_tree, hf_tcp_option_kind, tvb, offset, 1, ENC_NA);
offset += 1;
proto_tree_add_item(ts_tree, hf_tcp_option_len, tvb, offset, 1, ENC_NA);
offset += 1;
proto_tree_add_item(ts_tree, hf_tcp_option_timestamp_tsval, tvb, offset,
4, ENC_BIG_ENDIAN);
ts_val = tvb_get_ntohl(tvb, offset);
offset += 4;
proto_tree_add_item(ts_tree, hf_tcp_option_timestamp_tsecr, tvb, offset,
4, ENC_BIG_ENDIAN);
ts_ecr = tvb_get_ntohl(tvb, offset);
/* offset += 4; */
proto_item_append_text(ti, "TSval %u, TSecr %u", ts_val, ts_ecr);
if (tcp_ignore_timestamps == FALSE) {
tcp_info_append_uint(pinfo, "TSval", ts_val);
tcp_info_append_uint(pinfo, "TSecr", ts_ecr);
}
}
/*
* The TCP Extensions for Multipath Operation with Multiple Addresses
* are defined in draft-ietf-mptcp-multiaddressed-04
*
* <http://tools.ietf.org/html/draft-ief-mptcp-multiaddressed-04>
*
* Author: Andrei Maruseac <andrei.maruseac@intel.com>
*/
static void
dissect_tcpopt_mptcp(const ip_tcp_opt *optp _U_, tvbuff_t *tvb,
int offset, guint optlen, packet_info *pinfo _U_, proto_tree *opt_tree)
{
proto_item *ti;
proto_tree *mptcp_tree;
proto_tree *mptcp_flags_tree;
guint8 subtype;
guint8 index;
guint8 flags;
guint8 ipver;
ti = proto_tree_add_text(opt_tree, tvb, offset, optlen, "Multipath TCP");
mptcp_tree = proto_item_add_subtree(ti, ett_tcp_option_mptcp);
proto_tree_add_item(mptcp_tree, hf_tcp_option_kind, tvb, offset, 1, ENC_BIG_ENDIAN);
offset += 1;
proto_tree_add_item(mptcp_tree, hf_tcp_option_len, tvb, offset, 1, ENC_BIG_ENDIAN);
offset += 1;
proto_tree_add_item(mptcp_tree, hf_tcp_option_mptcp_subtype, tvb,
offset, 1, ENC_BIG_ENDIAN);
subtype = tvb_get_guint8(tvb, offset) >> 4;
proto_item_append_text(ti, ": %s", val_to_str(subtype, mptcp_subtype_vs, "Unknown (%d)"));
switch (subtype) {
case TCPOPT_MPTCP_MP_CAPABLE:
proto_tree_add_item(mptcp_tree, hf_tcp_option_mptcp_version, tvb,
offset, 1, ENC_BIG_ENDIAN);
offset += 1;
flags = tvb_get_guint8(tvb, offset);
ti = proto_tree_add_uint(mptcp_tree, hf_tcp_option_mptcp_flags, tvb,
offset, 1, flags);
mptcp_flags_tree = proto_item_add_subtree(ti, ett_tcp_option_mptcp);
proto_tree_add_item(mptcp_flags_tree, hf_tcp_option_mptcp_C_flag,
tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(mptcp_flags_tree, hf_tcp_option_mptcp_S_flag,
tvb, offset, 1, ENC_BIG_ENDIAN);
offset += 1;
if (optlen == 12 || optlen == 20) {
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_sender_key, tvb, offset, 8, ENC_BIG_ENDIAN);
offset += 8;
}
if (optlen == 20) {
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_recv_key, tvb, offset, 8, ENC_BIG_ENDIAN);
}
break;
case TCPOPT_MPTCP_MP_JOIN:
switch (optlen) {
case 12:
flags = tvb_get_guint8(tvb, offset) & 0x01;
ti = proto_tree_add_uint(mptcp_tree,
hf_tcp_option_mptcp_flags, tvb,
offset, 1, flags);
mptcp_flags_tree = proto_item_add_subtree(ti,
ett_tcp_option_mptcp);
proto_tree_add_item(mptcp_flags_tree,
hf_tcp_option_mptcp_B_flag, tvb, offset,
1, ENC_BIG_ENDIAN);
offset += 1;
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_address_id, tvb, offset,
1, ENC_BIG_ENDIAN);
offset += 1;
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_recv_token, tvb, offset,
4, ENC_BIG_ENDIAN);
offset += 4;
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_sender_rand, tvb, offset,
4, ENC_BIG_ENDIAN);
break;
case 16:
flags = tvb_get_guint8(tvb, offset) & 0x01;
ti = proto_tree_add_uint(mptcp_tree,
hf_tcp_option_mptcp_flags, tvb,
offset, 1, flags);
mptcp_flags_tree = proto_item_add_subtree(ti,
ett_tcp_option_mptcp);
proto_tree_add_item(mptcp_flags_tree,
hf_tcp_option_mptcp_B_flag, tvb, offset,
1, ENC_BIG_ENDIAN);
offset += 1;
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_address_id, tvb, offset,
1, ENC_BIG_ENDIAN);
offset += 1;
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_sender_trunc_mac, tvb, offset,
8, ENC_BIG_ENDIAN);
offset += 8;
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_sender_rand, tvb, offset,
4, ENC_BIG_ENDIAN);
break;
case 24:
offset += 2;
for (index = 0; index < 5; index++) {
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_sender_mac, tvb, offset,
4, ENC_BIG_ENDIAN);
offset += 4;
}
break;
default:
break;
}
break;
case TCPOPT_MPTCP_DSS:
offset += 1;
flags = tvb_get_guint8(tvb, offset) & 0x1F;
ti = proto_tree_add_uint(mptcp_tree, hf_tcp_option_mptcp_flags, tvb,
offset, 1, flags);
mptcp_flags_tree = proto_item_add_subtree(ti, ett_tcp_option_mptcp);
proto_tree_add_item(mptcp_flags_tree, hf_tcp_option_mptcp_F_flag,
tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(mptcp_flags_tree, hf_tcp_option_mptcp_m_flag,
tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(mptcp_flags_tree, hf_tcp_option_mptcp_M_flag,
tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(mptcp_flags_tree, hf_tcp_option_mptcp_a_flag,
tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(mptcp_flags_tree, hf_tcp_option_mptcp_A_flag,
tvb, offset, 1, ENC_BIG_ENDIAN);
offset += 1;
if (flags & 1) {
if (flags & 2) {
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_data_ack, tvb, offset,
8, ENC_BIG_ENDIAN);
offset += 8;
} else {
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_data_ack, tvb, offset,
4, ENC_BIG_ENDIAN);
offset += 4;
}
}
if (flags & 4) {
if (flags & 8) {
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_data_seq_no, tvb, offset,
8, ENC_BIG_ENDIAN);
offset += 8;
} else {
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_data_seq_no, tvb, offset,
4, ENC_BIG_ENDIAN);
offset += 4;
}
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_subflow_seq_no, tvb, offset,
4, ENC_BIG_ENDIAN);
offset += 4;
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_data_lvl_len, tvb, offset,
2, ENC_BIG_ENDIAN);
offset += 2;
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_checksum, tvb, offset,
2, ENC_BIG_ENDIAN);
}
break;
case TCPOPT_MPTCP_ADD_ADDR:
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_ipver, tvb, offset, 1, ENC_BIG_ENDIAN);
ipver = tvb_get_guint8(tvb, offset) & 0x0F;
offset += 1;
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_address_id, tvb, offset, 1, ENC_BIG_ENDIAN);
offset += 1;
switch (ipver) {
case 4:
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_ipv4, tvb, offset, 4, ENC_BIG_ENDIAN);
offset += 4;
break;
case 6:
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_ipv6, tvb, offset, 16, ENC_NA);
offset += 16;
break;
default:
break;
}
if (optlen % 4 == 2) {
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_port, tvb, offset, 2, ENC_BIG_ENDIAN);
}
break;
case TCPOPT_MPTCP_REMOVE_ADDR:
offset += 1;
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_address_id, tvb, offset,
1, ENC_BIG_ENDIAN);
break;
case TCPOPT_MPTCP_MP_PRIO:
flags = tvb_get_guint8(tvb, offset) & 0x01;
ti = proto_tree_add_uint(mptcp_tree, hf_tcp_option_mptcp_flags, tvb,
offset, 1, flags);
mptcp_flags_tree = proto_item_add_subtree(ti, ett_tcp_option_mptcp);
proto_tree_add_item(mptcp_flags_tree, hf_tcp_option_mptcp_B_flag,
tvb, offset, 1, ENC_BIG_ENDIAN);
offset += 1;
if (optlen == 4) {
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_address_id, tvb, offset, 1, ENC_BIG_ENDIAN);
}
break;
case TCPOPT_MPTCP_MP_FAIL:
offset += 1;
offset += 1;
proto_tree_add_item(mptcp_tree,
hf_tcp_option_mptcp_data_seq_no, tvb, offset, 8, ENC_BIG_ENDIAN);
break;
default:
break;
}
}
static void
dissect_tcpopt_cc(const ip_tcp_opt *optp, tvbuff_t *tvb,
int offset, guint optlen, packet_info *pinfo, proto_tree *opt_tree)
{
proto_item *hidden_item;
guint32 cc;
hidden_item = proto_tree_add_item(opt_tree, hf_tcp_option_kind, tvb,
offset, 1, ENC_BIG_ENDIAN);
PROTO_ITEM_SET_HIDDEN(hidden_item);
hidden_item = proto_tree_add_item(opt_tree, hf_tcp_option_len, tvb,
offset + 1, 1, ENC_BIG_ENDIAN);
PROTO_ITEM_SET_HIDDEN(hidden_item);
cc = tvb_get_ntohl(tvb, offset + 2);
hidden_item = proto_tree_add_boolean(opt_tree, hf_tcp_option_cc, tvb, offset,
optlen, TRUE);
PROTO_ITEM_SET_HIDDEN(hidden_item);
proto_tree_add_text(opt_tree, tvb, offset, optlen,
"%s: %u", optp->name, cc);
tcp_info_append_uint(pinfo, "CC", cc);
}
static value_string_ext qs_rate_vals_ext = VALUE_STRING_EXT_INIT(qs_rate_vals);
static void
dissect_tcpopt_qs(const ip_tcp_opt *optp, tvbuff_t *tvb,
int offset, guint optlen, packet_info *pinfo, proto_tree *opt_tree)
{
proto_item *hidden_item;
guint8 rate = tvb_get_guint8(tvb, offset + 2) & 0x0f;
hidden_item = proto_tree_add_item(opt_tree, hf_tcp_option_kind, tvb,
offset, 1, ENC_BIG_ENDIAN);
PROTO_ITEM_SET_HIDDEN(hidden_item);
hidden_item = proto_tree_add_item(opt_tree, hf_tcp_option_len, tvb,
offset + 1, 1, ENC_BIG_ENDIAN);
PROTO_ITEM_SET_HIDDEN(hidden_item);
hidden_item = proto_tree_add_boolean(opt_tree, hf_tcp_option_qs, tvb, offset,
optlen, TRUE);
PROTO_ITEM_SET_HIDDEN(hidden_item);
proto_tree_add_text(opt_tree, tvb, offset, optlen,
"%s: Rate response, %s, TTL diff %u ", optp->name,
val_to_str_ext(rate, &qs_rate_vals_ext, "Unknown"),
tvb_get_guint8(tvb, offset + 3));
col_append_fstr(pinfo->cinfo, COL_INFO, " QSresp=%s", val_to_str_ext(rate, &qs_rate_vals_ext, "Unknown"));
}
static void
dissect_tcpopt_scps(const ip_tcp_opt *optp _U_, tvbuff_t *tvb,
int offset, guint optlen, packet_info *pinfo,
proto_tree *opt_tree)
{
struct tcp_analysis *tcpd;
proto_tree *field_tree = NULL;
tcp_flow_t *flow;
int direction;
proto_item *tf = NULL, *hidden_item;
guint8 capvector;
guint8 connid;
hidden_item = proto_tree_add_item(opt_tree, hf_tcp_option_kind, tvb,
offset, 1, ENC_BIG_ENDIAN);
PROTO_ITEM_SET_HIDDEN(hidden_item);
hidden_item = proto_tree_add_item(opt_tree, hf_tcp_option_len, tvb,
offset + 1, 1, ENC_BIG_ENDIAN);
PROTO_ITEM_SET_HIDDEN(hidden_item);
tcpd = get_tcp_conversation_data(NULL,pinfo);
/* check direction and get ua lists */
direction=CMP_ADDRESS(&pinfo->src, &pinfo->dst);
/* if the addresses are equal, match the ports instead */
if(direction==0) {
direction= (pinfo->srcport > pinfo->destport) ? 1 : -1;
}
if(direction>=0)
flow =&(tcpd->flow1);
else
flow =&(tcpd->flow2);
/* If the option length == 4, this is a real SCPS capability option
* See "CCSDS 714.0-B-2 (CCSDS Recommended Standard for SCPS Transport Protocol
* (SCPS-TP)" Section 3.2.3 for definition.
*/
if (optlen == 4) {
hidden_item = proto_tree_add_boolean(opt_tree, hf_tcp_option_scps,
tvb, offset, optlen, TRUE);
PROTO_ITEM_SET_HIDDEN(hidden_item);
capvector = tvb_get_guint8(tvb, offset + 2);
connid = tvb_get_guint8(tvb, offset + 3);
tf = proto_tree_add_item(opt_tree, hf_tcp_option_scps_vector, tvb,
offset + 2, 1, ENC_BIG_ENDIAN);
field_tree = proto_item_add_subtree(tf, ett_tcp_option_scps);
proto_tree_add_item(field_tree, hf_tcp_scpsoption_flags_bets, tvb,
offset + 2, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(field_tree, hf_tcp_scpsoption_flags_snack1, tvb,
offset + 2, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(field_tree, hf_tcp_scpsoption_flags_snack2, tvb,
offset + 2, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(field_tree, hf_tcp_scpsoption_flags_compress, tvb,
offset + 2, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(field_tree, hf_tcp_scpsoption_flags_nlts, tvb,
offset + 2, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(field_tree, hf_tcp_scpsoption_flags_reserved, tvb,
offset + 2, 1, ENC_BIG_ENDIAN);
if (capvector) {
struct capvec
{
guint8 mask;
const gchar *str;
} capvecs[] = {
{0x80, "BETS"},
{0x40, "SNACK1"},
{0x20, "SNACK2"},
{0x10, "COMP"},
{0x08, "NLTS"},
{0x07, "RESERVED"}
};
gboolean anyflag = FALSE;
guint i;
col_append_str(pinfo->cinfo, COL_INFO, " SCPS[");
for (i = 0; i < sizeof(capvecs)/sizeof(struct capvec); i++) {
if (capvector & capvecs[i].mask) {
proto_item_append_text(tf, "%s%s", anyflag ? ", " : " (",
capvecs[i].str);
col_append_fstr(pinfo->cinfo, COL_INFO, "%s%s",
anyflag ? ", " : "", capvecs[i].str);
anyflag = TRUE;
}
}
col_append_str(pinfo->cinfo, COL_INFO, "]");
proto_item_append_text(tf, ")");
}
proto_tree_add_item(field_tree, hf_tcp_scpsoption_connection_id, tvb,
offset + 3, 1, ENC_BIG_ENDIAN);
flow->scps_capable = 1;
if (connid)
tcp_info_append_uint(pinfo, "Connection ID", connid);
} else {
/* The option length != 4, so this is an infamous "extended capabilities
* option. See "CCSDS 714.0-B-2 (CCSDS Recommended Standard for SCPS
* Transport Protocol (SCPS-TP)" Section 3.2.5 for definition.
*
* As the format of this option is only partially defined (it is
* a community (or more likely vendor) defined format beyond that, so
* at least for now, we only parse the standardized portion of the option.
*/
guint8 local_offset = 2;
guint8 binding_space;
guint8 extended_cap_length;
if (flow->scps_capable != 1) {
/* There was no SCPS capabilities option preceding this */
proto_tree_add_uint_format(opt_tree, hf_tcp_option_scps_vector,
tvb, offset, optlen, 0,
"Illegal SCPS Extended Capabilities (%d bytes)",
optlen);
} else {
tf = proto_tree_add_uint_format(opt_tree, hf_tcp_option_scps_vector,
tvb, offset, optlen, 0,
"SCPS Extended Capabilities (%d bytes)",
optlen);
field_tree=proto_item_add_subtree(tf, ett_tcp_option_scps_extended);
/* There may be multiple binding spaces included in a single option,
* so we will semi-parse each of the stacked binding spaces - skipping
* over the octets following the binding space identifier and length.
*/
while (optlen > local_offset) {
/* 1st octet is Extended Capability Binding Space */
binding_space = tvb_get_guint8(tvb, (offset + local_offset));
/* 2nd octet (upper 4-bits) has binding space length in 16-bit words.
* As defined by the specification, this length is exclusive of the
* octets containing the extended capability type and length
*/
extended_cap_length =
(tvb_get_guint8(tvb, (offset + local_offset + 1)) >> 4);
/* Convert the extended capabilities length into bytes for display */
extended_cap_length = (extended_cap_length << 1);
proto_tree_add_item(field_tree, hf_tcp_option_scps_binding, tvb, offset + local_offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_uint(field_tree, hf_tcp_option_scps_binding_len, tvb, offset + local_offset + 1, 1, extended_cap_length);
/* Step past the binding space and length octets */
local_offset += 2;
proto_tree_add_text(field_tree, tvb, offset + local_offset,
extended_cap_length,
"Binding Space Data (%u bytes)",
extended_cap_length);
tcp_info_append_uint(pinfo, "EXCAP", binding_space);
/* Step past the Extended capability data
* Treat the extended capability data area as opaque;
* If one desires to parse the extended capability data
* (say, in a vendor aware build of wireshark), it would
* be triggered here.
*/
local_offset += extended_cap_length;
}
}
}
}
static void
dissect_tcpopt_user_to(const ip_tcp_opt *optp, tvbuff_t *tvb,
int offset, guint optlen, packet_info *pinfo, proto_tree *opt_tree)
{
proto_item *hidden_item, *tf;
proto_tree *field_tree;
gboolean g;
guint16 to;
hidden_item = proto_tree_add_item(opt_tree, hf_tcp_option_kind, tvb,
offset, 1, ENC_BIG_ENDIAN);
PROTO_ITEM_SET_HIDDEN(hidden_item);
hidden_item = proto_tree_add_item(opt_tree, hf_tcp_option_len, tvb,
offset + 1, 1, ENC_BIG_ENDIAN);
PROTO_ITEM_SET_HIDDEN(hidden_item);
g = tvb_get_ntohs(tvb, offset + 2) & 0x8000;
to = tvb_get_ntohs(tvb, offset + 2) & 0x7FFF;
hidden_item = proto_tree_add_boolean(opt_tree, hf_tcp_option_user_to, tvb, offset,
optlen, TRUE);
PROTO_ITEM_SET_HIDDEN(hidden_item);
tf = proto_tree_add_uint_format(opt_tree, hf_tcp_option_user_to_val, tvb, offset,
optlen, to, "%s: %u %s", optp->name, to, g ? "minutes" : "seconds");
field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
proto_tree_add_item(field_tree, hf_tcp_option_user_to_granularity, tvb, offset + 2, 2, ENC_BIG_ENDIAN);
proto_tree_add_item(field_tree, hf_tcp_option_user_to_val, tvb, offset + 2, 2, ENC_BIG_ENDIAN);
tcp_info_append_uint(pinfo, "USER_TO", to);
}
/* This is called for SYN+ACK packets and the purpose is to verify that
* the SCPS capabilities option has been successfully negotiated for the flow.
* If the SCPS capabilities option was offered by only one party, the
* proactively set scps_capable attribute of the flow (set upon seeing
* the first instance of the SCPS option) is revoked.
*/
static void
verify_scps(packet_info *pinfo, proto_item *tf_syn, struct tcp_analysis *tcpd)
{
tf_syn = 0x0;
if(tcpd) {
if ((!(tcpd->flow1.scps_capable)) || (!(tcpd->flow2.scps_capable))) {
tcpd->flow1.scps_capable = 0;
tcpd->flow2.scps_capable = 0;
} else {
expert_add_info_format(pinfo, tf_syn, PI_SEQUENCE, PI_NOTE,
"Connection establish request (SYN-ACK): SCPS Capabilities Negotiated");
}
}
}
/* See "CCSDS 714.0-B-2 (CCSDS Recommended Standard for SCPS
* Transport Protocol (SCPS-TP)" Section 3.5 for definition of the SNACK option
*/
static void
dissect_tcpopt_snack(const ip_tcp_opt *optp, tvbuff_t *tvb,
int offset, guint optlen, packet_info *pinfo,
proto_tree *opt_tree)
{
struct tcp_analysis *tcpd=NULL;
guint16 relative_hole_offset;
guint16 relative_hole_size;
guint16 base_mss = 0;
guint32 ack;
guint32 hole_start;
guint32 hole_end;
char null_modifier[] = "\0";
char relative_modifier[] = "(relative)";
char *modifier = null_modifier;
proto_item *hidden_item;
hidden_item = proto_tree_add_item(opt_tree, hf_tcp_option_kind, tvb,
offset, 1, ENC_BIG_ENDIAN);
PROTO_ITEM_SET_HIDDEN(hidden_item);
hidden_item = proto_tree_add_item(opt_tree, hf_tcp_option_len, tvb,
offset + 1, 1, ENC_BIG_ENDIAN);
PROTO_ITEM_SET_HIDDEN(hidden_item);
tcpd = get_tcp_conversation_data(NULL,pinfo);
/* The SNACK option reports missing data with a granularity of segments. */
relative_hole_offset = tvb_get_ntohs(tvb, offset + 2);
relative_hole_size = tvb_get_ntohs(tvb, offset + 4);
hidden_item = proto_tree_add_boolean(opt_tree, hf_tcp_option_snack, tvb,
offset, optlen, TRUE);
PROTO_ITEM_SET_HIDDEN(hidden_item);
hidden_item = proto_tree_add_uint(opt_tree, hf_tcp_option_snack_offset,
tvb, offset, optlen, relative_hole_offset);
PROTO_ITEM_SET_HIDDEN(hidden_item);
hidden_item = proto_tree_add_uint(opt_tree, hf_tcp_option_snack_size,
tvb, offset, optlen, relative_hole_size);
PROTO_ITEM_SET_HIDDEN(hidden_item);
proto_tree_add_text(opt_tree, tvb, offset, optlen,
"%s: Offset %u, Size %u", optp->name,
relative_hole_offset, relative_hole_size);
ack = tvb_get_ntohl(tvb, 8);
if (tcp_relative_seq) {
ack -= tcpd->rev->base_seq;
modifier = relative_modifier;
}
/* To aid analysis, we can use a simple but generally effective heuristic
* to report the most likely boundaries of the missing data. If the
* flow is scps_capable, we track the maximum sized segment that was
* acknowledged by the receiver and use that as the reporting granularity.
* This may be different from the negotiated MTU due to PMTUD or flows
* that do not send max-sized segments.
*/
base_mss = tcpd->fwd->maxsizeacked;
if (base_mss) {
/* Scale the reported offset and hole size by the largest segment acked */
hole_start = ack + (base_mss * relative_hole_offset);
hole_end = hole_start + (base_mss * relative_hole_size);
hidden_item = proto_tree_add_uint(opt_tree, hf_tcp_option_snack_le,
tvb, offset, optlen, hole_start);
PROTO_ITEM_SET_HIDDEN(hidden_item);
hidden_item = proto_tree_add_uint(opt_tree, hf_tcp_option_snack_re,
tvb, offset, optlen, hole_end);
PROTO_ITEM_SET_HIDDEN(hidden_item);
proto_tree_add_text(opt_tree, tvb, offset, optlen,
"\tMissing Sequence %u - %u %s",
hole_start, hole_end, modifier);
tcp_info_append_uint(pinfo, "SNLE", hole_start);
tcp_info_append_uint(pinfo, "SNRE", hole_end);
expert_add_info_format(pinfo, NULL, PI_SEQUENCE, PI_NOTE,
"SNACK Sequence %u - %u %s",
hole_start, hole_end, modifier);
}
}
enum
{
PROBE_VERSION_UNSPEC = 0,
PROBE_VERSION_1 = 1,
PROBE_VERSION_2 = 2,
PROBE_VERSION_MAX
};
/* Probe type definition. */
enum
{
PROBE_QUERY = 0,
PROBE_RESPONSE = 1,
PROBE_INTERNAL = 2,
PROBE_TRACE = 3,
PROBE_QUERY_SH = 4,
PROBE_RESPONSE_SH = 5,
PROBE_QUERY_INFO = 6,
PROBE_RESPONSE_INFO = 7,
PROBE_QUERY_INFO_SH = 8,
PROBE_QUERY_INFO_SID = 9,
PROBE_RST = 10,
PROBE_TYPE_MAX
};
static const value_string rvbd_probe_type_vs[] = {
{ PROBE_QUERY, "Probe Query" },
{ PROBE_RESPONSE, "Probe Response" },
{ PROBE_INTERNAL, "Probe Internal" },
{ PROBE_TRACE, "Probe Trace" },
{ PROBE_QUERY_SH, "Probe Query SH" },
{ PROBE_RESPONSE_SH, "Probe Response SH" },
{ PROBE_QUERY_INFO, "Probe Query Info" },
{ PROBE_RESPONSE_INFO, "Probe Response Info" },
{ PROBE_QUERY_INFO_SH, "Probe Query Info SH" },
{ PROBE_QUERY_INFO_SID, "Probe Query Info Store ID" },
{ PROBE_RST, "Probe Reset" },
{ 0, NULL }
};
#define PROBE_OPTLEN_OFFSET 1
#define PROBE_VERSION_TYPE_OFFSET 2
#define PROBE_V1_RESERVED_OFFSET 3
#define PROBE_V1_PROBER_OFFSET 4
#define PROBE_V1_APPLI_VERSION_OFFSET 8
#define PROBE_V1_PROXY_ADDR_OFFSET 8
#define PROBE_V1_PROXY_PORT_OFFSET 12
#define PROBE_V1_SH_CLIENT_ADDR_OFFSET 8
#define PROBE_V1_SH_PROXY_ADDR_OFFSET 12
#define PROBE_V1_SH_PROXY_PORT_OFFSET 16
#define PROBE_V2_INFO_OFFSET 3
#define PROBE_V2_INFO_CLIENT_ADDR_OFFSET 4
#define PROBE_V2_INFO_STOREID_OFFSET 4
#define PROBE_VERSION_MASK 0x01
/* Probe Query Extra Info flags */
#define RVBD_FLAGS_PROBE_LAST 0x01
#define RVBD_FLAGS_PROBE_NCFE 0x04
/* Probe Response Extra Info flags */
#define RVBD_FLAGS_PROBE_SERVER 0x01
#define RVBD_FLAGS_PROBE_SSLCERT 0x02
#define RVBD_FLAGS_PROBE 0x10
static void
rvbd_probe_decode_version_type(const guint8 vt, guint8 *ver, guint8 *type)
{
if (vt & PROBE_VERSION_MASK) {
*ver = PROBE_VERSION_1;
*type = vt >> 4;
} else {
*ver = PROBE_VERSION_2;
*type = vt >> 1;
}
}
static void
rvbd_probe_resp_add_info(proto_item *pitem, packet_info *pinfo, guint32 ip, guint16 port)
{
proto_item_append_text(pitem, ", Server Steelhead: %s:%u", ip_to_str((guint8 *)&ip), port);
col_prepend_fstr(pinfo->cinfo, COL_INFO, "SA+, ");
}
static void
dissect_tcpopt_rvbd_probe(const ip_tcp_opt *optp _U_, tvbuff_t *tvb, int offset,
guint optlen, packet_info *pinfo, proto_tree *opt_tree)
{
guint8 ver, type;
proto_tree *field_tree;
proto_item *pitem;
rvbd_probe_decode_version_type(
tvb_get_guint8(tvb, offset + PROBE_VERSION_TYPE_OFFSET),
&ver, &type);
pitem = proto_tree_add_boolean_format_value(
opt_tree, hf_tcp_option_rvbd_probe, tvb, offset, optlen, 1,
"%s", val_to_str(type, rvbd_probe_type_vs, "Probe Unknown"));
if (type >= PROBE_TYPE_MAX)
return;
/* optlen, type, ver are common for all probes */
field_tree = proto_item_add_subtree(pitem, ett_tcp_opt_rvbd_probe);
pitem = proto_tree_add_item(field_tree, hf_tcp_option_len, tvb,
offset + PROBE_OPTLEN_OFFSET, 1, ENC_BIG_ENDIAN);
PROTO_ITEM_SET_HIDDEN(pitem);
pitem = proto_tree_add_item(field_tree, hf_tcp_option_kind, tvb,
offset, 1, ENC_BIG_ENDIAN);
PROTO_ITEM_SET_HIDDEN(pitem);
proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_optlen, tvb,
offset + PROBE_OPTLEN_OFFSET, 1, ENC_BIG_ENDIAN);
if (ver == PROBE_VERSION_1) {
guint32 ip;
guint16 port;
proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_type1, tvb,
offset + PROBE_VERSION_TYPE_OFFSET, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_version1, tvb,
offset + PROBE_VERSION_TYPE_OFFSET, 1, ENC_BIG_ENDIAN);
if (type == PROBE_INTERNAL)
return;
proto_tree_add_text(field_tree, tvb, offset + PROBE_V1_RESERVED_OFFSET,
1, "Reserved");
ip = tvb_get_ipv4(tvb, offset + PROBE_V1_PROBER_OFFSET);
proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_prober, tvb,
offset + PROBE_V1_PROBER_OFFSET, 4, ENC_BIG_ENDIAN);
switch (type) {
case PROBE_QUERY:
case PROBE_QUERY_SH:
case PROBE_TRACE:
proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_appli_ver, tvb,
offset + PROBE_V1_APPLI_VERSION_OFFSET, 2,
ENC_BIG_ENDIAN);
proto_item_append_text(pitem, ", CSH IP: %s", ip_to_str((guint8 *)&ip));
if (check_col(pinfo->cinfo, COL_INFO)) {
/* Small look-ahead hack to distinguish S+ from S+* */
#define PROBE_V1_QUERY_LEN 10
const guint8 qinfo_hdr[] = { 0x4c, 0x04, 0x0c };
int not_cfe = 0;
/* tvb_memeql seems to be the only API that doesn't throw
an exception in case of an error */
if (tvb_memeql(tvb, offset + PROBE_V1_QUERY_LEN,
qinfo_hdr, sizeof(qinfo_hdr)) == 0) {
not_cfe = tvb_get_guint8(tvb, offset + PROBE_V1_QUERY_LEN +
sizeof(qinfo_hdr)) & RVBD_FLAGS_PROBE_NCFE;
}
col_prepend_fstr(pinfo->cinfo, COL_INFO, "S%s, ",
type == PROBE_TRACE ? "#" :
not_cfe ? "+*" : "+");
}
break;
case PROBE_RESPONSE:
ip = tvb_get_ipv4(tvb, offset + PROBE_V1_PROXY_ADDR_OFFSET);
proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_proxy, tvb,
offset + PROBE_V1_PROXY_ADDR_OFFSET, 4, ENC_BIG_ENDIAN);
port = tvb_get_ntohs(tvb, offset + PROBE_V1_PROXY_PORT_OFFSET);
proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_proxy_port, tvb,
offset + PROBE_V1_PROXY_PORT_OFFSET, 2, ENC_BIG_ENDIAN);
rvbd_probe_resp_add_info(pitem, pinfo, ip, port);
break;
case PROBE_RESPONSE_SH:
proto_tree_add_item(field_tree,
hf_tcp_option_rvbd_probe_client, tvb,
offset + PROBE_V1_SH_CLIENT_ADDR_OFFSET, 4,
ENC_BIG_ENDIAN);
ip = tvb_get_ipv4(tvb, offset + PROBE_V1_SH_PROXY_ADDR_OFFSET);
proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_proxy, tvb,
offset + PROBE_V1_SH_PROXY_ADDR_OFFSET, 4, ENC_BIG_ENDIAN);
port = tvb_get_ntohs(tvb, offset + PROBE_V1_SH_PROXY_PORT_OFFSET);
proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_proxy_port, tvb,
offset + PROBE_V1_SH_PROXY_PORT_OFFSET, 2, ENC_BIG_ENDIAN);
rvbd_probe_resp_add_info(pitem, pinfo, ip, port);
break;
}
}
else if (ver == PROBE_VERSION_2) {
proto_item *ver_pi;
proto_item *flag_pi;
proto_tree *flag_tree;
guint8 flags;
proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_type2, tvb,
offset + PROBE_VERSION_TYPE_OFFSET, 1, ENC_BIG_ENDIAN);
proto_tree_add_uint_format_value(
field_tree, hf_tcp_option_rvbd_probe_version2, tvb,
offset + PROBE_VERSION_TYPE_OFFSET, 1, ver, "%u", ver);
/* Use version1 for filtering purposes because version2 packet
value is 0, but filtering is usually done for value 2 */
ver_pi = proto_tree_add_uint(field_tree, hf_tcp_option_rvbd_probe_version1, tvb,
offset + PROBE_VERSION_TYPE_OFFSET, 1, ver);
PROTO_ITEM_SET_HIDDEN(ver_pi);
switch (type) {
case PROBE_QUERY_INFO:
case PROBE_QUERY_INFO_SH:
case PROBE_QUERY_INFO_SID:
flags = tvb_get_guint8(tvb, offset + PROBE_V2_INFO_OFFSET);
flag_pi = proto_tree_add_uint(field_tree, hf_tcp_option_rvbd_probe_flags,
tvb, offset + PROBE_V2_INFO_OFFSET,
1, flags);
flag_tree = proto_item_add_subtree(flag_pi, ett_tcp_opt_rvbd_probe_flags);
proto_tree_add_item(flag_tree,
hf_tcp_option_rvbd_probe_flag_not_cfe,
tvb, offset + PROBE_V2_INFO_OFFSET, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(flag_tree,
hf_tcp_option_rvbd_probe_flag_last_notify,
tvb, offset + PROBE_V2_INFO_OFFSET, 1, ENC_BIG_ENDIAN);
if (type == PROBE_QUERY_INFO_SH)
proto_tree_add_item(flag_tree,
hf_tcp_option_rvbd_probe_client, tvb,
offset + PROBE_V2_INFO_CLIENT_ADDR_OFFSET,
4, ENC_BIG_ENDIAN);
else if (type == PROBE_QUERY_INFO_SID)
proto_tree_add_item(flag_tree,
hf_tcp_option_rvbd_probe_storeid, tvb,
offset + PROBE_V2_INFO_STOREID_OFFSET,
4, ENC_BIG_ENDIAN);
if (type != PROBE_QUERY_INFO_SID &&
check_col(pinfo->cinfo, COL_INFO) &&
(tvb_get_guint8(tvb, 13) & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK) &&
(flags & RVBD_FLAGS_PROBE_LAST)) {
col_prepend_fstr(pinfo->cinfo, COL_INFO, "SA++, ");
}
break;
case PROBE_RESPONSE_INFO:
flag_pi = proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_flags,
tvb, offset + PROBE_V2_INFO_OFFSET,
1, ENC_BIG_ENDIAN);
flag_tree = proto_item_add_subtree(flag_pi, ett_tcp_opt_rvbd_probe_flags);
proto_tree_add_item(flag_tree,
hf_tcp_option_rvbd_probe_flag_probe_cache,
tvb, offset + PROBE_V2_INFO_OFFSET, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(flag_tree,
hf_tcp_option_rvbd_probe_flag_sslcert,
tvb, offset + PROBE_V2_INFO_OFFSET, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(flag_tree,
hf_tcp_option_rvbd_probe_flag_server_connected,
tvb, offset + PROBE_V2_INFO_OFFSET, 1, ENC_BIG_ENDIAN);
break;
case PROBE_RST:
proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_flags,
tvb, offset + PROBE_V2_INFO_OFFSET,
1, ENC_BIG_ENDIAN);
break;
}
}
}
enum {
TRPY_OPTNUM_OFFSET = 0,
TRPY_OPTLEN_OFFSET = 1,
TRPY_OPTIONS_OFFSET = 2,
TRPY_SRC_ADDR_OFFSET = 4,
TRPY_DST_ADDR_OFFSET = 8,
TRPY_SRC_PORT_OFFSET = 12,
TRPY_DST_PORT_OFFSET = 14,
TRPY_CLIENT_PORT_OFFSET = 16
};
/* Trpy Flags */
#define RVBD_FLAGS_TRPY_MODE 0x0001
#define RVBD_FLAGS_TRPY_OOB 0x0002
#define RVBD_FLAGS_TRPY_CHKSUM 0x0004
#define RVBD_FLAGS_TRPY_FW_RST 0x0100
#define RVBD_FLAGS_TRPY_FW_RST_INNER 0x0200
#define RVBD_FLAGS_TRPY_FW_RST_PROBE 0x0400
static const true_false_string trpy_mode_str = {
"Port Transparency",
"Full Transparency"
};
static void
dissect_tcpopt_rvbd_trpy(const ip_tcp_opt *optp _U_, tvbuff_t *tvb,
int offset, guint optlen, packet_info *pinfo,
proto_tree *opt_tree)
{
proto_tree *field_tree;
proto_tree *flag_tree;
proto_item *pitem;
proto_item *flag_pi;
guint32 src, dst;
guint16 sport, dport, flags;
static dissector_handle_t sport_handle = NULL;
col_prepend_fstr(pinfo->cinfo, COL_INFO, "TRPY, ");
pitem = proto_tree_add_boolean_format_value(
opt_tree, hf_tcp_option_rvbd_trpy, tvb, offset, optlen, 1,
"%s", "");
field_tree = proto_item_add_subtree(pitem, ett_tcp_opt_rvbd_trpy);
pitem = proto_tree_add_item(field_tree, hf_tcp_option_len, tvb,
offset + PROBE_OPTLEN_OFFSET, 1, ENC_BIG_ENDIAN);
PROTO_ITEM_SET_HIDDEN(pitem);
pitem = proto_tree_add_item(field_tree, hf_tcp_option_kind, tvb,
offset, 1, ENC_BIG_ENDIAN);
PROTO_ITEM_SET_HIDDEN(pitem);
proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_optlen, tvb,
offset + PROBE_OPTLEN_OFFSET, 1, ENC_BIG_ENDIAN);
flags = tvb_get_ntohs(tvb, offset + TRPY_OPTIONS_OFFSET);
flag_pi = proto_tree_add_item(field_tree, hf_tcp_option_rvbd_trpy_flags,
tvb, offset + TRPY_OPTIONS_OFFSET,
2, ENC_BIG_ENDIAN);
flag_tree = proto_item_add_subtree(flag_pi, ett_tcp_opt_rvbd_trpy_flags);
proto_tree_add_item(flag_tree, hf_tcp_option_rvbd_trpy_flag_fw_rst_probe,
tvb, offset + TRPY_OPTIONS_OFFSET, 2, ENC_BIG_ENDIAN);
proto_tree_add_item(flag_tree, hf_tcp_option_rvbd_trpy_flag_fw_rst_inner,
tvb, offset + TRPY_OPTIONS_OFFSET, 2, ENC_BIG_ENDIAN);
proto_tree_add_item(flag_tree, hf_tcp_option_rvbd_trpy_flag_fw_rst,
tvb, offset + TRPY_OPTIONS_OFFSET, 2, ENC_BIG_ENDIAN);
proto_tree_add_item(flag_tree, hf_tcp_option_rvbd_trpy_flag_chksum,
tvb, offset + TRPY_OPTIONS_OFFSET, 2, ENC_BIG_ENDIAN);
proto_tree_add_item(flag_tree, hf_tcp_option_rvbd_trpy_flag_oob,
tvb, offset + TRPY_OPTIONS_OFFSET, 2, ENC_BIG_ENDIAN);
proto_tree_add_item(flag_tree, hf_tcp_option_rvbd_trpy_flag_mode,
tvb, offset + TRPY_OPTIONS_OFFSET, 2, ENC_BIG_ENDIAN);
src = tvb_get_ipv4(tvb, offset + TRPY_SRC_ADDR_OFFSET);
proto_tree_add_item(field_tree, hf_tcp_option_rvbd_trpy_src,
tvb, offset + TRPY_SRC_ADDR_OFFSET, 4, ENC_BIG_ENDIAN);
dst = tvb_get_ipv4(tvb, offset + TRPY_DST_ADDR_OFFSET);
proto_tree_add_item(field_tree, hf_tcp_option_rvbd_trpy_dst,
tvb, offset + TRPY_DST_ADDR_OFFSET, 4, ENC_BIG_ENDIAN);
sport = tvb_get_ntohs(tvb, offset + TRPY_SRC_PORT_OFFSET);
proto_tree_add_item(field_tree, hf_tcp_option_rvbd_trpy_src_port,
tvb, offset + TRPY_SRC_PORT_OFFSET, 2, ENC_BIG_ENDIAN);
dport = tvb_get_ntohs(tvb, offset + TRPY_DST_PORT_OFFSET);
proto_tree_add_item(field_tree, hf_tcp_option_rvbd_trpy_dst_port,
tvb, offset + TRPY_DST_PORT_OFFSET, 2, ENC_BIG_ENDIAN);
proto_item_append_text(pitem, "%s:%u -> %s:%u",
ip_to_str((guint8 *)&src), sport,
ip_to_str((guint8 *)&dst), dport);
/* Client port only set on SYN: optlen == 18 */
if ((flags & RVBD_FLAGS_TRPY_OOB) && (optlen > TCPOLEN_RVBD_TRPY_MIN))
proto_tree_add_item(field_tree, hf_tcp_option_rvbd_trpy_client_port,
tvb, offset + TRPY_CLIENT_PORT_OFFSET, 2, ENC_BIG_ENDIAN);
/* We need to map this TCP session on our own dissector instead of what
Wireshark thinks runs on these ports */
if (sport_handle == NULL) {
sport_handle = find_dissector("sport");
}
if (sport_handle != NULL) {
conversation_t *conversation;
conversation = find_conversation(pinfo->fd->num,
&pinfo->src, &pinfo->dst, pinfo->ptype,
pinfo->srcport, pinfo->destport, 0);
if (conversation == NULL) {
conversation = conversation_new(pinfo->fd->num,
&pinfo->src, &pinfo->dst, pinfo->ptype,
pinfo->srcport, pinfo->destport, 0);
}
if (conversation->dissector_handle != sport_handle) {
conversation_set_dissector(conversation, sport_handle);
}
}
}
static const ip_tcp_opt tcpopts[] = {
{
TCPOPT_EOL,
"End of Option List (EOL)",
NULL,
NO_LENGTH,
0,
NULL,
},
{
TCPOPT_NOP,
"No-Operation (NOP)",
NULL,
NO_LENGTH,
0,
NULL,
},
{
TCPOPT_MSS,
"Maximum segment size",
NULL,
FIXED_LENGTH,
TCPOLEN_MSS,
dissect_tcpopt_mss
},
{
TCPOPT_WINDOW,
"Window scale",
NULL,
FIXED_LENGTH,
TCPOLEN_WINDOW,
dissect_tcpopt_wscale
},
{
TCPOPT_SACK_PERM,
"SACK permitted",
NULL,
FIXED_LENGTH,
TCPOLEN_SACK_PERM,
dissect_tcpopt_sack_perm,
},
{
TCPOPT_SACK,
"SACK",
&ett_tcp_option_sack,
VARIABLE_LENGTH,
TCPOLEN_SACK_MIN,
dissect_tcpopt_sack
},
{
TCPOPT_ECHO,
"Echo",
NULL,
FIXED_LENGTH,
TCPOLEN_ECHO,
dissect_tcpopt_echo
},
{
TCPOPT_ECHOREPLY,
"Echo reply",
NULL,
FIXED_LENGTH,
TCPOLEN_ECHOREPLY,
dissect_tcpopt_echo
},
{
TCPOPT_TIMESTAMP,
"Timestamps",
NULL,
FIXED_LENGTH,
TCPOLEN_TIMESTAMP,
dissect_tcpopt_timestamp
},
{
TCPOPT_MPTCP,
"Multipath TCP",
NULL,
VARIABLE_LENGTH,
TCPOLEN_MPTCP_MIN,
dissect_tcpopt_mptcp
},
{
TCPOPT_CC,
"CC",
NULL,
FIXED_LENGTH,
TCPOLEN_CC,
dissect_tcpopt_cc
},
{
TCPOPT_CCNEW,
"CC.NEW",
NULL,
FIXED_LENGTH,
TCPOLEN_CCNEW,
dissect_tcpopt_cc
},
{
TCPOPT_CCECHO,
"CC.ECHO",
NULL,
FIXED_LENGTH,
TCPOLEN_CCECHO,
dissect_tcpopt_cc
},
{
TCPOPT_MD5,
"TCP MD5 signature",
NULL,
FIXED_LENGTH,
TCPOLEN_MD5,
NULL
},
{
TCPOPT_SCPS,
"SCPS capabilities",
&ett_tcp_option_scps,
VARIABLE_LENGTH,
TCPOLEN_SCPS,
dissect_tcpopt_scps
},
{
TCPOPT_SNACK,
"Selective Negative Acknowledgment",
NULL,
FIXED_LENGTH,
TCPOLEN_SNACK,
dissect_tcpopt_snack
},
{
TCPOPT_RECBOUND,
"SCPS record boundary",
NULL,
FIXED_LENGTH,
TCPOLEN_RECBOUND,
NULL
},
{
TCPOPT_CORREXP,
"SCPS corruption experienced",
NULL,
FIXED_LENGTH,
TCPOLEN_CORREXP,
NULL
},
{
TCPOPT_QS,
"Quick-Start",
NULL,
FIXED_LENGTH,
TCPOLEN_QS,
dissect_tcpopt_qs
},
{
TCPOPT_USER_TO,
"User Timeout",
&ett_tcp_option_user_to,
FIXED_LENGTH,
TCPOLEN_USER_TO,
dissect_tcpopt_user_to
},
{
TCPOPT_RVBD_PROBE,
"Riverbed Probe",
NULL,
VARIABLE_LENGTH,
TCPOLEN_RVBD_PROBE_MIN,
dissect_tcpopt_rvbd_probe
},
{
TCPOPT_RVBD_TRPY,
"Riverbed Transparency",
NULL,
FIXED_LENGTH,
TCPOLEN_RVBD_TRPY_MIN,
dissect_tcpopt_rvbd_trpy
},
{
TCPOPT_EXP_FD,
"Experimental",
NULL,
VARIABLE_LENGTH,
TCPOLEN_EXP_MIN,
dissect_tcpopt_exp
},
{
TCPOPT_EXP_FE,
"Experimental",
NULL,
VARIABLE_LENGTH,
TCPOLEN_EXP_MIN,
dissect_tcpopt_exp
}
};
#define N_TCP_OPTS array_length(tcpopts)
/* Determine if there is a sub-dissector and call it; return TRUE
if there was a sub-dissector, FALSE otherwise.
This has been separated into a stand alone routine to other protocol
dissectors can call to it, e.g., SOCKS. */
static gboolean try_heuristic_first = FALSE;
/* this function can be called with tcpd==NULL as from the msproxy dissector */
gboolean
decode_tcp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
proto_tree *tree, int src_port, int dst_port,
struct tcp_analysis *tcpd)
{
tvbuff_t *next_tvb;
int low_port, high_port;
int save_desegment_offset;
guint32 save_desegment_len;
/* Don't call subdissectors for keepalives. Even though they do contain
* payload "data", it's just garbage. Display any data the keepalive
* packet might contain though.
*/
if(tcpd && tcpd->ta){
if(tcpd->ta->flags&TCP_A_KEEP_ALIVE){
next_tvb = tvb_new_subset_remaining(tvb, offset);
call_dissector(data_handle, next_tvb, pinfo, tree);
return TRUE;
}
}
next_tvb = tvb_new_subset_remaining(tvb, offset);
if (tcp_no_subdissector_on_error && tcpd && tcpd->ta && tcpd->ta->flags & (TCP_A_RETRANSMISSION | TCP_A_OUT_OF_ORDER)) {
/* Don't try to dissect a retransmission high chance that it will mess
* subdissectors for protocols that require in-order delivery of the
* PDUs. (i.e. DCE/RPCoverHTTP and encryption)
*/
return FALSE;
}
/* determine if this packet is part of a conversation and call dissector */
/* for the conversation if available */
if (try_conversation_dissector(&pinfo->src, &pinfo->dst, PT_TCP,
src_port, dst_port, next_tvb, pinfo, tree)){
pinfo->want_pdu_tracking -= !!(pinfo->want_pdu_tracking);
return TRUE;
}
if (try_heuristic_first) {
/* do lookup with the heuristic subdissector table */
save_desegment_offset = pinfo->desegment_offset;
save_desegment_len = pinfo->desegment_len;
if (dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, tree)){
pinfo->want_pdu_tracking -= !!(pinfo->want_pdu_tracking);
return TRUE;
}
/*
* They rejected the packet; make sure they didn't also request
* desegmentation (we could just override the request, but
* rejecting a packet *and* requesting desegmentation is a sign
* of the dissector's code needing clearer thought, so we fail
* so that the problem is made more obvious).
*/
DISSECTOR_ASSERT(save_desegment_offset == pinfo->desegment_offset &&
save_desegment_len == pinfo->desegment_len);
}
/* Do lookups with the subdissector table.
Try the server port captured on the SYN or SYN|ACK packet. After that
try the port number with the lower value first, followed by the
port number with the higher value. This means that, for packets
where a dissector is registered for *both* port numbers:
1) we pick the same dissector for traffic going in both directions;
2) we prefer the port number that's more likely to be the right
one (as that prefers well-known ports to reserved ports);
although there is, of course, no guarantee that any such strategy
will always pick the right port number.
XXX - we ignore port numbers of 0, as some dissectors use a port
number of 0 to disable the port. */
if (tcpd && tcpd->server_port != 0 &&
dissector_try_uint(subdissector_table, tcpd->server_port, next_tvb, pinfo, tree)){
pinfo->want_pdu_tracking -= !!(pinfo->want_pdu_tracking);
return TRUE;
}
if (src_port > dst_port) {
low_port = dst_port;
high_port = src_port;
} else {
low_port = src_port;
high_port = dst_port;
}
if (tcp_no_subdissector_on_error && tcpd && tcpd->ta && tcpd->ta->flags & (TCP_A_RETRANSMISSION | TCP_A_OUT_OF_ORDER)) {
/* Don't try to dissect a retransmission high chance that it will mess
* subdissectors for protocols that require in-order delivery of the
* PDUs. (i.e. DCE/RPCoverHTTP and encryption)
*/
return FALSE;
}
if (low_port != 0 &&
dissector_try_uint(subdissector_table, low_port, next_tvb, pinfo, tree)){
pinfo->want_pdu_tracking -= !!(pinfo->want_pdu_tracking);
return TRUE;
}
if (high_port != 0 &&
dissector_try_uint(subdissector_table, high_port, next_tvb, pinfo, tree)){
pinfo->want_pdu_tracking -= !!(pinfo->want_pdu_tracking);
return TRUE;
}
if (!try_heuristic_first) {
/* do lookup with the heuristic subdissector table */
save_desegment_offset = pinfo->desegment_offset;
save_desegment_len = pinfo->desegment_len;
if (dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, tree)){
pinfo->want_pdu_tracking -= !!(pinfo->want_pdu_tracking);
return TRUE;
}
/*
* They rejected the packet; make sure they didn't also request
* desegmentation (we could just override the request, but
* rejecting a packet *and* requesting desegmentation is a sign
* of the dissector's code needing clearer thought, so we fail
* so that the problem is made more obvious).
*/
DISSECTOR_ASSERT(save_desegment_offset == pinfo->desegment_offset &&
save_desegment_len == pinfo->desegment_len);
}
/* Oh, well, we don't know this; dissect it as data. */
call_dissector(data_handle,next_tvb, pinfo, tree);
pinfo->want_pdu_tracking -= !!(pinfo->want_pdu_tracking);
return FALSE;
}
static void
process_tcp_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
proto_tree *tree, proto_tree *tcp_tree, int src_port, int dst_port,
guint32 seq, guint32 nxtseq, gboolean is_tcp_segment,
struct tcp_analysis *tcpd)
{
pinfo->want_pdu_tracking=0;
TRY {
if(is_tcp_segment){
/*qqq see if it is an unaligned PDU */
if(tcpd && tcp_analyze_seq && (!tcp_desegment)){
if(seq || nxtseq){
offset=scan_for_next_pdu(tvb, tcp_tree, pinfo, offset,
seq, nxtseq, tcpd->fwd->multisegment_pdus);
}
}
}
/* if offset is -1 this means that this segment is known
* to be fully inside a previously detected pdu
* so we don't even need to try to dissect it either.
*/
if( (offset!=-1) &&
decode_tcp_ports(tvb, offset, pinfo, tree, src_port,
dst_port, tcpd) ){
/*
* We succeeded in handing off to a subdissector.
*
* Is this a TCP segment or a reassembled chunk of
* TCP payload?
*/
if(is_tcp_segment){
/* if !visited, check want_pdu_tracking and
store it in table */
if(tcpd && (!pinfo->fd->flags.visited) &&
tcp_analyze_seq && pinfo->want_pdu_tracking){
if(seq || nxtseq){
pdu_store_sequencenumber_of_next_pdu(
pinfo,
seq,
nxtseq+pinfo->bytes_until_next_pdu,
tcpd->fwd->multisegment_pdus);
}
}
}
}
}
CATCH_ALL {
/* We got an exception. At this point the dissection is
* completely aborted and execution will be transferred back
* to (probably) the frame dissector.
* Here we have to place whatever we want the dissector
* to do before aborting the tcp dissection.
*/
/*
* Is this a TCP segment or a reassembled chunk of TCP
* payload?
*/
if(is_tcp_segment){
/*
* It's from a TCP segment.
*
* if !visited, check want_pdu_tracking and store it
* in table
*/
if(tcpd && (!pinfo->fd->flags.visited) && tcp_analyze_seq && pinfo->want_pdu_tracking){
if(seq || nxtseq){
pdu_store_sequencenumber_of_next_pdu(pinfo,
seq,
nxtseq+pinfo->bytes_until_next_pdu,
tcpd->fwd->multisegment_pdus);
}
}
}
RETHROW;
}
ENDTRY;
}
void
dissect_tcp_payload(tvbuff_t *tvb, packet_info *pinfo, int offset, guint32 seq,
guint32 nxtseq, guint32 sport, guint32 dport,
proto_tree *tree, proto_tree *tcp_tree,
struct tcp_analysis *tcpd)
{
gboolean save_fragmented;
/* Can we desegment this segment? */
if (pinfo->can_desegment) {
/* Yes. */
desegment_tcp(tvb, pinfo, offset, seq, nxtseq, sport, dport, tree,
tcp_tree, tcpd);
} else {
/* No - just call the subdissector.
Mark this as fragmented, so if somebody throws an exception,
we don't report it as a malformed frame. */
save_fragmented = pinfo->fragmented;
pinfo->fragmented = TRUE;
process_tcp_payload(tvb, offset, pinfo, tree, tcp_tree, sport, dport,
seq, nxtseq, TRUE, tcpd);
pinfo->fragmented = save_fragmented;
}
}
static void
dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
guint8 th_off_x2; /* combines th_off and th_x2 */
guint16 th_sum;
guint32 ack;
guint16 th_urp;
proto_tree *tcp_tree = NULL, *field_tree = NULL;
proto_item *ti = NULL, *tf, *hidden_item;
int offset = 0;
emem_strbuf_t *flags_strbuf = ep_strbuf_new_label("<None>");
const gchar *flags[] = {"FIN", "SYN", "RST", "PSH", "ACK", "URG", "ECN", "CWR", "NS"};
gint i;
guint bpos;
guint optlen;
guint32 nxtseq = 0;
guint reported_len;
vec_t cksum_vec[4];
guint32 phdr[2];
guint16 computed_cksum;
guint16 real_window;
guint length_remaining;
gboolean desegment_ok;
struct tcpinfo tcpinfo;
struct tcpheader *tcph;
proto_item *tf_syn = NULL, *tf_fin = NULL, *tf_rst = NULL, *scaled_pi;
conversation_t *conv=NULL;
struct tcp_analysis *tcpd=NULL;
struct tcp_per_packet_data_t *tcppd=NULL;
proto_item *item;
proto_tree *checksum_tree;
tcph=ep_alloc(sizeof(struct tcpheader));
SET_ADDRESS(&tcph->ip_src, pinfo->src.type, pinfo->src.len, pinfo->src.data);
SET_ADDRESS(&tcph->ip_dst, pinfo->dst.type, pinfo->dst.len, pinfo->dst.data);
col_set_str(pinfo->cinfo, COL_PROTOCOL, "TCP");
/* Clear out the Info column. */
col_clear(pinfo->cinfo, COL_INFO);
tcph->th_sport = tvb_get_ntohs(tvb, offset);
tcph->th_dport = tvb_get_ntohs(tvb, offset + 2);
col_append_fstr(pinfo->cinfo, COL_INFO, "%s > %s",
get_tcp_port(tcph->th_sport), get_tcp_port(tcph->th_dport));
if (tree) {
if (tcp_summary_in_tree) {
ti = proto_tree_add_protocol_format(tree, proto_tcp, tvb, 0, -1,
"Transmission Control Protocol, Src Port: %s (%u), Dst Port: %s (%u)",
get_tcp_port(tcph->th_sport), tcph->th_sport,
get_tcp_port(tcph->th_dport), tcph->th_dport);
}
else {
ti = proto_tree_add_item(tree, proto_tcp, tvb, 0, -1, ENC_NA);
}
tcp_tree = proto_item_add_subtree(ti, ett_tcp);
pinfo->tcp_tree=tcp_tree;
proto_tree_add_uint_format(tcp_tree, hf_tcp_srcport, tvb, offset, 2, tcph->th_sport,
"Source port: %s (%u)", get_tcp_port(tcph->th_sport), tcph->th_sport);
proto_tree_add_uint_format(tcp_tree, hf_tcp_dstport, tvb, offset + 2, 2, tcph->th_dport,
"Destination port: %s (%u)", get_tcp_port(tcph->th_dport), tcph->th_dport);
hidden_item = proto_tree_add_uint(tcp_tree, hf_tcp_port, tvb, offset, 2, tcph->th_sport);
PROTO_ITEM_SET_HIDDEN(hidden_item);
hidden_item = proto_tree_add_uint(tcp_tree, hf_tcp_port, tvb, offset + 2, 2, tcph->th_dport);
PROTO_ITEM_SET_HIDDEN(hidden_item);
/* If we're dissecting the headers of a TCP packet in an ICMP packet
* then go ahead and put the sequence numbers in the tree now (because
* they won't be put in later because the ICMP packet only contains up
* to the sequence number).
* We should only need to do this for IPv4 since IPv6 will hopefully
* carry enough TCP payload for this dissector to put the sequence
* numbers in via the regular code path.
*/
if (pinfo->layer_names != NULL && pinfo->layer_names->str != NULL) {
/* use strstr because g_strrstr is only present in glib2.0 and
* g_str_has_suffix in glib2.2
*/
if (strstr(pinfo->layer_names->str, "icmp:ip") != NULL)
proto_tree_add_item(tcp_tree, hf_tcp_seq, tvb, offset + 4, 4, ENC_BIG_ENDIAN);
}
}
/* Set the source and destination port numbers as soon as we get them,
so that they're available to the "Follow TCP Stream" code even if
we throw an exception dissecting the rest of the TCP header. */
pinfo->ptype = PT_TCP;
pinfo->srcport = tcph->th_sport;
pinfo->destport = tcph->th_dport;
tcph->th_seq = tvb_get_ntohl(tvb, offset + 4);
tcph->th_ack = tvb_get_ntohl(tvb, offset + 8);
th_off_x2 = tvb_get_guint8(tvb, offset + 12);
tcph->th_flags = tvb_get_ntohs(tvb, offset + 12) & 0x0FFF;
tcph->th_win = tvb_get_ntohs(tvb, offset + 14);
real_window = tcph->th_win;
tcph->th_hlen = hi_nibble(th_off_x2) * 4; /* TCP header length, in bytes */
/* find(or create if needed) the conversation for this tcp session */
conv=find_or_create_conversation(pinfo);
tcpd=get_tcp_conversation_data(conv,pinfo);
/* If this is a SYN packet, then check if it's seq-nr is different
* from the base_seq of the retrieved conversation. If this is the
* case, create a new conversation with the same addresses and ports
* and set the TA_PORTS_REUSED flag. If the seq-nr is the same as
* the base_seq, then do nothing so it will be marked as a retrans-
* mission later.
*/
if(tcpd && ((tcph->th_flags&(TH_SYN|TH_ACK))==TH_SYN) &&
(tcpd->fwd->base_seq!=0) &&
(tcph->th_seq!=tcpd->fwd->base_seq) ) {
if (!(pinfo->fd->flags.visited)) {
conv=conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
tcpd=get_tcp_conversation_data(conv,pinfo);
}
if(!tcpd->ta)
tcp_analyze_get_acked_struct(pinfo->fd->num, tcph->th_seq, tcph->th_ack, TRUE, tcpd);
tcpd->ta->flags|=TCP_A_REUSED_PORTS;
}
if (tcpd) {
item = proto_tree_add_uint(tcp_tree, hf_tcp_stream, tvb, offset, 0, tcpd->stream);
PROTO_ITEM_SET_GENERATED(item);
/* Copy the stream index into the header as well to make it available
* to tap listeners.
*/
tcph->th_stream = tcpd->stream;
}
/* Do we need to calculate timestamps relative to the tcp-stream? */
if (tcp_calculate_ts) {
tcppd = p_get_proto_data(pinfo->fd, proto_tcp);
/*
* Calculate the timestamps relative to this conversation (but only on the
* first run when frames are accessed sequentially)
*/
if (!(pinfo->fd->flags.visited))
tcp_calculate_timestamps(pinfo, tcpd, tcppd);
}
/*
* If we've been handed an IP fragment, we don't know how big the TCP
* segment is, so don't do anything that requires that we know that.
*
* The same applies if we're part of an error packet. (XXX - if the
* ICMP and ICMPv6 dissectors could set a "this is how big the IP
* header says it is" length in the tvbuff, we could use that; such
* a length might also be useful for handling packets where the IP
* length is bigger than the actual data available in the frame; the
* dissectors should trust that length, and then throw a
* ReportedBoundsError exception when they go past the end of the frame.)
*
* We also can't determine the segment length if the reported length
* of the TCP packet is less than the TCP header length.
*/
reported_len = tvb_reported_length(tvb);
if (!pinfo->fragmented && !pinfo->flags.in_error_pkt) {
if (reported_len < tcph->th_hlen) {
proto_item *pi;
pi = proto_tree_add_text(tcp_tree, tvb, offset, 0,
"Short segment. Segment/fragment does not contain a full TCP header"
" (might be NMAP or someone else deliberately sending unusual packets)");
PROTO_ITEM_SET_GENERATED(pi);
expert_add_info_format(pinfo, pi, PI_MALFORMED, PI_WARN, "Short segment");
tcph->th_have_seglen = FALSE;
} else {
/* Compute the length of data in this segment. */
tcph->th_seglen = reported_len - tcph->th_hlen;
tcph->th_have_seglen = TRUE;
if (tree) { /* Add the seglen as an invisible field */
hidden_item = proto_tree_add_uint(ti, hf_tcp_len, tvb, offset+12, 1, tcph->th_seglen);
PROTO_ITEM_SET_HIDDEN(hidden_item);
}
/* handle TCP seq# analysis parse all new segments we see */
if(tcp_analyze_seq){
if(!(pinfo->fd->flags.visited)){
tcp_analyze_sequence_number(pinfo, tcph->th_seq, tcph->th_ack, tcph->th_seglen, tcph->th_flags, tcph->th_win, tcpd);
}
if(tcpd && tcp_relative_seq) {
(tcph->th_seq) -= tcpd->fwd->base_seq;
(tcph->th_ack) -= tcpd->rev->base_seq;
}
}
/* re-calculate window size, based on scaling factor */
if (!(tcph->th_flags&TH_SYN)) { /* SYNs are never scaled */
if (tcpd && (tcpd->fwd->win_scale>=0)) {
(tcph->th_win)<<=tcpd->fwd->win_scale;
}
}
/* Compute the sequence number of next octet after this segment. */
nxtseq = tcph->th_seq + tcph->th_seglen;
}
} else
tcph->th_have_seglen = FALSE;
if (check_col(pinfo->cinfo, COL_INFO) || tree) {
gboolean first_flag = TRUE;
for (i = 0; i < 9; i++) {
bpos = 1 << i;
if (tcph->th_flags & bpos) {
if (first_flag) {
ep_strbuf_truncate(flags_strbuf, 0);
}
ep_strbuf_append_printf(flags_strbuf, "%s%s", first_flag ? "" : ", ", flags[i]);
first_flag = FALSE;
}
}
if (tcph->th_flags & 0x0E00) {
if (first_flag) {
ep_strbuf_truncate(flags_strbuf, 0);
}
ep_strbuf_append_printf(flags_strbuf, "%sReserved", first_flag ? "" : ", ");
}
}
col_append_fstr(pinfo->cinfo, COL_INFO, " [%s] Seq=%u", flags_strbuf->str, tcph->th_seq);
if (tcph->th_flags&TH_ACK) {
col_append_fstr(pinfo->cinfo, COL_INFO, " Ack=%u", tcph->th_ack);
}
col_append_fstr(pinfo->cinfo, COL_INFO, " Win=%u", tcph->th_win);
if (tree) {
if (tcp_summary_in_tree) {
proto_item_append_text(ti, ", Seq: %u", tcph->th_seq);
}
if(tcp_relative_seq){
proto_tree_add_uint_format(tcp_tree, hf_tcp_seq, tvb, offset + 4, 4, tcph->th_seq, "Sequence number: %u (relative sequence number)", tcph->th_seq);
} else {
proto_tree_add_uint(tcp_tree, hf_tcp_seq, tvb, offset + 4, 4, tcph->th_seq);
}
}
if (tcph->th_hlen < TCPH_MIN_LEN) {
/* Give up at this point; we put the source and destination port in
the tree, before fetching the header length, so that they'll
show up if this is in the failing packet in an ICMP error packet,
but it's now time to give up if the header length is bogus. */
col_append_fstr(pinfo->cinfo, COL_INFO, ", bogus TCP header length (%u, must be at least %u)",
tcph->th_hlen, TCPH_MIN_LEN);
if (tree) {
proto_tree_add_uint_format(tcp_tree, hf_tcp_hdr_len, tvb, offset + 12, 1, tcph->th_hlen,
"Header length: %u bytes (bogus, must be at least %u)", tcph->th_hlen,
TCPH_MIN_LEN);
}
return;
}
if (tree) {
if (tcp_summary_in_tree) {
if(tcph->th_flags&TH_ACK){
proto_item_append_text(ti, ", Ack: %u", tcph->th_ack);
}
if (tcph->th_have_seglen)
proto_item_append_text(ti, ", Len: %u", tcph->th_seglen);
}
proto_item_set_len(ti, tcph->th_hlen);
if (tcph->th_have_seglen) {
if (nxtseq != tcph->th_seq) {
if(tcp_relative_seq){
tf=proto_tree_add_uint_format(tcp_tree, hf_tcp_nxtseq, tvb, offset, 0, nxtseq, "Next sequence number: %u (relative sequence number)", nxtseq);
} else {
tf=proto_tree_add_uint(tcp_tree, hf_tcp_nxtseq, tvb, offset, 0, nxtseq);
}
PROTO_ITEM_SET_GENERATED(tf);
}
}
}
if (tcph->th_flags & TH_ACK) {
if (tree) {
if (tcp_relative_seq){
proto_tree_add_uint_format(tcp_tree, hf_tcp_ack, tvb, offset + 8, 4, tcph->th_ack, "Acknowledgment number: %u (relative ack number)", tcph->th_ack);
} else {
proto_tree_add_uint(tcp_tree, hf_tcp_ack, tvb, offset + 8, 4, tcph->th_ack);
}
}
} else {
/* Verify that the ACK field is zero */
ack = tvb_get_ntohl(tvb, offset+8);
if (ack != 0){
item = proto_tree_add_uint_format(tcp_tree, hf_tcp_ack, tvb, offset + 8, 4, ack,
"Acknowledgment Number: 0x%08x [should be 0x00000000 because ACK flag is not set]",
ack);
expert_add_info_format(pinfo, item, PI_PROTOCOL, PI_WARN,
"Acknowledgment number: Broken TCP. The acknowledge field is nonzero while the ACK flag is not set");
}
}
if (tree) {
proto_tree_add_uint_format(tcp_tree, hf_tcp_hdr_len, tvb, offset + 12, 1, tcph->th_hlen,
"Header length: %u bytes", tcph->th_hlen);
tf = proto_tree_add_uint_format(tcp_tree, hf_tcp_flags, tvb, offset + 12, 2,
tcph->th_flags, "Flags: 0x%03x (%s)", tcph->th_flags, flags_strbuf->str);
field_tree = proto_item_add_subtree(tf, ett_tcp_flags);
proto_tree_add_boolean(field_tree, hf_tcp_flags_res, tvb, offset + 12, 1, tcph->th_flags);
proto_tree_add_boolean(field_tree, hf_tcp_flags_ns, tvb, offset + 12, 1, tcph->th_flags);
proto_tree_add_boolean(field_tree, hf_tcp_flags_cwr, tvb, offset + 13, 1, tcph->th_flags);
proto_tree_add_boolean(field_tree, hf_tcp_flags_ecn, tvb, offset + 13, 1, tcph->th_flags);
proto_tree_add_boolean(field_tree, hf_tcp_flags_urg, tvb, offset + 13, 1, tcph->th_flags);
proto_tree_add_boolean(field_tree, hf_tcp_flags_ack, tvb, offset + 13, 1, tcph->th_flags);
proto_tree_add_boolean(field_tree, hf_tcp_flags_push, tvb, offset + 13, 1, tcph->th_flags);
tf_rst = proto_tree_add_boolean(field_tree, hf_tcp_flags_reset, tvb, offset + 13, 1, tcph->th_flags);
tf_syn = proto_tree_add_boolean(field_tree, hf_tcp_flags_syn, tvb, offset + 13, 1, tcph->th_flags);
tf_fin = proto_tree_add_boolean(field_tree, hf_tcp_flags_fin, tvb, offset + 13, 1, tcph->th_flags);
/* As discussed in bug 5541, it is better to use two separate
* fields for the real and calculated window size.
*/
proto_tree_add_uint(tcp_tree, hf_tcp_window_size_value, tvb, offset + 14, 2, real_window);
scaled_pi = proto_tree_add_uint(tcp_tree, hf_tcp_window_size, tvb, offset + 14, 2, tcph->th_win);
PROTO_ITEM_SET_GENERATED(scaled_pi);
if( !(tcph->th_flags&TH_SYN) && tcpd ) {
switch (tcpd->fwd->win_scale) {
case -1:
scaled_pi = proto_tree_add_int_format(tcp_tree, hf_tcp_window_size_scalefactor, tvb, offset + 14, 2, tcpd->fwd->win_scale, "Window size scaling factor: %d (unknown)", tcpd->fwd->win_scale);
PROTO_ITEM_SET_GENERATED(scaled_pi);
break;
case -2:
scaled_pi = proto_tree_add_int_format(tcp_tree, hf_tcp_window_size_scalefactor, tvb, offset + 14, 2, tcpd->fwd->win_scale, "Window size scaling factor: %d (no window scaling used)", tcpd->fwd->win_scale);
PROTO_ITEM_SET_GENERATED(scaled_pi);
break;
default:
scaled_pi = proto_tree_add_int_format(tcp_tree, hf_tcp_window_size_scalefactor, tvb, offset + 14, 2, 1<<tcpd->fwd->win_scale, "Window size scaling factor: %d", 1<<tcpd->fwd->win_scale);
PROTO_ITEM_SET_GENERATED(scaled_pi);
}
}
}
if(tcph->th_flags & TH_SYN) {
if(tcph->th_flags & TH_ACK) {
expert_add_info_format(pinfo, tf_syn, PI_SEQUENCE, PI_CHAT, "Connection establish acknowledge (SYN+ACK): server port %s",
get_tcp_port(tcph->th_sport));
/* Save the server port to help determine dissector used */
tcpd->server_port = tcph->th_sport;
}
else {
expert_add_info_format(pinfo, tf_syn, PI_SEQUENCE, PI_CHAT, "Connection establish request (SYN): server port %s",
get_tcp_port(tcph->th_dport));
/* Save the server port to help determine dissector used */
tcpd->server_port = tcph->th_dport;
}
}
if(tcph->th_flags & TH_FIN)
/* XXX - find a way to know the server port and output only that one */
expert_add_info_format(pinfo, tf_fin, PI_SEQUENCE, PI_CHAT, "Connection finish (FIN)");
if(tcph->th_flags & TH_RST)
/* XXX - find a way to know the server port and output only that one */
expert_add_info_format(pinfo, tf_rst, PI_SEQUENCE, PI_CHAT, "Connection reset (RST)");
/* Supply the sequence number of the first byte and of the first byte
after the segment. */
tcpinfo.seq = tcph->th_seq;
tcpinfo.nxtseq = nxtseq;
tcpinfo.lastackseq = tcph->th_ack;
/* Assume we'll pass un-reassembled data to subdissectors. */
tcpinfo.is_reassembled = FALSE;
pinfo->private_data = &tcpinfo;
/*
* Assume, initially, that we can't desegment.
*/
pinfo->can_desegment = 0;
th_sum = tvb_get_ntohs(tvb, offset + 16);
if (!pinfo->fragmented && tvb_bytes_exist(tvb, 0, reported_len)) {
/* The packet isn't part of an un-reassembled fragmented datagram
and isn't truncated. This means we have all the data, and thus
can checksum it and, unless it's being returned in an error
packet, are willing to allow subdissectors to request reassembly
on it. */
if (tcp_check_checksum) {
/* We haven't turned checksum checking off; checksum it. */
/* Set up the fields of the pseudo-header. */
cksum_vec[0].ptr = pinfo->src.data;
cksum_vec[0].len = pinfo->src.len;
cksum_vec[1].ptr = pinfo->dst.data;
cksum_vec[1].len = pinfo->dst.len;
cksum_vec[2].ptr = (const guint8 *)phdr;
switch (pinfo->src.type) {
case AT_IPv4:
phdr[0] = g_htonl((IP_PROTO_TCP<<16) + reported_len);
cksum_vec[2].len = 4;
break;
case AT_IPv6:
phdr[0] = g_htonl(reported_len);
phdr[1] = g_htonl(IP_PROTO_TCP);
cksum_vec[2].len = 8;
break;
default:
/* TCP runs only atop IPv4 and IPv6.... */
DISSECTOR_ASSERT_NOT_REACHED();
break;
}
cksum_vec[3].ptr = tvb_get_ptr(tvb, offset, reported_len);
cksum_vec[3].len = reported_len;
computed_cksum = in_cksum(cksum_vec, 4);
if (computed_cksum == 0 && th_sum == 0xffff) {
item = proto_tree_add_uint_format(tcp_tree, hf_tcp_checksum, tvb,
offset + 16, 2, th_sum,
"Checksum: 0x%04x [should be 0x0000 (see RFC 1624)]", th_sum);
checksum_tree = proto_item_add_subtree(item, ett_tcp_checksum);
item = proto_tree_add_boolean(checksum_tree, hf_tcp_checksum_good, tvb,
offset + 16, 2, FALSE);
PROTO_ITEM_SET_GENERATED(item);
item = proto_tree_add_boolean(checksum_tree, hf_tcp_checksum_bad, tvb,
offset + 16, 2, FALSE);
PROTO_ITEM_SET_GENERATED(item);
expert_add_info_format(pinfo, item, PI_CHECKSUM, PI_WARN, "TCP Checksum 0xffff instead of 0x0000 (see RFC 1624)");
col_append_str(pinfo->cinfo, COL_INFO, " [TCP CHECKSUM 0xFFFF]");
/* Checksum is treated as valid on most systems, so we're willing to desegment it. */
desegment_ok = TRUE;
} else if (computed_cksum == 0) {
item = proto_tree_add_uint_format(tcp_tree, hf_tcp_checksum, tvb,
offset + 16, 2, th_sum, "Checksum: 0x%04x [correct]", th_sum);
checksum_tree = proto_item_add_subtree(item, ett_tcp_checksum);
item = proto_tree_add_boolean(checksum_tree, hf_tcp_checksum_good, tvb,
offset + 16, 2, TRUE);
PROTO_ITEM_SET_GENERATED(item);
item = proto_tree_add_boolean(checksum_tree, hf_tcp_checksum_bad, tvb,
offset + 16, 2, FALSE);
PROTO_ITEM_SET_GENERATED(item);
/* Checksum is valid, so we're willing to desegment it. */
desegment_ok = TRUE;
} else if (th_sum == 0) {
/* checksum is probably fine but checksum offload is used */
item = proto_tree_add_uint_format(tcp_tree, hf_tcp_checksum, tvb,
offset + 16, 2, th_sum, "Checksum: 0x%04x [Checksum Offloaded]", th_sum);
checksum_tree = proto_item_add_subtree(item, ett_tcp_checksum);
item = proto_tree_add_boolean(checksum_tree, hf_tcp_checksum_good, tvb,
offset + 16, 2, FALSE);
PROTO_ITEM_SET_GENERATED(item);
item = proto_tree_add_boolean(checksum_tree, hf_tcp_checksum_bad, tvb,
offset + 16, 2, FALSE);
PROTO_ITEM_SET_GENERATED(item);
/* Checksum is (probably) valid, so we're willing to desegment it. */
desegment_ok = TRUE;
} else {
item = proto_tree_add_uint_format(tcp_tree, hf_tcp_checksum, tvb,
offset + 16, 2, th_sum,
"Checksum: 0x%04x [incorrect, should be 0x%04x (maybe caused by \"TCP checksum offload\"?)]", th_sum,
in_cksum_shouldbe(th_sum, computed_cksum));
checksum_tree = proto_item_add_subtree(item, ett_tcp_checksum);
item = proto_tree_add_boolean(checksum_tree, hf_tcp_checksum_good, tvb,
offset + 16, 2, FALSE);
PROTO_ITEM_SET_GENERATED(item);
item = proto_tree_add_boolean(checksum_tree, hf_tcp_checksum_bad, tvb,
offset + 16, 2, TRUE);
PROTO_ITEM_SET_GENERATED(item);
expert_add_info_format(pinfo, item, PI_CHECKSUM, PI_ERROR, "Bad checksum");
col_append_str(pinfo->cinfo, COL_INFO, " [TCP CHECKSUM INCORRECT]");
/* Checksum is invalid, so we're not willing to desegment it. */
desegment_ok = FALSE;
pinfo->noreassembly_reason = " [incorrect TCP checksum]";
}
} else {
item = proto_tree_add_uint_format(tcp_tree, hf_tcp_checksum, tvb,
offset + 16, 2, th_sum, "Checksum: 0x%04x [validation disabled]", th_sum);
checksum_tree = proto_item_add_subtree(item, ett_tcp_checksum);
item = proto_tree_add_boolean(checksum_tree, hf_tcp_checksum_good, tvb,
offset + 16, 2, FALSE);
PROTO_ITEM_SET_GENERATED(item);
item = proto_tree_add_boolean(checksum_tree, hf_tcp_checksum_bad, tvb,
offset + 16, 2, FALSE);
PROTO_ITEM_SET_GENERATED(item);
/* We didn't check the checksum, and don't care if it's valid,
so we're willing to desegment it. */
desegment_ok = TRUE;
}
} else {
/* We don't have all the packet data, so we can't checksum it... */
item = proto_tree_add_uint_format(tcp_tree, hf_tcp_checksum, tvb,
offset + 16, 2, th_sum, "Checksum: 0x%04x [unchecked, not all data available]", th_sum);
checksum_tree = proto_item_add_subtree(item, ett_tcp_checksum);
item = proto_tree_add_boolean(checksum_tree, hf_tcp_checksum_good, tvb,
offset + 16, 2, FALSE);
PROTO_ITEM_SET_GENERATED(item);
item = proto_tree_add_boolean(checksum_tree, hf_tcp_checksum_bad, tvb,
offset + 16, 2, FALSE);
PROTO_ITEM_SET_GENERATED(item);
/* ...and aren't willing to desegment it. */
desegment_ok = FALSE;
}
if (desegment_ok) {
/* We're willing to desegment this. Is desegmentation enabled? */
if (tcp_desegment) {
/* Yes - is this segment being returned in an error packet? */
if (!pinfo->flags.in_error_pkt) {
/* No - indicate that we will desegment.
We do NOT want to desegment segments returned in error
packets, as they're not part of a TCP connection. */
pinfo->can_desegment = 2;
}
}
}
th_urp = tvb_get_ntohs(tvb, offset + 18);
if (tcph->th_flags & TH_URG) {
/* Export the urgent pointer, for the benefit of protocols such as
rlogin. */
tcpinfo.urgent = TRUE;
tcpinfo.urgent_pointer = th_urp;
col_append_fstr(pinfo->cinfo, COL_INFO, " Urg=%u", th_urp);
if (tcp_tree != NULL)
proto_tree_add_uint(tcp_tree, hf_tcp_urgent_pointer, tvb, offset + 18, 2, th_urp);
} else {
tcpinfo.urgent = FALSE;
if (th_urp) {
item = proto_tree_add_uint_format(tcp_tree, hf_tcp_urgent_pointer, tvb, offset + 18, 2, th_urp,
"Urgent Pointer: 0x%04x [should be 0x0000 because URG flag is not set]",
th_urp);
expert_add_info_format(pinfo, item, PI_PROTOCOL, PI_WARN,
"Urgent Pointer: Broken TCP. The urgent pointer field is nonzero while the URG flag is not set");
}
}
if (tcph->th_have_seglen) {
col_append_fstr(pinfo->cinfo, COL_INFO, " Len=%u", tcph->th_seglen);
}
/* Decode TCP options, if any. */
if (tcph->th_hlen > TCPH_MIN_LEN) {
/* There's more than just the fixed-length header. Decode the
options. */
optlen = tcph->th_hlen - TCPH_MIN_LEN; /* length of options, in bytes */
tvb_ensure_bytes_exist(tvb, offset + 20, optlen);
if (tcp_tree != NULL) {
guint8 *p_options = ep_tvb_memdup(tvb, offset + 20, optlen);
tf = proto_tree_add_bytes_format(tcp_tree, hf_tcp_options, tvb, offset + 20,
optlen, p_options, "Options: (%u bytes)", optlen);
field_tree = proto_item_add_subtree(tf, ett_tcp_options);
} else {
tf = NULL;
field_tree = NULL;
}
dissect_ip_tcp_options(tvb, offset + 20, optlen,
tcpopts, N_TCP_OPTS, TCPOPT_EOL, pinfo, field_tree, tf);
}
if(!pinfo->fd->flags.visited){
if((tcph->th_flags & TH_SYN)==TH_SYN) {
/* Check the validity of the window scale value
*/
verify_tcp_window_scaling((tcph->th_flags&TH_ACK)==TH_ACK,tcpd);
}
if((tcph->th_flags & (TH_SYN|TH_ACK))==(TH_SYN|TH_ACK)) {
/* If the SYN or the SYN+ACK offered SCPS capabilities,
* validate the flow's bidirectional scps capabilities.
* The or protects against broken implementations offering
* SCPS capabilities on SYN+ACK even if it wasn't offered with the SYN
*/
if(tcpd && ((tcpd->rev->scps_capable) || (tcpd->fwd->scps_capable))) {
verify_scps(pinfo, tf_syn, tcpd);
}
}
}
/* Skip over header + options */
offset += tcph->th_hlen;
/* Check the packet length to see if there's more data
(it could be an ACK-only packet) */
length_remaining = MAX(0, tvb_length_remaining(tvb, offset));
if (tcph->th_have_seglen) {
if( data_out_file ) {
reassemble_tcp( tcpd->stream, /* tcp stream index */
tcph->th_seq, /* sequence number */
tcph->th_ack, /* acknowledgment number */
tcph->th_seglen, /* data length */
(gchar*)tvb_get_ptr(tvb, offset, length_remaining), /* data */
length_remaining, /* captured data length */
( tcph->th_flags & TH_SYN ), /* is syn set? */
&pinfo->net_src,
&pinfo->net_dst,
pinfo->srcport,
pinfo->destport);
}
}
/* handle TCP seq# analysis, print any extra SEQ/ACK data for this segment*/
if(tcp_analyze_seq){
guint32 use_seq = tcph->th_seq;
guint32 use_ack = tcph->th_ack;
/* May need to recover absolute values here... */
if (tcp_relative_seq) {
use_seq += tcpd->fwd->base_seq;
use_ack += tcpd->rev->base_seq;
}
tcp_print_sequence_number_analysis(pinfo, tvb, tcp_tree, tcpd, use_seq, use_ack);
}
/* handle conversation timestamps */
if(tcp_calculate_ts){
tcp_print_timestamps(pinfo, tvb, tcp_tree, tcpd, tcppd);
}
tap_queue_packet(tcp_tap, pinfo, tcph);
/* A FIN packet might complete reassembly so we need to explicitly
* check for this here.
*/
if(tcph->th_have_seglen && tcpd && (tcph->th_flags & TH_FIN)
&& (tcpd->fwd->flags&TCP_FLOW_REASSEMBLE_UNTIL_FIN) ){
struct tcp_multisegment_pdu *msp;
/* find the most previous PDU starting before this sequence number */
msp=se_tree_lookup32_le(tcpd->fwd->multisegment_pdus, tcph->th_seq-1);
if(msp){
fragment_data *ipfd_head;
ipfd_head = fragment_add(tvb, offset, pinfo, msp->first_frame,
tcp_fragment_table,
tcph->th_seq - msp->seq,
tcph->th_seglen,
FALSE );
if(ipfd_head){
tvbuff_t *next_tvb;
/* create a new TVB structure for desegmented data
* datalen-1 to strip the dummy FIN byte off
*/
next_tvb = tvb_new_child_real_data(tvb, ipfd_head->data, ipfd_head->datalen, ipfd_head->datalen);
/* add desegmented data to the data source list */
add_new_data_source(pinfo, next_tvb, "Reassembled TCP");
/* call the payload dissector
* but make sure we don't offer desegmentation any more
*/
pinfo->can_desegment = 0;
process_tcp_payload(next_tvb, 0, pinfo, tree, tcp_tree, tcph->th_sport, tcph->th_dport, tcph->th_seq, nxtseq, FALSE, tcpd);
print_tcp_fragment_tree(ipfd_head, tree, tcp_tree, pinfo, next_tvb);
return;
}
}
}
if (tcpd && ((tcpd->fwd && tcpd->fwd->command) || (tcpd->rev && tcpd->rev->command))) {
ti = proto_tree_add_text(tcp_tree, tvb, offset, 0, "Process Information");
PROTO_ITEM_SET_GENERATED(ti);
field_tree = proto_item_add_subtree(ti, ett_tcp_process_info);
if (tcpd->fwd && tcpd->fwd->command) {
proto_tree_add_uint_format_value(field_tree, hf_tcp_proc_dst_uid, tvb, 0, 0,
tcpd->fwd->process_uid, "%u", tcpd->fwd->process_uid);
proto_tree_add_uint_format_value(field_tree, hf_tcp_proc_dst_pid, tvb, 0, 0,
tcpd->fwd->process_pid, "%u", tcpd->fwd->process_pid);
proto_tree_add_string_format_value(field_tree, hf_tcp_proc_dst_uname, tvb, 0, 0,
tcpd->fwd->username, "%s", tcpd->fwd->username);
proto_tree_add_string_format_value(field_tree, hf_tcp_proc_dst_cmd, tvb, 0, 0,
tcpd->fwd->command, "%s", tcpd->fwd->command);
}
if (tcpd->rev && tcpd->rev->command) {
proto_tree_add_uint_format_value(field_tree, hf_tcp_proc_src_uid, tvb, 0, 0,
tcpd->rev->process_uid, "%u", tcpd->rev->process_uid);
proto_tree_add_uint_format_value(field_tree, hf_tcp_proc_src_pid, tvb, 0, 0,
tcpd->rev->process_pid, "%u", tcpd->rev->process_pid);
proto_tree_add_string_format_value(field_tree, hf_tcp_proc_src_uname, tvb, 0, 0,
tcpd->rev->username, "%s", tcpd->rev->username);
proto_tree_add_string_format_value(field_tree, hf_tcp_proc_src_cmd, tvb, 0, 0,
tcpd->rev->command, "%s", tcpd->rev->command);
}
}
/*
* XXX - what, if any, of this should we do if this is included in an
* error packet? It might be nice to see the details of the packet
* that caused the ICMP error, but it might not be nice to have the
* dissector update state based on it.
* Also, we probably don't want to run TCP taps on those packets.
*/
if (length_remaining != 0) {
if (tcph->th_flags & TH_RST) {
/*
* RFC1122 says:
*
* 4.2.2.12 RST Segment: RFC-793 Section 3.4
*
* A TCP SHOULD allow a received RST segment to include data.
*
* DISCUSSION
* It has been suggested that a RST segment could contain
* ASCII text that encoded and explained the cause of the
* RST. No standard has yet been established for such
* data.
*
* so for segments with RST we just display the data as text.
*/
proto_tree_add_text(tcp_tree, tvb, offset, length_remaining,
"Reset cause: %s",
tvb_format_text(tvb, offset, length_remaining));
} else {
dissect_tcp_payload(tvb, pinfo, offset, tcph->th_seq, nxtseq,
tcph->th_sport, tcph->th_dport, tree, tcp_tree, tcpd);
}
}
}
static void
tcp_init(void)
{
tcp_stream_index = 0;
fragment_table_init(&tcp_fragment_table);
}
void
proto_register_tcp(void)
{
static hf_register_info hf[] = {
{ &hf_tcp_srcport,
{ "Source Port", "tcp.srcport", FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_tcp_dstport,
{ "Destination Port", "tcp.dstport", FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_tcp_port,
{ "Source or Destination Port", "tcp.port", FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_tcp_stream,
{ "Stream index", "tcp.stream", FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_tcp_seq,
{ "Sequence number", "tcp.seq", FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_tcp_nxtseq,
{ "Next sequence number", "tcp.nxtseq", FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_tcp_ack,
{ "Acknowledgment number", "tcp.ack", FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_tcp_hdr_len,
{ "Header Length", "tcp.hdr_len", FT_UINT8, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_tcp_flags,
{ "Flags", "tcp.flags", FT_UINT16, BASE_HEX, NULL, TH_MASK,
NULL, HFILL }},
{ &hf_tcp_flags_res,
{ "Reserved", "tcp.flags.res", FT_BOOLEAN, 12, TFS(&tfs_set_notset), TH_RES,
"Three reserved bits (must be zero)", HFILL }},
{ &hf_tcp_flags_ns,
{ "Nonce", "tcp.flags.ns", FT_BOOLEAN, 12, TFS(&tfs_set_notset), TH_NS,
"ECN concealment protection (RFC 3540)", HFILL }},
{ &hf_tcp_flags_cwr,
{ "Congestion Window Reduced (CWR)", "tcp.flags.cwr", FT_BOOLEAN, 12, TFS(&tfs_set_notset), TH_CWR,
NULL, HFILL }},
{ &hf_tcp_flags_ecn,
{ "ECN-Echo", "tcp.flags.ecn", FT_BOOLEAN, 12, TFS(&tfs_set_notset), TH_ECN,
NULL, HFILL }},
{ &hf_tcp_flags_urg,
{ "Urgent", "tcp.flags.urg", FT_BOOLEAN, 12, TFS(&tfs_set_notset), TH_URG,
NULL, HFILL }},
{ &hf_tcp_flags_ack,
{ "Acknowledgment", "tcp.flags.ack", FT_BOOLEAN, 12, TFS(&tfs_set_notset), TH_ACK,
NULL, HFILL }},
{ &hf_tcp_flags_push,
{ "Push", "tcp.flags.push", FT_BOOLEAN, 12, TFS(&tfs_set_notset), TH_PUSH,
NULL, HFILL }},
{ &hf_tcp_flags_reset,
{ "Reset", "tcp.flags.reset", FT_BOOLEAN, 12, TFS(&tfs_set_notset), TH_RST,
NULL, HFILL }},
{ &hf_tcp_flags_syn,
{ "Syn", "tcp.flags.syn", FT_BOOLEAN, 12, TFS(&tfs_set_notset), TH_SYN,
NULL, HFILL }},
{ &hf_tcp_flags_fin,
{ "Fin", "tcp.flags.fin", FT_BOOLEAN, 12, TFS(&tfs_set_notset), TH_FIN,
NULL, HFILL }},
{ &hf_tcp_window_size_value,
{ "Window size value", "tcp.window_size_value", FT_UINT16, BASE_DEC, NULL, 0x0,
"The window size value from the TCP header", HFILL }},
/* 32 bits so we can present some values adjusted to window scaling */
{ &hf_tcp_window_size,
{ "Calculated window size", "tcp.window_size", FT_UINT32, BASE_DEC, NULL, 0x0,
"The scaled window size (if scaling has been used)", HFILL }},
{ &hf_tcp_window_size_scalefactor,
{ "Window size scaling factor", "tcp.window_size_scalefactor", FT_INT32, BASE_DEC, NULL, 0x0,
"The window size scaling factor (-1 when unknown, -2 when no scaling is used)", HFILL }},
{ &hf_tcp_checksum,
{ "Checksum", "tcp.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
"Details at: http://www.wireshark.org/docs/wsug_html_chunked/ChAdvChecksums.html", HFILL }},
{ &hf_tcp_checksum_good,
{ "Good Checksum", "tcp.checksum_good", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"True: checksum matches packet content; False: doesn't match content or not checked", HFILL }},
{ &hf_tcp_checksum_bad,
{ "Bad Checksum", "tcp.checksum_bad", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"True: checksum doesn't match packet content; False: matches content or not checked", HFILL }},
{ &hf_tcp_analysis,
{ "SEQ/ACK analysis", "tcp.analysis", FT_NONE, BASE_NONE, NULL, 0x0,
"This frame has some of the TCP analysis shown", HFILL }},
{ &hf_tcp_analysis_flags,
{ "TCP Analysis Flags", "tcp.analysis.flags", FT_NONE, BASE_NONE, NULL, 0x0,
"This frame has some of the TCP analysis flags set", HFILL }},
{ &hf_tcp_analysis_retransmission,
{ "Retransmission", "tcp.analysis.retransmission", FT_NONE, BASE_NONE, NULL, 0x0,
"This frame is a suspected TCP retransmission", HFILL }},
{ &hf_tcp_analysis_fast_retransmission,
{ "Fast Retransmission", "tcp.analysis.fast_retransmission", FT_NONE, BASE_NONE, NULL, 0x0,
"This frame is a suspected TCP fast retransmission", HFILL }},
{ &hf_tcp_analysis_out_of_order,
{ "Out Of Order", "tcp.analysis.out_of_order", FT_NONE, BASE_NONE, NULL, 0x0,
"This frame is a suspected Out-Of-Order segment", HFILL }},
{ &hf_tcp_analysis_reused_ports,
{ "TCP Port numbers reused", "tcp.analysis.reused_ports", FT_NONE, BASE_NONE, NULL, 0x0,
"A new tcp session has started with previously used port numbers", HFILL }},
{ &hf_tcp_analysis_lost_packet,
{ "Previous Segment Unseen", "tcp.analysis.lost_segment", FT_NONE, BASE_NONE, NULL, 0x0,
"We may not have captured a segment before this one", HFILL }},
{ &hf_tcp_analysis_ack_lost_packet,
{ "ACKed Unseen Packet", "tcp.analysis.ack_lost_segment", FT_NONE, BASE_NONE, NULL, 0x0,
"This frame ACKs a segment that may not have been captured", HFILL }},
{ &hf_tcp_analysis_window_update,
{ "Window update", "tcp.analysis.window_update", FT_NONE, BASE_NONE, NULL, 0x0,
"This frame is a tcp window update", HFILL }},
{ &hf_tcp_analysis_window_full,
{ "Window full", "tcp.analysis.window_full", FT_NONE, BASE_NONE, NULL, 0x0,
"This segment has caused the allowed window to become 100% full", HFILL }},
{ &hf_tcp_analysis_keep_alive,
{ "Keep Alive", "tcp.analysis.keep_alive", FT_NONE, BASE_NONE, NULL, 0x0,
"This is a keep-alive segment", HFILL }},
{ &hf_tcp_analysis_keep_alive_ack,
{ "Keep Alive ACK", "tcp.analysis.keep_alive_ack", FT_NONE, BASE_NONE, NULL, 0x0,
"This is an ACK to a keep-alive segment", HFILL }},
{ &hf_tcp_analysis_duplicate_ack,
{ "Duplicate ACK", "tcp.analysis.duplicate_ack", FT_NONE, BASE_NONE, NULL, 0x0,
"This is a duplicate ACK", HFILL }},
{ &hf_tcp_analysis_duplicate_ack_num,
{ "Duplicate ACK #", "tcp.analysis.duplicate_ack_num", FT_UINT32, BASE_DEC, NULL, 0x0,
"This is duplicate ACK number #", HFILL }},
{ &hf_tcp_analysis_duplicate_ack_frame,
{ "Duplicate to the ACK in frame", "tcp.analysis.duplicate_ack_frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
"This is a duplicate to the ACK in frame #", HFILL }},
{ &hf_tcp_continuation_to,
{ "This is a continuation to the PDU in frame", "tcp.continuation_to", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
"This is a continuation to the PDU in frame #", HFILL }},
{ &hf_tcp_analysis_zero_window_probe,
{ "Zero Window Probe", "tcp.analysis.zero_window_probe", FT_NONE, BASE_NONE, NULL, 0x0,
"This is a zero-window-probe", HFILL }},
{ &hf_tcp_analysis_zero_window_probe_ack,
{ "Zero Window Probe Ack", "tcp.analysis.zero_window_probe_ack", FT_NONE, BASE_NONE, NULL, 0x0,
"This is an ACK to a zero-window-probe", HFILL }},
{ &hf_tcp_analysis_zero_window,
{ "Zero Window", "tcp.analysis.zero_window", FT_NONE, BASE_NONE, NULL, 0x0,
"This is a zero-window", HFILL }},
{ &hf_tcp_len,
{ "TCP Segment Len", "tcp.len", FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL}},
{ &hf_tcp_analysis_acks_frame,
{ "This is an ACK to the segment in frame", "tcp.analysis.acks_frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
"Which previous segment is this an ACK for", HFILL}},
{ &hf_tcp_analysis_bytes_in_flight,
{ "Bytes in flight", "tcp.analysis.bytes_in_flight", FT_UINT32, BASE_DEC, NULL, 0x0,
"How many bytes are now in flight for this connection", HFILL}},
{ &hf_tcp_analysis_ack_rtt,
{ "The RTT to ACK the segment was", "tcp.analysis.ack_rtt", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
"How long time it took to ACK the segment (RTT)", HFILL}},
{ &hf_tcp_analysis_rto,
{ "The RTO for this segment was", "tcp.analysis.rto", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
"How long transmission was delayed before this segment was retransmitted (RTO)", HFILL}},
{ &hf_tcp_analysis_rto_frame,
{ "RTO based on delta from frame", "tcp.analysis.rto_frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
"This is the frame we measure the RTO from", HFILL }},
{ &hf_tcp_urgent_pointer,
{ "Urgent pointer", "tcp.urgent_pointer", FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_tcp_segment_overlap,
{ "Segment overlap", "tcp.segment.overlap", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"Segment overlaps with other segments", HFILL }},
{ &hf_tcp_segment_overlap_conflict,
{ "Conflicting data in segment overlap", "tcp.segment.overlap.conflict", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"Overlapping segments contained conflicting data", HFILL }},
{ &hf_tcp_segment_multiple_tails,
{ "Multiple tail segments found", "tcp.segment.multipletails", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"Several tails were found when reassembling the pdu", HFILL }},
{ &hf_tcp_segment_too_long_fragment,
{ "Segment too long", "tcp.segment.toolongfragment", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"Segment contained data past end of the pdu", HFILL }},
{ &hf_tcp_segment_error,
{ "Reassembling error", "tcp.segment.error", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
"Reassembling error due to illegal segments", HFILL }},
{ &hf_tcp_segment_count,
{ "Segment count", "tcp.segment.count", FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_tcp_segment,
{ "TCP Segment", "tcp.segment", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
NULL, HFILL }},
{ &hf_tcp_segments,
{ "Reassembled TCP Segments", "tcp.segments", FT_NONE, BASE_NONE, NULL, 0x0,
"TCP Segments", HFILL }},
{ &hf_tcp_reassembled_in,
{ "Reassembled PDU in frame", "tcp.reassembled_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
"The PDU that doesn't end in this segment is reassembled in this frame", HFILL }},
{ &hf_tcp_reassembled_length,
{ "Reassembled TCP length", "tcp.reassembled.length", FT_UINT32, BASE_DEC, NULL, 0x0,
"The total length of the reassembled payload", HFILL }},
{ &hf_tcp_option_kind,
{ "Kind", "tcp.option_kind", FT_UINT8,
BASE_DEC, VALS(tcp_option_kind_vs), 0x0, "This TCP option's kind", HFILL }},
{ &hf_tcp_option_len,
{ "Length", "tcp.option_len", FT_UINT8,
BASE_DEC, NULL, 0x0, "Length of this TCP option in bytes (including kind and length fields)", HFILL }},
{ &hf_tcp_options,
{ "TCP Options", "tcp.options", FT_BYTES,
BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_tcp_option_mss,
{ "TCP MSS Option", "tcp.options.mss", FT_NONE,
BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_tcp_option_mss_val,
{ "MSS Value", "tcp.options.mss_val", FT_UINT16,
BASE_DEC, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_wscale_shift,
{ "Shift count", "tcp.options.wscale.shift", FT_UINT8,
BASE_DEC, NULL, 0x0, "Logarithmically encoded power of 2 scale factor", HFILL}},
{ &hf_tcp_option_wscale_multiplier,
{ "Multiplier", "tcp.options.wscale.multiplier", FT_UINT8,
BASE_DEC, NULL, 0x0, "Multiply segment window size by this for scaled window size", HFILL}},
{ &hf_tcp_option_exp,
{ "TCP Option - Experimental", "tcp.options.experimental", FT_BYTES,
BASE_NONE, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_exp_data,
{ "Data", "tcp.options.experimental.data", FT_BYTES,
BASE_NONE, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_sack_perm,
{ "TCP SACK Permitted Option", "tcp.options.sack_perm",
FT_BOOLEAN,
BASE_NONE, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_sack,
{ "TCP SACK Option", "tcp.options.sack", FT_BOOLEAN,
BASE_NONE, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_sack_sle,
{"TCP SACK Left Edge", "tcp.options.sack_le", FT_UINT32,
BASE_DEC, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_sack_sre,
{"TCP SACK Right Edge", "tcp.options.sack_re", FT_UINT32,
BASE_DEC, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_echo,
{ "TCP Echo Option", "tcp.options.echo", FT_BOOLEAN,
BASE_NONE, NULL, 0x0, "TCP Sack Echo", HFILL}},
{ &hf_tcp_option_echo_reply,
{ "TCP Echo Reply Option", "tcp.options.echo_reply",
FT_BOOLEAN,
BASE_NONE, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_timestamp_tsval,
{ "Timestamp value", "tcp.options.timestamp.tsval", FT_UINT32,
BASE_DEC, NULL, 0x0, "Value of sending machine's timestamp clock", HFILL}},
{ &hf_tcp_option_timestamp_tsecr,
{ "Timestamp echo reply", "tcp.options.timestamp.tsecr", FT_UINT32,
BASE_DEC, NULL, 0x0, "Echoed timestamp from remote machine", HFILL}},
{ &hf_tcp_option_mptcp_subtype,
{ "Multipath TCP subtype", "tcp.options.mptcp.subtype", FT_UINT8,
BASE_DEC, VALS(mptcp_subtype_vs), 0xF0, NULL, HFILL}},
{ &hf_tcp_option_mptcp_version,
{ "Multipath TCP version", "tcp.options.mptcp.version", FT_UINT8,
BASE_DEC, NULL, 0x0F, NULL, HFILL}},
{ &hf_tcp_option_mptcp_flags,
{ "Multipath TCP flags", "tcp.options.mptcp.flags", FT_UINT8,
BASE_HEX, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_mptcp_B_flag,
{ "Backup flag", "tcp.options.mptcp.backup.flag", FT_UINT8,
BASE_DEC, NULL, 0x01, NULL, HFILL}},
{ &hf_tcp_option_mptcp_C_flag,
{ "Checksum required", "tcp.options.mptcp.checksumreq.flags", FT_UINT8,
BASE_DEC, NULL, 0x80, NULL, HFILL}},
{ &hf_tcp_option_mptcp_S_flag,
{ "Use HMAC-SHA1", "tcp.options.mptcp.sha1.flag", FT_UINT8,
BASE_DEC, NULL, 0x01, NULL, HFILL}},
{ &hf_tcp_option_mptcp_F_flag,
{ "DATA_FIN", "tcp.options.mptcp.datafin.flag", FT_UINT8,
BASE_DEC, NULL, 0x10, NULL, HFILL}},
{ &hf_tcp_option_mptcp_m_flag,
{ "Data Sequence Number is 8 octets", "tcp.options.mptcp.dseqn8.flag", FT_UINT8,
BASE_DEC, NULL, 0x08, NULL, HFILL}},
{ &hf_tcp_option_mptcp_M_flag,
{ "Data Sequence Number, Subflow Sequence Number, Data-level Length, Checksum present", "tcp.options.mptcp.dseqnpresent.flag", FT_UINT8,
BASE_DEC, NULL, 0x04, NULL, HFILL}},
{ &hf_tcp_option_mptcp_a_flag,
{ "Data ACK is 8 octets", "tcp.options.mptcp.dataack8.flag", FT_UINT8,
BASE_DEC, NULL, 0x02, NULL, HFILL}},
{ &hf_tcp_option_mptcp_A_flag,
{ "Data ACK is present", "tcp.options.mptcp.dataackpresent.flag", FT_UINT8,
BASE_DEC, NULL, 0x01, NULL, HFILL}},
{ &hf_tcp_option_mptcp_address_id,
{ "Multipath TCP Address ID", "tcp.options.mptcp.addrid", FT_UINT8,
BASE_DEC, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_mptcp_sender_key,
{ "Multipath TCP Sender's Key", "tcp.options.mptcp.sendkey", FT_UINT64,
BASE_DEC, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_mptcp_recv_key,
{ "Multipath TCP Receiver's Key", "tcp.options.mptcp.recvkey", FT_UINT64,
BASE_DEC, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_mptcp_recv_token,
{ "Multipath TCP Receiver's Token", "tcp.options.mptcp.recvtok", FT_UINT32,
BASE_DEC, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_mptcp_sender_rand,
{ "Multipath TCP Sender's Random Number", "tcp.options.mptcp.sendrand", FT_UINT32,
BASE_DEC, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_mptcp_sender_trunc_mac,
{ "Multipath TCP Sender's Truncated MAC", "tcp.options.mptcp.sendtruncmac", FT_UINT64,
BASE_DEC, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_mptcp_sender_mac,
{ "Multipath TCP Sender's MAC", "tcp.options.mptcp.sendmac", FT_UINT32,
BASE_DEC, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_mptcp_data_ack,
{ "Multipath TCP Data ACK", "tcp.options.mptcp.dataack", FT_UINT64,
BASE_DEC, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_mptcp_data_seq_no,
{ "Multipath TCP Data Sequence Number", "tcp.options.mptcp.dataseqno", FT_UINT64,
BASE_DEC, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_mptcp_subflow_seq_no,
{ "Multipath TCP Subflow Sequence Number", "tcp.options.mptcp.subflowseqno", FT_UINT32,
BASE_DEC, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_mptcp_data_lvl_len,
{ "Multipath TCP Data-level Length", "tcp.options.mptcp.datalvllen", FT_UINT16,
BASE_DEC, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_mptcp_checksum,
{ "Multipath TCP Checksum", "tcp.options.mptcp.checksum", FT_UINT16,
BASE_DEC, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_mptcp_ipver,
{ "Multipath TCP IPVer", "tcp.options.mptcp.ipver", FT_UINT8,
BASE_DEC, NULL, 0x0F, NULL, HFILL}},
{ &hf_tcp_option_mptcp_ipv4,
{ "Multipath TCP Address", "tcp.options.mptcp.ipv4", FT_IPv4,
BASE_NONE, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_mptcp_ipv6,
{ "Multipath TCP Address", "tcp.options.mptcp.ipv6", FT_IPv6,
BASE_NONE, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_mptcp_port,
{ "Multipath TCP Port", "tcp.options.mptcp.port", FT_UINT16,
BASE_DEC, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_cc,
{ "TCP CC Option", "tcp.options.cc", FT_BOOLEAN, BASE_NONE,
NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_ccnew,
{ "TCP CC New Option", "tcp.options.ccnew", FT_BOOLEAN,
BASE_NONE, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_ccecho,
{ "TCP CC Echo Option", "tcp.options.ccecho", FT_BOOLEAN,
BASE_NONE, NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_md5,
{ "TCP MD5 Option", "tcp.options.md5", FT_BOOLEAN, BASE_NONE,
NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_qs,
{ "TCP QS Option", "tcp.options.qs", FT_BOOLEAN, BASE_NONE,
NULL, 0x0, NULL, HFILL}},
{ &hf_tcp_option_scps,
{ "TCP SCPS Capabilities Option", "tcp.options.scps",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
NULL, HFILL}},
{ &hf_tcp_option_scps_vector,
{ "TCP SCPS Capabilities Vector", "tcp.options.scps.vector",
FT_UINT8, BASE_HEX, NULL, 0x0,
NULL, HFILL}},
{ &hf_tcp_option_scps_binding,
{ "Binding Space (Community) ID",
"tcp.options.scps.binding.id",
FT_UINT8, BASE_DEC, NULL, 0x0,
"TCP SCPS Extended Binding Space (Community) ID", HFILL}},
{ &hf_tcp_option_scps_binding_len,
{ "Extended Capability Length",
"tcp.options.scps.binding.len",
FT_UINT8, BASE_DEC, NULL, 0x0,
"TCP SCPS Extended Capability Length in bytes", HFILL}},
{ &hf_tcp_option_snack,
{ "TCP Selective Negative Acknowledgment Option",
"tcp.options.snack",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
NULL, HFILL}},
{ &hf_tcp_option_snack_offset,
{ "TCP SNACK Offset", "tcp.options.snack.offset",
FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL}},
{ &hf_tcp_option_snack_size,
{ "TCP SNACK Size", "tcp.options.snack.size",
FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL}},
{ &hf_tcp_option_snack_le,
{ "TCP SNACK Left Edge", "tcp.options.snack.le",
FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL}},
{ &hf_tcp_option_snack_re,
{ "TCP SNACK Right Edge", "tcp.options.snack.re",
FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL}},
{ &hf_tcp_scpsoption_flags_bets,
{ "Partial Reliability Capable (BETS)",
"tcp.options.scpsflags.bets", FT_BOOLEAN, 8,
TFS(&tfs_set_notset), 0x80, NULL, HFILL }},
{ &hf_tcp_scpsoption_flags_snack1,
{ "Short Form SNACK Capable (SNACK1)",
"tcp.options.scpsflags.snack1", FT_BOOLEAN, 8,
TFS(&tfs_set_notset), 0x40, NULL, HFILL }},
{ &hf_tcp_scpsoption_flags_snack2,
{ "Long Form SNACK Capable (SNACK2)",
"tcp.options.scpsflags.snack2", FT_BOOLEAN, 8,
TFS(&tfs_set_notset), 0x20, NULL, HFILL }},
{ &hf_tcp_scpsoption_flags_compress,
{ "Lossless Header Compression (COMP)",
"tcp.options.scpsflags.compress", FT_BOOLEAN, 8,
TFS(&tfs_set_notset), 0x10, NULL, HFILL }},
{ &hf_tcp_scpsoption_flags_nlts,
{ "Network Layer Timestamp (NLTS)",
"tcp.options.scpsflags.nlts", FT_BOOLEAN, 8,
TFS(&tfs_set_notset), 0x8, NULL, HFILL }},
{ &hf_tcp_scpsoption_flags_reserved,
{ "Reserved",
"tcp.options.scpsflags.reserved", FT_UINT8, BASE_DEC,
NULL, 0x7, NULL, HFILL }},
{ &hf_tcp_scpsoption_connection_id,
{ "Connection ID",
"tcp.options.scps.binding",
FT_UINT8, BASE_DEC, NULL, 0x0,
"TCP SCPS Connection ID", HFILL}},
{ &hf_tcp_option_user_to,
{ "TCP User Timeout", "tcp.options.user_to", FT_BOOLEAN,
BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_tcp_option_user_to_granularity,
{ "Granularity", "tcp.options.user_to_granularity", FT_BOOLEAN,
16, TFS(&tcp_option_user_to_granularity), 0x8000, "TCP User Timeout Granularity", HFILL}},
{ &hf_tcp_option_user_to_val,
{ "User Timeout", "tcp.options.user_to_val", FT_UINT16,
BASE_DEC, NULL, 0x7FFF, "TCP User Timeout Value", HFILL}},
{ &hf_tcp_option_rvbd_probe,
{ "Riverbed Probe", "tcp.options.rvbd.probe", FT_BOOLEAN,
BASE_NONE, NULL, 0x0, "RVBD TCP Probe Option", HFILL }},
{ &hf_tcp_option_rvbd_probe_type1,
{ "Type", "tcp.options.rvbd.probe.type1",
FT_UINT8, BASE_DEC, NULL, 0xF0, NULL, HFILL }},
{ &hf_tcp_option_rvbd_probe_type2,
{ "Type", "tcp.options.rvbd.probe.type2",
FT_UINT8, BASE_DEC, NULL, 0xFE, NULL, HFILL }},
{ &hf_tcp_option_rvbd_probe_version1,
{ "Version", "tcp.options.rvbd.probe.version",
FT_UINT8, BASE_DEC, NULL, 0x0F, NULL, HFILL }},
{ &hf_tcp_option_rvbd_probe_version2,
{ "Version", "tcp.options.rvbd.probe.version_raw",
FT_UINT8, BASE_DEC, NULL, 0x01, "Version 2 Raw Value", HFILL }},
{ &hf_tcp_option_rvbd_probe_optlen,
{ "Length", "tcp.options.rvbd.probe.len",
FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_tcp_option_rvbd_probe_prober,
{ "CSH IP", "tcp.options.rvbd.probe.prober",
FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_tcp_option_rvbd_probe_proxy,
{ "SSH IP", "tcp.options.rvbd.probe.proxy.ip",
FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_tcp_option_rvbd_probe_proxy_port,
{ "SSH Port", "tcp.options.rvbd.probe.proxy.port",
FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_tcp_option_rvbd_probe_appli_ver,
{ "Application Version", "tcp.options.rvbd.probe.appli_ver",
FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_tcp_option_rvbd_probe_client,
{ "Client IP", "tcp.options.rvbd.probe.client.ip",
FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_tcp_option_rvbd_probe_storeid,
{ "CFE Store ID", "tcp.options.rvbd.probe.storeid",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_tcp_option_rvbd_probe_flags,
{ "Probe Flags", "tcp.options.rvbd.probe.flags",
FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
{ &hf_tcp_option_rvbd_probe_flag_not_cfe,
{ "Not CFE", "tcp.options.rvbd.probe.flags.notcfe",
FT_BOOLEAN, 8, TFS(&tfs_set_notset), RVBD_FLAGS_PROBE_NCFE,
NULL, HFILL }},
{ &hf_tcp_option_rvbd_probe_flag_last_notify,
{ "Last Notify", "tcp.options.rvbd.probe.flags.last",
FT_BOOLEAN, 8, TFS(&tfs_set_notset), RVBD_FLAGS_PROBE_LAST,
NULL, HFILL }},
{ &hf_tcp_option_rvbd_probe_flag_probe_cache,
{ "Disable Probe Cache on CSH", "tcp.options.rvbd.probe.flags.probe",
FT_BOOLEAN, 8, TFS(&tfs_set_notset), RVBD_FLAGS_PROBE,
NULL, HFILL }},
{ &hf_tcp_option_rvbd_probe_flag_sslcert,
{ "SSL Enabled", "tcp.options.rvbd.probe.flags.ssl",
FT_BOOLEAN, 8, TFS(&tfs_set_notset), RVBD_FLAGS_PROBE_SSLCERT,
NULL, HFILL }},
{ &hf_tcp_option_rvbd_probe_flag_server_connected,
{ "SSH outer to server established", "tcp.options.rvbd.probe.flags.server",
FT_BOOLEAN, 8, TFS(&tfs_set_notset), RVBD_FLAGS_PROBE_SERVER,
NULL, HFILL }},
{ &hf_tcp_option_rvbd_trpy,
{ "Riverbed Transparency", "tcp.options.rvbd.trpy",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"RVBD TCP Transparency Option", HFILL }},
{ &hf_tcp_option_rvbd_trpy_flags,
{ "Transparency Options", "tcp.options.rvbd.trpy.flags",
FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }},
{ &hf_tcp_option_rvbd_trpy_flag_fw_rst_probe,
{ "Enable FW traversal feature", "tcp.options.rvbd.trpy.flags.fw_rst_probe",
FT_BOOLEAN, 16, TFS(&tfs_set_notset),
RVBD_FLAGS_TRPY_FW_RST_PROBE,
"Reset state created by probe on the nexthop firewall",
HFILL }},
{ &hf_tcp_option_rvbd_trpy_flag_fw_rst_inner,
{ "Enable Inner FW feature on All FWs", "tcp.options.rvbd.trpy.flags.fw_rst_inner",
FT_BOOLEAN, 16, TFS(&tfs_set_notset),
RVBD_FLAGS_TRPY_FW_RST_INNER,
"Reset state created by transparent inner on all firewalls"
" before passing connection through",
HFILL }},
{ &hf_tcp_option_rvbd_trpy_flag_fw_rst,
{ "Enable Transparency FW feature on All FWs", "tcp.options.rvbd.trpy.flags.fw_rst",
FT_BOOLEAN, 16, TFS(&tfs_set_notset),
RVBD_FLAGS_TRPY_FW_RST,
"Reset state created by probe on all firewalls before "
"establishing transparent inner connection", HFILL }},
{ &hf_tcp_option_rvbd_trpy_flag_chksum,
{ "Reserved", "tcp.options.rvbd.trpy.flags.chksum",
FT_BOOLEAN, 16, TFS(&tfs_set_notset),
RVBD_FLAGS_TRPY_CHKSUM, NULL, HFILL }},
{ &hf_tcp_option_rvbd_trpy_flag_oob,
{ "Out of band connection", "tcp.options.rvbd.trpy.flags.oob",
FT_BOOLEAN, 16, TFS(&tfs_set_notset),
RVBD_FLAGS_TRPY_OOB, NULL, HFILL }},
{ &hf_tcp_option_rvbd_trpy_flag_mode,
{ "Transparency Mode", "tcp.options.rvbd.trpy.flags.mode",
FT_BOOLEAN, 16, TFS(&trpy_mode_str),
RVBD_FLAGS_TRPY_MODE, NULL, HFILL }},
{ &hf_tcp_option_rvbd_trpy_src,
{ "Src SH IP Addr", "tcp.options.rvbd.trpy.src.ip",
FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_tcp_option_rvbd_trpy_dst,
{ "Dst SH IP Addr", "tcp.options.rvbd.trpy.dst.ip",
FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_tcp_option_rvbd_trpy_src_port,
{ "Src SH Inner Port", "tcp.options.rvbd.trpy.src.port",
FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_tcp_option_rvbd_trpy_dst_port,
{ "Dst SH Inner Port", "tcp.options.rvbd.trpy.dst.port",
FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_tcp_option_rvbd_trpy_client_port,
{ "Out of band connection Client Port", "tcp.options.rvbd.trpy.client.port",
FT_UINT16, BASE_DEC, NULL , 0x0, NULL, HFILL }},
{ &hf_tcp_pdu_time,
{ "Time until the last segment of this PDU", "tcp.pdu.time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
"How long time has passed until the last frame of this PDU", HFILL}},
{ &hf_tcp_pdu_size,
{ "PDU Size", "tcp.pdu.size", FT_UINT32, BASE_DEC, NULL, 0x0,
"The size of this PDU", HFILL}},
{ &hf_tcp_pdu_last_frame,
{ "Last frame of this PDU", "tcp.pdu.last_frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
"This is the last frame of the PDU starting in this segment", HFILL }},
{ &hf_tcp_ts_relative,
{ "Time since first frame in this TCP stream", "tcp.time_relative", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
"Time relative to first frame in this TCP stream", HFILL}},
{ &hf_tcp_ts_delta,
{ "Time since previous frame in this TCP stream", "tcp.time_delta", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
"Time delta from previous frame in this TCP stream", HFILL}},
{ &hf_tcp_proc_src_uid,
{ "Source process user ID", "tcp.proc.srcuid", FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL}},
{ &hf_tcp_proc_src_pid,
{ "Source process ID", "tcp.proc.srcpid", FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL}},
{ &hf_tcp_proc_src_uname,
{ "Source process user name", "tcp.proc.srcuname", FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL}},
{ &hf_tcp_proc_src_cmd,
{ "Source process name", "tcp.proc.srccmd", FT_STRING, BASE_NONE, NULL, 0x0,
"Source process command name", HFILL}},
{ &hf_tcp_proc_dst_uid,
{ "Destination process user ID", "tcp.proc.dstuid", FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL}},
{ &hf_tcp_proc_dst_pid,
{ "Destination process ID", "tcp.proc.dstpid", FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL}},
{ &hf_tcp_proc_dst_uname,
{ "Destination process user name", "tcp.proc.dstuname", FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL}},
{ &hf_tcp_proc_dst_cmd,
{ "Destination process name", "tcp.proc.dstcmd", FT_STRING, BASE_NONE, NULL, 0x0,
"Destination process command name", HFILL}},
{ &hf_tcp_data,
{ "TCP segment data", "tcp.data", FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL}}
};
static gint *ett[] = {
&ett_tcp,
&ett_tcp_flags,
&ett_tcp_options,
&ett_tcp_option_timestamp,
&ett_tcp_option_mptcp,
&ett_tcp_option_wscale,
&ett_tcp_option_sack,
&ett_tcp_option_scps,
&ett_tcp_option_scps_extended,
&ett_tcp_option_user_to,
&ett_tcp_option_exp,
&ett_tcp_option_sack_perm,
&ett_tcp_option_mss,
&ett_tcp_opt_rvbd_probe,
&ett_tcp_opt_rvbd_probe_flags,
&ett_tcp_opt_rvbd_trpy,
&ett_tcp_opt_rvbd_trpy_flags,
&ett_tcp_analysis_faults,
&ett_tcp_analysis,
&ett_tcp_timestamps,
&ett_tcp_segments,
&ett_tcp_segment,
&ett_tcp_checksum,
&ett_tcp_process_info
};
module_t *tcp_module;
proto_tcp = proto_register_protocol("Transmission Control Protocol",
"TCP", "tcp");
register_dissector("tcp", dissect_tcp, proto_tcp);
proto_register_field_array(proto_tcp, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
/* subdissector code */
subdissector_table = register_dissector_table("tcp.port",
"TCP port", FT_UINT16, BASE_DEC);
register_heur_dissector_list("tcp", &heur_subdissector_list);
/* Register configuration preferences */
tcp_module = prefs_register_protocol(proto_tcp, NULL);
prefs_register_bool_preference(tcp_module, "summary_in_tree",
"Show TCP summary in protocol tree",
"Whether the TCP summary line should be shown in the protocol tree",
&tcp_summary_in_tree);
prefs_register_bool_preference(tcp_module, "check_checksum",
"Validate the TCP checksum if possible",
"Whether to validate the TCP checksum",
&tcp_check_checksum);
prefs_register_bool_preference(tcp_module, "desegment_tcp_streams",
"Allow subdissector to reassemble TCP streams",
"Whether subdissector can request TCP streams to be reassembled",
&tcp_desegment);
prefs_register_bool_preference(tcp_module, "analyze_sequence_numbers",
"Analyze TCP sequence numbers",
"Make the TCP dissector analyze TCP sequence numbers to find and flag segment retransmissions, missing segments and RTT",
&tcp_analyze_seq);
prefs_register_bool_preference(tcp_module, "relative_sequence_numbers",
"Relative sequence numbers",
"Make the TCP dissector use relative sequence numbers instead of absolute ones. "
"To use this option you must also enable \"Analyze TCP sequence numbers\". ",
&tcp_relative_seq);
/* We now have tcp.window_size_value and tcp.window_size, so no need
* anymore for the preference window_scaling
*/
prefs_register_obsolete_preference(tcp_module, "window_scaling");
prefs_register_bool_preference(tcp_module, "track_bytes_in_flight",
"Track number of bytes in flight",
"Make the TCP dissector track the number on un-ACKed bytes of data are in flight per packet. "
"To use this option you must also enable \"Analyze TCP sequence numbers\". "
"This takes a lot of memory but allows you to track how much data are in flight at a time and graphing it in io-graphs",
&tcp_track_bytes_in_flight);
prefs_register_bool_preference(tcp_module, "calculate_timestamps",
"Calculate conversation timestamps",
"Calculate timestamps relative to the first frame and the previous frame in the tcp conversation",
&tcp_calculate_ts);
prefs_register_bool_preference(tcp_module, "try_heuristic_first",
"Try heuristic sub-dissectors first",
"Try to decode a packet using an heuristic sub-dissector before using a sub-dissector registered to a specific port",
&try_heuristic_first);
prefs_register_bool_preference(tcp_module, "ignore_tcp_timestamps",
"Ignore TCP Timestamps in summary",
"Do not place the TCP Timestamps in the summary line",
&tcp_ignore_timestamps);
prefs_register_bool_preference(tcp_module, "no_subdissector_on_error",
"Do not call subdissectors for error packets",
"Do not call any subdissectors for Retransmitted or OutOfOrder segments",
&tcp_no_subdissector_on_error);
register_init_routine(tcp_init);
}
void
proto_reg_handoff_tcp(void)
{
dissector_handle_t tcp_handle;
tcp_handle = find_dissector("tcp");
dissector_add_uint("ip.proto", IP_PROTO_TCP, tcp_handle);
data_handle = find_dissector("data");
tcp_tap = register_tap("tcp");
}
/*
* Editor modelines
*
* Local Variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* ex: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/
|