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
|
// WSDG Chapter Dissection
[#ChapterDissection]
== Packet Dissection
[#ChDissectWorks]
=== How packet dissection works
Each dissector decodes its part of the protocol and then hands off
decoding to subsequent dissectors for an encapsulated protocol.
Every dissection starts with the Frame dissector which dissects the
details of the capture file itself (e.g. timestamps). From there it passes the
data on to the lowest-level data dissector, e.g. the Ethernet dissector for
the Ethernet header. The payload is then passed on to the next dissector (e.g.
IP) and so on. At each stage, details of the packet are decoded and
displayed.
Dissectors can either be built-in to Wireshark or written as a self-registering
plugin (a shared library or DLL).
There is little difference in having your dissector as either a plugin
or built-in. You have limited function access through the ABI exposed
by functions declared as WS_DLL_PUBLIC.
The big benefit of writing a dissector as a plugin is that rebuilding
a plugin is much faster than rebuilding wireshark after editing a built-in
dissector.
As such, starting with a plugin often makes initial development quicker, while
the finished code may make more sense as a built-in dissector.
[NOTE]
.Read README.dissector
====
The file _doc/README.dissector_ contains detailed information about writing
a dissector. In many cases it is more up to date than this document.
====
[#ChDissectAdd]
=== Adding a basic dissector
Let’s step through adding a basic dissector. We'll start with the made up "foo"
protocol. It consists of the following basic items.
* A packet type - 8 bits. Possible values: 1 - initialisation, 2 - terminate, 3 - data.
* A set of flags stored in 8 bits. 0x01 - start packet, 0x02 - end packet, 0x04 - priority packet.
* A sequence number - 16 bits.
* An IPv4 address.
[#ChDissectSetup]
==== Setting up the dissector
The first decision you need to make is if this dissector will be a
built-in dissector and included in the main program, or a plugin.
Plugins are easier to write initially, so let’s start with that.
With a little care, the plugin can be converted into a built-in dissector.
For detailed information about plugins, see <<ChapterPlugins>>.
.Dissector Initialisation.
[source,c]
----
#include "config.h"
#include <epan/packet.h>
#define FOO_PORT 1234
static int proto_foo;
static dissector_handle_t foo_handle;
----
Let’s go through this a bit at a time. First we have some boilerplate
include files. These will be pretty constant to start with.
Then a `#define` for the UDP port that carries _foo_ traffic.
Next we have `proto_foo`, an int that stores our protocol handle.
This handle will be set when the dissector is registered within the main program.
It’s good practice to make all variables and functions that aren't exported
static to minimize name space pollution. This normally isn't a problem unless your
dissector gets so big that it spans multiple files.
Then there's `foo_handle`, a structure that holds a handle to
the function which will perform the actual dissection of our protocol.
This will be set at the same time as `proto_foo`.
Now that we have the basics in place to interact with the main program, we'll
start with two protocol dissector setup functions: `proto_register_XXX` and
`proto_reg_handoff_XXX`.
[#ChDissectSetupRegister]
===== Dissector registration
Each protocol must have a register function with the form "proto_register_XXX".
This function is used to register the protocol in Wireshark.
The code to call the register routines is generated automatically and is
called when Wireshark starts. In this example, the function is named
`proto_register_foo`.
.Dissector Registration.
[source,c]
----
void
proto_register_foo(void)
{
proto_foo = proto_register_protocol (
"FOO Protocol", /* protocol name */
"FOO", /* protocol short name */
"foo" /* protocol filter_name */
);
foo_handle = register_dissector_with_description (
"foo", /* dissector name */
"Foo Protocol", /* dissector description */
dissect_foo, /* dissector function */
proto_foo /* protocol being dissected */
);
}
----
`proto_register_foo` calls `proto_register_protocol()`, which takes a `name`,
`short name`, and `filter_name` for your protocol. The
name and short name are used in the "Preferences" and "Enabled protocols"
dialogs and the documentation's generated field name list. The
`filter_name` is used as the display filter name. `proto_register_protocol()`
returns a protocol handle, which can be used to refer to the protocol and
obtain a handle to the protocol's dissector.
[NOTE]
====
There is a distinction in Wireshark between the _protocol_ and its _dissector_.
A protocol may have several dissectors -- e.g. for handling protocol variants --
but they are all under the umbrella of the same protocol.
====
We haven't registered a dissector yet. We do that next with
`register_dissector_with_description()`. This function takes a short `name`
for the dissector, a human-readable `description` of the dissector,
a pointer to the dissection function (which we haven't created yet),
and the protocol handle we just created.
[TIP]
====
You can also register your dissector via `register_dissector()`, which takes
all the same arguments except the human-readable description string.
You will see older code (and documentation) which uses `create_dissector_handle()`
instead. This is discouraged, because the resulting handle will have no name,
and thus can't be called from other dissectors. There are cases when this
is what you want, but those are rare.
====
The dissector's short name is how you refer to the dissector from other places
in Wireshark, such as the "Decode As" feature, or when calling it from another
dissector.
In simple cases like ours it's okay to use the same name for the dissector as
for the protocol.
If you're writing dissectors for a more complex protocol,
then the dissector names should reflect their use.
For example, if our _foo_ protocol needs different dissection when it's
carried over UDP than when it's carried over TCP, then you might choose
`foo.udp` and `foo.tcp` for your dissector names.
(The short name for the protocol would still be `foo`.)
[#ChDissectSetupHandoff]
===== Dissector handoff
Next we need a handoff routine.
.Dissector Handoff.
[source,c]
----
void
proto_reg_handoff_foo(void)
{
dissector_add_uint("udp.port", FOO_PORT, foo_handle);
}
----
A handoff routine associates a protocol handler with the protocol's
traffic. We do this here by calling
`dissector_add_uint()` to associate traffic on UDP port `FOO_PORT` (1234)
with the foo protocol, so that Wireshark will call `dissect_foo()` when
it receives UDP traffic on port 1234.
[TIP]
====
Wireshark's dissector convention is to put `proto_register_foo()` and
`proto_reg_handoff_foo()` as the last two functions in the dissector source.
====
[#ChDissectSetupFunction]
===== Dissector function
The next step is to write the dissecting function, `dissect_foo()`.
We'll start with a basic placeholder.
.Dissection.
[source,c]
----
static int
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void *data _U_)
{
col_set_str(pinfo->cinfo, COL_PROTOCOL, "FOO");
/* Clear the info column */
col_clear(pinfo->cinfo,COL_INFO);
return tvb_captured_length(tvb);
}
----
`dissect_foo()` is called to dissect the packets presented to it. The packet data
is held in a special buffer referenced here as `tvb`. The packet_info structure
contains general data about the protocol and we can update
information here. The tree parameter is where the detail dissection takes place.
Note that the `\_U_` following `tree` and `data` signals to the compiler that the
parameters are unused, so that the compiler does not print a warning.
For now we'll do the minimum we can get away with. `col_set_str()` is used to set
Wireshark's Protocol column to "FOO" so everyone can see it’s being
recognised. The
only other thing we do is to clear out any data in the INFO column if it’s being
displayed.
At this point we have a basic dissector ready to compile and install.
The dissector doesn't do anything other than identify the protocol and label it.
Here is the dissector's complete code:
.Complete _packet-foo.c_ so far.
[source,c]
----
#include "config.h"
#include <epan/packet.h>
#define FOO_PORT 1234
static int proto_foo;
static dissector_handle_t foo_handle;
static int
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void *data _U_)
{
col_set_str(pinfo->cinfo, COL_PROTOCOL, "FOO");
/* Clear the info column */
col_clear(pinfo->cinfo,COL_INFO);
return tvb_captured_length(tvb);
}
void
proto_register_foo(void)
{
proto_foo = proto_register_protocol (
"FOO Protocol", /* protocol name */
"FOO", /* protocol short name */
"foo" /* protocol filter_name */
);
foo_handle = register_dissector_with_description (
"foo", /* dissector name */
"Foo Protocol", /* dissector description */
dissect_foo, /* dissector function */
proto_foo /* protocol being dissected */
);
}
void
proto_reg_handoff_foo(void)
{
dissector_add_uint("udp.port", FOO_PORT, foo_handle);
}
----
To compile this dissector and create a plugin a few support files
are required, besides the dissector source in _packet-foo.c_:
* _CMakeLists.txt_ - Contains the CMake file and version info for this plugin.
* _packet-foo.c_ - Your dissector source.
* _plugin.rc.in_ - Contains the DLL resource template for Windows. (optional)
Samples of these files are available in the gryphon plugin directory
(plugins/epan/gryphon).
If you copy the files from the gryphon plugin, _CMakeLists.txt_ will need
to be updated with the correct plugin name, version
info, and the relevant files to compile.
In the main top-level source directory, copy _CMakeListsCustom.txt.example_ to
_CMakeListsCustom.txt_ and add the path of your plugin to the list in
`CUSTOM_PLUGIN_SRC_DIR`.
Compile the dissector to a DLL or shared library and either run Wireshark from
the build directory as detailed in <<ChSrcRunFirstTime>> or copy the plugin
binary into the plugin directory of your Wireshark installation and run that.
[#ChDissectDetails]
==== Dissecting the protocol's details
Now that we have our basic dissector up and running, let’s do something with it.
The simplest thing to start with is labeling the payload. We can label the
payload by building a subtree to decode our results into.
This subtree will hold all the protocol's details and
helps keep things looking nice in the detailed display.
We add the new subtree with `proto_tree_add_item()`, as is depicted below:
.Plugin Packet Dissection.
[source,c]
----
static int
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
col_set_str(pinfo->cinfo, COL_PROTOCOL, "FOO");
/* Clear out stuff in the info column */
col_clear(pinfo->cinfo,COL_INFO);
proto_item *ti = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, ENC_NA);
return tvb_captured_length(tvb);
}
----
As the `FOO` protocol does not encapsulate another protocol, we
consume all of the tvb's data, from `0` to the end (`-1`).
The final parameter specifies the "encoding" and is set to
`ENC_NA` ("not applicable"), as the protocol item doesn't have
a specific encoding.
When we start dissecting the values of _fields_ in the protocol data,
we'll have to tell Wireshark about their encoding; for example, integers
can be big endian (`ENC_BIG_ENDIAN`) or little endian (`ENC_LITTLE_ENDIAN`).
After adding the call to
`proto_tree_add_item()`
, there should be a label `FOO` in the protocol's detailed display.
Selecting this label will highlight the remaining contents of the packet.
Now let’s go to the next step and add some protocol dissection. To do this
we'll need to construct tables to define which fields will be present in the
packet and to store the opened/closed state of the subtree. We'll
add these statically allocated arrays to the beginning of the file
(right after the dissector handle)
and name them
`hf_foo_pdu_type` ('hf' is short for 'header field') and `ett_foo`.
The arrays will then registered after the call to
`proto_register_protocol()` by calling `proto_register_field_array()`
and `proto_register_subtree_array()`:
.Registering data structures.
[source,c]
----
static int hf_foo_pdu_type;
static int ett_foo;
/* ... */
void
proto_register_foo(void)
{
static hf_register_info hf[] = {
{ &hf_foo_pdu_type,
{ "FOO PDU Type", "foo.type",
FT_UINT8, BASE_DEC,
NULL, 0x0,
NULL, HFILL }
}
};
/* Setup protocol subtree array */
static int *ett[] = {
&ett_foo
};
proto_foo = proto_register_protocol (
"FOO Protocol", /* protocol name */
"FOO", /* protocol short name */
"foo" /* protocol filter_name */
);
proto_register_field_array(proto_foo, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
foo_handle = register_dissector_with_description (
"foo", /* dissector name */
"Foo Protocol", /* dissector description */
dissect_foo, /* dissector function */
proto_foo /* protocol being dissected */
);
}
----
As you can see, a field `foo.type` was defined inside the array of
header fields.
Now we can dissect the `FOO PDU Type` (referenced as `foo.type`)
field in `dissect_foo()` by adding
the FOO Protocol's subtree with `proto_item_add_subtree()` and
then calling `proto_tree_add_item()` to add the field:
.Dissector starting to dissect the packets.
[source,c]
----
proto_item *ti = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, ENC_NA);
proto_tree *foo_tree = proto_item_add_subtree(ti, ett_foo);
proto_tree_add_item(foo_tree, hf_foo_pdu_type, tvb, 0, 1, ENC_BIG_ENDIAN);
----
As mentioned earlier, the foo protocol begins with an 8-bit `packet type`
which can have three possible values: 1 - initialisation, 2 - terminate, 3 - data.
Here's how we can add the packet details:
The `proto_item_add_subtree()` call has added a child node
to the protocol tree which is where we will do our detail dissection.
The expansion of this node is controlled by the `ett_foo`
variable. It remembers if the node should be expanded or not as you move
between packets. All subsequent dissection will be added to this tree,
as you can see from the next call.
A call to `proto_tree_add_item()` in the foo_tree,
this time using the `hf_foo_pdu_type` to control the formatting
of the item. The pdu type is one byte of data, starting at 0. We assume it is
in network order (also called big endian), so that is why we use `ENC_BIG_ENDIAN`.
For a 1-byte quantity, there is no order issue, but it is good practice to
make this the same as any multibyte fields that may be present, and as we will
see in the next section, this particular protocol uses network order.
If we look in detail at the `hf_foo_pdu_type` declaration in
the static array we can see the details of the definition.
----
static hf_register_info hf[] = {
{ &hf_foo_pdu_type,
{ "FOO PDU Type", "foo.type",
FT_UINT8, BASE_DEC,
NULL, 0x0,
NULL, HFILL }
}
};
----
* _hf_foo_pdu_type_ - The node's index.
* _FOO PDU Type_ - The item's label, as it will appear in the protocol tree.
* _foo.type_ - The item's abbreviated name, for use in the display filter
(e.g., `foo.type==1`).
* _FT_UINT8_ - The item's type: An 8bit unsigned integer.
This tallies with our call above where we tell it to only look at one byte.
* _BASE_DEC_ - For an integer type, this tells it to be printed as a decimal
number. It could be hexadecimal (BASE_HEX) or octal (BASE_OCT) if that made more sense.
We'll ignore the rest of the structure for now.
If you install this plugin and try it out, you'll see something that begins to look
useful.
Now let’s finish off dissecting the simple protocol. We need to add a few
more variables to the hfarray, and a couple more procedure calls.
.Wrapping up the packet dissection.
[source,c]
----
...
static int hf_foo_flags;
static int hf_foo_sequenceno;
static int hf_foo_initialip;
...
static int
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
int offset = 0;
...
proto_item *ti = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, ENC_NA);
proto_tree *foo_tree = proto_item_add_subtree(ti, ett_foo);
proto_tree_add_item(foo_tree, hf_foo_pdu_type, tvb, offset, 1, ENC_BIG_ENDIAN);
offset += 1;
proto_tree_add_item(foo_tree, hf_foo_flags, tvb, offset, 1, ENC_BIG_ENDIAN);
offset += 1;
proto_tree_add_item(foo_tree, hf_foo_sequenceno, tvb, offset, 2, ENC_BIG_ENDIAN);
offset += 2;
proto_tree_add_item(foo_tree, hf_foo_initialip, tvb, offset, 4, ENC_BIG_ENDIAN);
offset += 4;
...
return tvb_captured_length(tvb);
}
void
proto_register_foo(void) {
...
...
{ &hf_foo_flags,
{ "FOO PDU Flags", "foo.flags",
FT_UINT8, BASE_HEX,
NULL, 0x0,
NULL, HFILL }
},
{ &hf_foo_sequenceno,
{ "FOO PDU Sequence Number", "foo.seqn",
FT_UINT16, BASE_DEC,
NULL, 0x0,
NULL, HFILL }
},
{ &hf_foo_initialip,
{ "FOO PDU Initial IP", "foo.initialip",
FT_IPv4, BASE_NONE,
NULL, 0x0,
NULL, HFILL }
},
...
...
}
...
----
This dissects all the bits of this simple hypothetical protocol.
With these extra bits in place, the whole protocol is now dissected.
[TIP]
====
We've introduced a new variable `offset` into the mix to help keep track of where we are
in the packet dissection. This is easier to read, and change if needed,
than if you use absolute byte offsets in each call to `proto_tree_add_item()`.
====
==== Improving the dissection information
We can certainly improve the display of the protocol with a bit of extra data.
The first step is to add some text labels. Let’s start by labeling the packet
types. There is some useful support for this sort of thing by adding a couple of
extra things. First we add a simple table mapping an integer type to a readable name.
Place this code just after all your `hf_...` declarations:
.Naming the packet types.
[source,c]
----
static const value_string packettypenames[] = {
{ 1, "Initialise" },
{ 2, "Terminate" },
{ 3, "Data" },
{ 0, NULL }
};
----
This is a handy data structure that can be used to look up a name for a value.
There are routines to directly access this lookup table, but we don't need to
do that, as the support code already has that added in. We just have to give
these details to the definition of the field in `proto_register_foo()`.
We do this with the `VALS` macro.
.Adding Names to the protocol.
[source,c]
----
{ &hf_foo_pdu_type,
{ "FOO PDU Type", "foo.type",
FT_UINT8, BASE_DEC,
VALS(packettypenames), 0x0,
NULL, HFILL }
}
----
This helps in deciphering the packets, and we can do a similar thing for the
flags structure. For this we need to add some more data to the table though.
.Adding Flags to the protocol.
[source,c]
----
#define FOO_START_FLAG 0x01
#define FOO_END_FLAG 0x02
#define FOO_PRIORITY_FLAG 0x04
...
static int hf_foo_startflag;
static int hf_foo_endflag;
static int hf_foo_priorityflag;
...
static int
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
...
...
static int* const bits[] = {
&hf_foo_startflag,
&hf_foo_endflag,
&hf_foo_priorityflag,
NULL
};
proto_tree_add_bitmask(foo_tree, tvb, offset, hf_foo_flags, ett_foo, bits, ENC_BIG_ENDIAN);
offset += 1;
...
...
return tvb_captured_length(tvb);
}
void
proto_register_foo(void) {
...
...
{ &hf_foo_startflag,
{ "FOO PDU Start Flags", "foo.flags.start",
FT_BOOLEAN, 8,
NULL, FOO_START_FLAG,
NULL, HFILL }
},
{ &hf_foo_endflag,
{ "FOO PDU End Flags", "foo.flags.end",
FT_BOOLEAN, 8,
NULL, FOO_END_FLAG,
NULL, HFILL }
},
{ &hf_foo_priorityflag,
{ "FOO PDU Priority Flags", "foo.flags.priority",
FT_BOOLEAN, 8,
NULL, FOO_PRIORITY_FLAG,
NULL, HFILL }
},
...
...
}
...
----
Some things to note here. For the flags, since each bit is a different flag, we use
the type `FT_BOOLEAN`, as the flag is either on or off. Second, we include the flag
mask in the 7th field of the data, which allows Wireshark to mask the relevant bit.
We've also changed the fifth field to 8, to indicate that we are looking at an 8 bit
quantity when the flags are extracted. Then finally we add the extra constructs
to the dissection routine.
This is starting to look fairly full featured now, but there are a couple of
other things we can do to make things look even more pretty. At the moment our
dissection shows the packets as "Foo Protocol" which whilst correct is a little
uninformative. We can enhance this by adding a little more detail.
First, let’s
get hold of the actual value of the protocol type. We can use the handy function
`tvb_get_uint8()` to do this. With this value in hand, there are a couple of
things we can do. We can set the INFO column of the non-detailed view to
show what sort of PDU it is -- which is extremely helpful when looking at
protocol traces. Second, we can also display this information in the dissection
window.
.Enhancing the display.
[source,c]
----
static int
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
int offset = 0;
uint8_t packet_type = tvb_get_uint8(tvb, 0);
col_set_str(pinfo->cinfo, COL_PROTOCOL, "FOO");
/* Clear out stuff in the info column */
col_clear(pinfo->cinfo, COL_INFO);
col_add_fstr(pinfo->cinfo, COL_INFO, "Type %s",
val_to_str(pinfo->pool, packet_type, packettypenames, "Unknown (0x%02x)"));
proto_item *ti = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, ENC_NA);
proto_item_append_text(ti, ", Type %s",
val_to_str(packet_type, packettypenames, "Unknown (0x%02x)"));
proto_tree *foo_tree = proto_item_add_subtree(ti, ett_foo);
proto_tree_add_item(foo_tree, hf_foo_pdu_type, tvb, offset, 1, ENC_BIG_ENDIAN);
offset += 1;
...
return tvb_captured_length(tvb);
}
----
So here, after grabbing the value of the first 8 bits, we use it with one of the
built-in utility routines `val_to_str()`, to lookup the value. If the value
isn't found we provide a fallback which just prints the value in hex. We use
this twice, once in the INFO field of the columns -- if it’s displayed -- and
once to append this data to the base of our dissecting tree.
[#ChDissectExpertInfo]
=== How to add an expert item
A dissector showing the protocol fields and interpretation of their values is
very informative. It can be even more helpful if the dissector can draw your
attention to fields where something noteworthy can be seen. This can be something
as simple as the start flag of a session, or something more severe as an invalid
value.
Here we take our dissector for `FOO` and add an expert item for the sequence
number being zero (assuming that's a noteworthy thing for this protocol).
.Expert item setup.
[source,c]
----
#include <epan/expert.h>
static expert_field ei_foo_seqn_zero;
/* ... */
void
proto_register_foo(void)
{
/* ... */
expert_module_t* expert_foo;
/* ... */
static ei_register_info ei[] = {
{
&ei_foo_seqn_zero,
{ "foo.seqn_zero", PI_SEQUENCE, PI_CHAT,
"Sequence number is zero", EXPFILL }
}
};
/* ... */
expert_foo = expert_register_protocol(proto_foo);
expert_register_field_array(expert_foo, ei, array_length(ei));
}
----
Let's go through this step by step. The data structures and functions needed for
expert items are found in `epan/expert.h`, so we have to include that file.
Next we have to allocate an `expert_field` structure for every type of expert item
we would like to add to the dissection.
We typically declare these right after the `ett_...` tree handle declarations.
Now we have to register with the protocol we are providing expert info for. Since
we already have a function to register our protocol, we add the expert info
registration there too. This is done by calling `expert_register_protocol()` with
the handle for the protocol we received earlier in this function.
Next we need to register an array of definitions of expert items that we would
like to add to the dissection. This array, not unlike the array of header fields
before, contains all the data the dissection engine needs to create and handle
the expert items.
The expert item definition consists of a pointer to the `expert_field` structure
we defined before and a structure with data elements of the expert item itself.
* _"foo.seqn_zero"_ - The display filter for the expert item
* _PI_SEQUENCE_ - The group to which the expert item belongs
* _PI_CHAT_ - The severity of the expert item
* _"Sequence number is zero"_ - The text string added to the dissection
We'll ignore the rest of the structure for now.
To keep an overview of lots of expert items it helps to categorize them into groups.
Currently there are several types of groups defined, e.g. `checksum`, `sequence`,
`protocol`, etc. All these are defined in the epan/proto.h header file.
Not every noteworthy field value is of equal severity. The start of a session
is nice to know, while an invalid value may be significant error in the protocol.
To differentiate between these severties the expert item is assigned one of them:
`comment`, `chat`, `note`, `warn` or `error`. Try to choose the lowest one which
is suitable. The topic you're currently working on seems probably more important
than it will look like in a few weeks.
With the expert item array setup, we add this to the dissection engine with a
call to `expert_register_field_array()`.
Now that all information of the expert item is defined and registered it's time
to actually add the expert item to the dissection.
.Expert item use.
[source,c]
----
static int
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
uint32_t sequenceno = 0xFFFF;
/* ... */
ti = proto_tree_add_item_ret_uint(foo_tree, hf_foo_sequenceno,
tvb, offset, 2, ENC_BIG_ENDIAN, &sequenceno);
if (sequenceno == 0) {
expert_add_info(pinfo, ti, &ei_foo_seqn_zero);
}
/* ... */
}
----
There's been a slight alteration to the function used to add the sequence number
dissection. Firstly, `proto_tree_add_item()` has changed to
`proto_tree_add_item_ret_uint()` so we can store the actual value of the field in the variable
`sequenceno`.
We can now use the value of this field to determine wether to add
the expert item.
Secondly, the `proto_item` returned by the function is saved in previously
defined variable `ti`.
Adding the expert item is simply done by calling `expert_add_info()` with reference
to the `packet_info` structure, the proto item `ti` to add the expert item to, and
the previously defined and registered expert item information.
[#ChDissectTransformed]
=== How to handle transformed data
Some protocols do clever things with data. They might possibly
encrypt the data, or compress data, or part of it. If you know
how these steps are taken it is possible to reverse them within the
dissector.
As encryption can be tricky, let’s consider the case of compression.
These techniques can also work for other transformations of data,
where some step is required before the data can be examined.
What basically needs to happen here, is to identify the data that needs
conversion, take that data and transform it into a new stream, and then call a
dissector on it. Often this needs to be done "on-the-fly" based on clues in the
packet. Sometimes this needs to be used in conjunction with other techniques,
such as packet reassembly. The following shows a technique to achieve this
effect.
.Decompressing data packets for dissection.
[source,c]
----
uint8_t flags = tvb_get_uint8(tvb, offset);
offset ++;
if (flags & FLAG_COMPRESSED) { /* the remainder of the packet is compressed */
uint16_t orig_size = tvb_get_ntohs(tvb, offset);
unsigned char *decompressed_buffer = (unsigned char*)wmem_alloc(pinfo->pool, orig_size);
offset += 2;
decompress_packet(tvb_memdup(pinfo->pool, tvb, offset, -1),
tvb_captured_length_remaining(tvb, offset),
decompressed_buffer, orig_size);
/* Now re-setup the tvb buffer to have the new data */
next_tvb = tvb_new_child_real_data(tvb, decompressed_buffer, orig_size, orig_size);
add_new_data_source(pinfo, next_tvb, "Decompressed Data");
} else {
next_tvb = tvb_new_subset_remaining(tvb, offset);
}
offset = 0;
/* process next_tvb from here on */
----
The first step here is to recognise the compression. In this case a flag byte
alerts us to the fact the remainder of the packet is compressed. Next we
retrieve the original size of the packet, which in this case is conveniently
within the protocol. If it’s not, it may be part of the compression routine to
work it out for you, in which case the logic would be different.
So armed with the size, a buffer is allocated to receive the uncompressed data
using `wmem_alloc()` in `pinfo->pool` memory, and the packet is decompressed into
it. The `tvb_memdup()` function is useful to get a copy of the raw data of
the packet from the offset onwards. In this case the decompression routine also
needs to know the length, which is given by the
`tvb_captured_length_remaining()` function.
Next we build a new tvb buffer from this data, using the
`tvb_new_child_real_data()` call. This data is a child of our original data, so
calling this function also acknowledges that. No need to call
`tvb_set_free_cb()` as the `pinfo->pool` was used (the memory block will be
automatically freed when the pinfo pool lifetime expires). Finally we add this
tvb as a new data source, so that the detailed display can show the
decompressed bytes as well as the original.
After this has been set up the remainder of the dissector can dissect the buffer
next_tvb, as it’s a new buffer the offset needs to be 0 as we start again from
the beginning of this buffer. To make the rest of the dissector work regardless
of whether compression was involved or not, in the case that compression was not
signaled, we use `tvb_new_subset_remaining()` to deliver us a new buffer based
on the old one but starting at the current offset, and extending to the end.
This makes dissecting the packet from this point on exactly the same regardless
of compression.
[#ChDissectReassemble]
=== How to reassemble split packets
Some protocols have times when they have to split a large packet across
multiple other packets. In this case the dissection can't be carried out correctly
until you have all the data. The first packet doesn't have enough data,
and the subsequent packets don't have the expect format.
To dissect these packets you need to wait until all the parts have
arrived and then start the dissection.
The following sections will guide you through two common cases. For a
description of all possible functions, structures and parameters, see
_epan/reassemble.h_.
[#ChDissectReassembleUdp]
==== How to reassemble split UDP packets
As an example, let’s examine a protocol that is layered on top of UDP that
splits up its own data stream. If a packet is bigger than some given size, it
will be split into chunks, and somehow signaled within its protocol.
To deal with such streams, we need several things to trigger from. We need to
know that this packet is part of a multi-packet sequence. We need to know how
many packets are in the sequence. We also need to know when we have all the
packets.
For this example we'll assume there is a simple in-protocol signaling mechanism
to give details. A flag byte that signals the presence of a multi-packet
sequence and also the last packet, followed by an ID of the sequence and a
packet sequence number.
----
msg_pkt ::= SEQUENCE {
.....
flags ::= SEQUENCE {
fragment BOOLEAN,
last_fragment BOOLEAN,
.....
}
msg_id INTEGER(0..65535),
frag_id INTEGER(0..65535),
.....
}
----
.Reassembling fragments - Part 1
[source,c]
----
#include <epan/reassemble.h>
...
save_fragmented = pinfo->fragmented;
flags = tvb_get_uint8(tvb, offset); offset++;
if (flags & FL_FRAGMENT) { /* fragmented */
tvbuff_t* new_tvb = NULL;
fragment_data *frag_msg = NULL;
uint16_t msg_seqid = tvb_get_ntohs(tvb, offset); offset += 2;
uint16_t msg_num = tvb_get_ntohs(tvb, offset); offset += 2;
pinfo->fragmented = true;
frag_msg = fragment_add_seq_check(msg_reassembly_table,
tvb, offset, pinfo,
msg_seqid, NULL, /* ID for fragments belonging together */
msg_num, /* fragment sequence number */
tvb_captured_length_remaining(tvb, offset), /* fragment length - to the end */
flags & FL_FRAG_LAST); /* More fragments? */
----
We start by saving the fragmented state of this packet, so we can restore it
later. Next comes some protocol specific stuff, to dig the fragment data out of
the stream if it’s present. Having decided it is present, we let the function
`fragment_add_seq_check()` do its work. We need to provide this with a certain
amount of parameters:
* The `msg_reassembly_table` table is for bookkeeping and is described later.
* The tvb buffer we are dissecting.
* The offset where the partial packet starts.
* The provided packet info.
* The sequence number of the fragment stream. There may be several streams of
fragments in flight, and this is used to key the relevant one to be used for
reassembly.
* Optional additional data for identifying the fragment. Can be set to `NULL`
(as is done in the example) for most dissectors.
* msg_num is the packet number within the sequence.
* The length here is specified as the rest of the tvb as we want the rest of the packet data.
* Finally a parameter that signals if this is the last fragment or not. This
might be a flag as in this case, or there may be a counter in the protocol.
.Reassembling fragments part 2
[source,c]
----
new_tvb = process_reassembled_data(tvb, offset, pinfo,
"Reassembled Message", frag_msg, &msg_frag_items,
NULL, msg_tree);
if (frag_msg) { /* Reassembled */
col_append_str(pinfo->cinfo, COL_INFO,
" (Message Reassembled)");
} else { /* Not last packet of reassembled Short Message */
col_append_fstr(pinfo->cinfo, COL_INFO,
" (Message fragment %u)", msg_num);
}
if (new_tvb) { /* take it all */
next_tvb = new_tvb;
} else { /* make a new subset */
next_tvb = tvb_new_subset_remaining(tvb, offset);
}
}
else { /* Not fragmented */
next_tvb = tvb_new_subset_remaining(tvb, offset);
}
.....
pinfo->fragmented = save_fragmented;
----
Having passed the fragment data to the reassembly handler, we can now check if
we have the whole message. If there is enough information, this routine will
return the newly reassembled data buffer.
After that, we add a couple of informative messages to the display to show that
this is part of a sequence. Then a bit of manipulation of the buffers and the
dissection can proceed. Normally you will probably not bother dissecting further
unless the fragments have been reassembled as there won't be much to find.
Sometimes the first packet in the sequence can be partially decoded though if
you wish.
Now the mysterious data we passed into the `fragment_add_seq_check()`.
.Reassembling fragments - Initialisation
[source,c]
----
static reassembly_table reassembly_table;
static void
proto_register_msg(void)
{
reassembly_table_register(&msg_reassemble_table,
&addresses_ports_reassembly_table_functions);
}
----
First a `reassembly_table` structure is declared and initialised in the protocol
initialisation routine. The second parameter specifies the functions that should
be used for identifying fragments. We will use
`addresses_ports_reassembly_table_functions` in order to identify fragments by
the given sequence number (`msg_seqid`), the source and destination addresses
and ports from the packet.
Following that, a `fragment_items` structure is allocated and filled in with a
series of ett items, hf data items, and a string tag. The ett and hf values
should be included in the relevant tables like all the other variables your
protocol may use. The hf variables need to be placed in the structure something
like the following. Of course the names may need to be adjusted.
.Reassembling fragments - Data
[source,c]
----
...
static int hf_msg_fragments;
static int hf_msg_fragment;
static int hf_msg_fragment_overlap;
static int hf_msg_fragment_overlap_conflicts;
static int hf_msg_fragment_multiple_tails;
static int hf_msg_fragment_too_long_fragment;
static int hf_msg_fragment_error;
static int hf_msg_fragment_count;
static int hf_msg_reassembled_in;
static int hf_msg_reassembled_length;
...
static int ett_msg_fragment;
static int ett_msg_fragments;
...
static const fragment_items msg_frag_items = {
/* Fragment subtrees */
&ett_msg_fragment,
&ett_msg_fragments,
/* Fragment fields */
&hf_msg_fragments,
&hf_msg_fragment,
&hf_msg_fragment_overlap,
&hf_msg_fragment_overlap_conflicts,
&hf_msg_fragment_multiple_tails,
&hf_msg_fragment_too_long_fragment,
&hf_msg_fragment_error,
&hf_msg_fragment_count,
/* Reassembled in field */
&hf_msg_reassembled_in,
/* Reassembled length field */
&hf_msg_reassembled_length,
/* Tag */
"Message fragments"
};
...
static hf_register_info hf[] =
{
...
{&hf_msg_fragments,
{"Message fragments", "msg.fragments",
FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } },
{&hf_msg_fragment,
{"Message fragment", "msg.fragment",
FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
{&hf_msg_fragment_overlap,
{"Message fragment overlap", "msg.fragment.overlap",
FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },
{&hf_msg_fragment_overlap_conflicts,
{"Message fragment overlapping with conflicting data",
"msg.fragment.overlap.conflicts",
FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },
{&hf_msg_fragment_multiple_tails,
{"Message has multiple tail fragments",
"msg.fragment.multiple_tails",
FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },
{&hf_msg_fragment_too_long_fragment,
{"Message fragment too long", "msg.fragment.too_long_fragment",
FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },
{&hf_msg_fragment_error,
{"Message defragmentation error", "msg.fragment.error",
FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
{&hf_msg_fragment_count,
{"Message fragment count", "msg.fragment.count",
FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
{&hf_msg_reassembled_in,
{"Reassembled in", "msg.reassembled.in",
FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
{&hf_msg_reassembled_length,
{"Reassembled length", "msg.reassembled.length",
FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
...
static int *ett[] =
{
...
&ett_msg_fragment,
&ett_msg_fragments
...
----
These hf variables are used internally within the reassembly routines to make
useful links, and to add data to the dissection. It produces links from one
packet to another, such as a partial packet having a link to the fully
reassembled packet. Likewise there are back pointers to the individual packets
from the reassembled one. The other variables are used for flagging up errors.
[#TcpDissectPdus]
==== How to reassemble split TCP Packets
A dissector gets a `tvbuff_t` pointer which holds the payload
of a TCP packet. This payload contains the header and data
of your application layer protocol.
When dissecting an application layer protocol you cannot assume
that each TCP packet contains exactly one application layer message.
One application layer message can be split into several TCP packets.
You also cannot assume that a TCP packet contains only one application layer message
and that the message header is at the start of your TCP payload.
More than one message can be transmitted in one TCP packet,
so that a message can start at an arbitrary position within a packet.
This sounds complicated, but there is a simple solution.
`tcp_dissect_pdus()` does all this tcp packet reassembling for you.
This function is implemented in _epan/dissectors/packet-tcp.h_.
.Reassembling TCP fragments
[source,c]
----
#include "config.h"
#include <epan/packet.h>
#include <epan/prefs.h>
#include "packet-tcp.h"
...
#define FRAME_HEADER_LEN 8
/* This method dissects fully reassembled messages */
static int
dissect_foo_message(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_)
{
/* TODO: implement your dissecting code */
return tvb_captured_length(tvb);
}
/* determine PDU length of protocol foo */
static unsigned
get_foo_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
{
/* TODO: change this to your needs */
return (unsigned)tvb_get_ntohl(tvb, offset+4); /* e.g. length is at offset 4 */
}
/* The main dissecting routine */
static int
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
tcp_dissect_pdus(tvb, pinfo, tree, true, FRAME_HEADER_LEN,
get_foo_message_len, dissect_foo_message, data);
return tvb_captured_length(tvb);
}
...
----
As you can see this is really simple. Just call `tcp_dissect_pdus()` in your
main dissection routine and move you message parsing code into another function.
This function gets called whenever a message has been reassembled.
The parameters tvb, pinfo, tree and data are just handed over to
`tcp_dissect_pdus()`. The 4th parameter is a flag to indicate if the data should
be reassembled or not. This could be set according to a dissector preference as
well. Parameter 5 indicates how much data has to be present for your dissector to be
able to determine the length of the foo message. Parameter 6 is a function
pointer to a method that returns the actual length of the foo message.
It gets called when at least the
number of bytes given in the previous parameter is available. Parameter 7 is a
function pointer to your real message dissector. Parameter 8 is the data
passed in from parent dissector.
Protocols which need more data before the message length can be determined can
return zero from their message-length function.
Other values smaller than the fixed length will result in an exception.
[#ChDissectTap]
=== How to tap protocols
Adding a Tap interface to a protocol allows it to do some useful things.
In particular you can produce protocol statistics from the tap interface.
A tap is basically a way of allowing other items to see what’s happening as
a protocol is dissected. A tap is registered with the main program, and
then called on each dissection. Some arbitrary protocol specific data
is provided with the routine that can be used.
To create a tap, you first need to register a tap. A tap is registered with an
integer handle, and registered with the routine `register_tap()`. This takes a
string name with which to find it again.
.Initialising a tap
[source,c]
----
#include <epan/packet.h>
#include <epan/tap.h>
static int foo_tap;
void proto_register_foo(void)
{
...
foo_tap = register_tap("foo");
----
Whilst you can program a tap without protocol specific data, it is generally not
very useful. Therefore it’s a good idea to declare a structure that can be
passed through the tap. This needs to be allocated in packet scope as it will be used
after the dissection routine has returned. It’s generally best to pick out some
generic parts of the protocol you are dissecting into the tap data. A packet
type, a priority or a status code maybe. The structure really needs to be
included in a header file so that it can be included by other components that
want to listen in to the tap.
Once you have these defined, it’s simply a case of populating the protocol
specific structure and then calling `tap_queue_packet`, probably as the last part
of the dissector.
.Calling a protocol tap
[source,c]
----
struct FooTap {
int packet_type;
int priority;
...
};
static int
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
...
struct FooTap *fooinfo = wmem_new0(pinfo->pool, struct FooTap);
fooinfo->packet_type = tvb_get_uint8(tvb, 0);
fooinfo->priority = tvb_get_ntohs(tvb, 8);
...
tap_queue_packet(foo_tap, pinfo, fooinfo);
return tvb_captured_length(tvb);
}
----
[TIP]
====
Allocate your structure using `wmem_new0()`, so it sets all values of your structure
to zero. This way, if you add members later but forget to initialize them, they will
have a consistent value, making troubleshooting easier.
====
This now enables those interested parties to listen in on the details
of this protocol conversation.
[#ChDissectStats]
==== How to produce protocol statistics (stats)
Given that you have a tap interface for the protocol, you can use this
to produce some interesting statistics (well presumably interesting!) from
protocol traces.
This can be done in a separate plugin, or in the same plugin that is
doing the dissection. The latter scheme is better, as the tap and stats
module typically rely on sharing protocol specific data, which might get out
of step between two different plugins.
Here is a mechanism to produce statistics from the above TAP interface.
.Initialising a stats interface
[source,c]
----
#include <epan/stats_tree.h>
void proto_reg_handoff_foo(void) {
...
stats_tree_register("foo", "foo", "Foo" STATS_TREE_MENU_SEPARATOR "Packet Types", 0,
foo_stats_tree_packet, foo_stats_tree_init, NULL);
}
----
The interface entry point, `proto_reg_handoff_foo()`,
calls the `stats_tree_register()` function, which takes three
strings, an integer, and three callback functions:
. This is the tap name that was registered using `register_tap()`.
. An abbreviation of the stats name.
. The name of the stats module. `STATS_TREE_MENU_SEPARATOR` can be used to make sub menus.
. Flags for per-packet callback, taken from `epan/stats_tree.h`.
. The function that will called to generate the stats.
. A function that can be called to initialise the stats data.
. A function that will be called to clean up the stats data.
In this case we only need the first two functions, as there is nothing specific to clean up.
[NOTE]
====
If you are registering statistics from a plugin, then your plugin should have
a plugin interface entry point called `plugin_register_tap_listener()`,
which should call `stats_tree_register_plugin()` instead of `stats_tree_register()`.
====
.Initialising a stats session
[source,c]
----
static const uint8_t* st_str_packets = "Total Packets";
static const uint8_t* st_str_packet_types = "FOO Packet Types";
static int st_node_packets = -1;
static int st_node_packet_types = -1;
static void foo_stats_tree_init(stats_tree* st)
{
st_node_packets = stats_tree_create_node(st, st_str_packets, 0, STAT_DT_INT, true);
st_node_packet_types = stats_tree_create_pivot(st, st_str_packet_types, st_node_packets);
}
----
In this case we create a new tree node, to handle the total packets,
and as a child of that we create a pivot table to handle the stats about
different packet types.
.Generating the stats
[source,c]
----
static tap_packet_status foo_stats_tree_packet(stats_tree* st, packet_info* pinfo, epan_dissect_t* edt, const void* p, tap_flags_t flags)
{
struct FooTap *pi = (struct FooTap *)p;
tick_stat_node(st, st_str_packets, 0, false);
stats_tree_tick_pivot(st, st_node_packet_types,
val_to_str(pinfo->pool, pi->packet_type, packettypenames, "Unknown packet type (%d)"));
return TAP_PACKET_REDRAW;
}
----
In this case the processing of the stats is quite simple. First we call the
`tick_stat_node` for the `st_str_packets` packet node, to count packets. Then a
call to `stats_tree_tick_pivot()` on the `st_node_packet_types` subtree allows
us to record statistics by packet type.
[NOTE]
====
Notice that stats trees and pivots are identified by their name string,
_not_ by the identifier returned by
`stats_tree_create_node()`/`stats_tree_create_pivot()`.
====
[#ChDissectFollowStream]
==== How to follow protocol streams
Now that you’re familiar with how taps work, you can also use them to allow your
dissector to follow *streams*, if your protocol has that concept, using the
menu:Analyze[Follow] menu or `tshark -z follow`.
[NOTE]
====
You cannot re-use a previously defined tap for this purpose.
You will need to define a separate tap.
====
.Registering a follow tap
[source,c]
----
#include <epan/packet.h>
#include <epan/tap.h>
#include <epan/follow.h>
static int foo_follow_tap;
static int
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
...
/* We only tap the packet if we're being asked to. */
if (have_tap_listener(foo_follow_tap)) {
/* For a follow tap, the userdata argument is
* the tvbuff containing your protocol's payload data.
* That may *not* be the entire tvbuff, as it is here.
*/
tap_queue_packet(foo_follow_tap, pinfo, tvb);
}
return tvb_captured_length(tvb);
}
void proto_register_foo(void)
{
...
foo_follow_tap = register_tap("foo_follow");
register_follow_stream(proto_foo, "foo_follow",
foo_follow_conv_filter,
foo_follow_index_filter,
foo_follow_address_filter,
foo_port_to_display,
foo_follow_tap_listener,
get_foo_stream_count,
foo_get_substream_id);
----
The arguments to `register_follow_stream()` are an integer, a string,
and several callback functions:
. The integer protocol identifier returned from `proto_register_protocol()`.
. The string name of your dedicated follow tap.
. A callback function which will return a display filter string.
The filter should follow a stream based on the *conversation*
that the current packet belongs to.
. A callback function which will return a display filter string.
The filter should follow a stream based on its *stream index*,
if your protocol has such a concept.
. A callback function which will return a display filter string.
The filter should follow a stream based on its *address/port pairs*.
. A callback function that will take a port number and resolve it
to a string identifying the service, or else return the port
as a string.
. A callback function that will handle reading the tvbuff information
provided by the tap when it is called.
. A callback function that will return the total number of unique
streams of your protocol in the current capture file.
May be `NULL`.
. A callback function that will identify whether the current packet
contains a substream of your protocol, if it has such a concept.
May be `NULL`.
If your protocol is carried over TCP or UDP, and its streams can be found
using IP addresses and ports, then you may be able to use some or all of
the standard callback functions defined for this purpose:
.Standard callbacks for following streams
[cols="1,4,4"]
|====
|Argument|TCP Function |UDP Function
|3 |`tcp_follow_conv_filter` |`udp_follow_conv_filter`
|4 |`tcp_follow_index_filter` |`udp_follow_index_filter`
|5 |`tcp_follow_address_filter`|`udp_follow_address_filter`
|6 |`tcp_port_to_display` |`udp_port_to_display`
|7 |`follow_tvb_tap_listener` |`follow_tvb_tap_listener`
|8 |`get_tcp_stream_count` |`get_udp_stream_count`
|9 |`NULL` |`NULL`
|====
If your protocol is not carried over TCP or UDP, or if you have more
complex needs than these functions can provide, you can create your own
callbacks. Refer to the above functions in the source code to see
their arguments, return types, and what they do.
[#ChDissectConversation]
=== How to use conversations
Some info about how to use conversations in a dissector can be found in the file
_doc/README.dissector_, chapter 2.2.
[#ChDissectIdl2wrs]
=== __idl2wrs__: Creating dissectors from CORBA IDL files
Many of Wireshark’s dissectors are automatically generated. This section shows
how to generate one from a CORBA IDL file.
==== What is it?
As you have probably guessed from the name, `idl2wrs` takes a user specified IDL
file and attempts to build a dissector that can decode the IDL traffic over
GIOP. The resulting file is “C” code, that should compile okay as a Wireshark
dissector.
`idl2wrs` parses the data struct given to it by the `omniidl` compiler,
and using the GIOP API available in packet-giop.[ch], generates get_CDR_xxx
calls to decode the CORBA traffic on the wire.
It consists of 4 main files.
README.idl2wrs::
This document
wireshark_be.py::
The main compiler backend
wireshark_gen.py::
A helper class, that generates the C code.
idl2wrs::
A simple shell script wrapper that the end user should use to generate the
dissector from the IDL file(s).
==== Why do this?
It is important to understand what CORBA traffic looks like over GIOP/IIOP, and
to help build a tool that can assist in troubleshooting CORBA interworking. This
was especially the case after seeing a lot of discussions about how particular
IDL types are represented inside an octet stream.
I have also had comments/feedback that this tool would be good for say a CORBA
class when teaching students what CORBA traffic looks like “on the wire”.
It is also COOL to work on a great Open Source project such as the case with
“Wireshark” ({wireshark-main-url}).
==== How to use idl2wrs
To use the idl2wrs to generate Wireshark dissectors, you need the following:
* Python must be installed. See link:https://python.org/[]
* `omniidl` from the omniORB package must be available. See link:http://omniorb.sourceforge.net/[]
* Of course you need Wireshark installed to compile the code and tweak it if
required. idl2wrs is part of the standard Wireshark distribution
To use idl2wrs to generate an Wireshark dissector from an idl file use the following procedure:
* To write the C code to stdout.
+
--
----
$ idl2wrs <your_file.idl>
----
e.g.:
----
$ idl2wrs echo.idl
----
--
* To write to a file, just redirect the output.
+
--
----
$ idl2wrs echo.idl > packet-test-idl.c
----
You may wish to comment out the register_giop_user_module() code and that will
leave you with heuristic dissection.
If you don't want to use the shell script wrapper, then try steps 3 or 4 instead.
--
* To write the C code to stdout.
+
--
----
$ omniidl -p ./ -b wireshark_be <your file.idl>
----
e.g.:
----
$ omniidl -p ./ -b wireshark_be echo.idl
----
--
* To write to a file, just redirect the output.
+
--
----
$ omniidl -p ./ -b wireshark_be echo.idl > packet-test-idl.c
----
You may wish to comment out the register_giop_user_module() code and that will
leave you with heuristic dissection.
--
* Copy the resulting C code to subdirectory epan/dissectors/ inside your
Wireshark source directory.
+
--
----
$ cp packet-test-idl.c /dir/where/wireshark/lives/epan/dissectors/
----
The new dissector has to be added to CMakeLists.txt in the same directory. Look
for the declaration DISSECTOR_SRC and add the new dissector there. For
example,
----
DISSECTOR_SRC = \
${CMAKE_CURRENT_SOURCE_DIR}/packet-2dparityfec.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-3com-njack.c
...
----
becomes
----
DISSECTOR_SRC = \
${CMAKE_CURRENT_SOURCE_DIR}/packet-test-idl.c \
${CMAKE_CURRENT_SOURCE_DIR}/packet-2dparityfec.c \
${CMAKE_CURRENT_SOURCE_DIR}/packet-3com-njack.c \
...
----
--
For the next steps, go up to the top of your Wireshark source directory.
* Create a build dir
+
--
----
$ mkdir build && cd build
----
--
* Run cmake
+
--
----
$ cmake ..
----
--
* Build the code
+
--
----
$ make
----
--
* Good Luck !!
==== TODO
* Exception code not generated (yet), but can be added manually.
* Enums not converted to symbolic values (yet), but can be added manually.
* Add command line options etc
* More I am sure :-)
==== Limitations
See the TODO list inside _packet-giop.c_
==== Notes
The `-p ./` option passed to omniidl indicates that the wireshark_be.py and
wireshark_gen.py are residing in the current directory. This may need tweaking
if you place these files somewhere else.
If it complains about being unable to find some modules (e.g. tempfile.py), you
may want to check if PYTHONPATH is set correctly.
// End of WSDG Chapter Dissection
|