{"id":335,"date":"2015-11-03T16:26:39","date_gmt":"2015-11-03T16:26:39","guid":{"rendered":"http:\/\/www.troliver.com\/?p=335"},"modified":"2016-09-27T22:39:52","modified_gmt":"2016-09-27T21:39:52","slug":"capturing-lldp-and-cdp-packets-using-c-and-winpcap","status":"publish","type":"post","link":"https:\/\/www.troliver.com\/?p=335","title":{"rendered":"Capturing LLDP and CDP packets using C++ and WinPcap"},"content":{"rendered":"<p>I&#8217;ll categorise and change the order of posts later perhaps, but here is the first in a series on how I made an application to record the switchport and vlan, of a switch, that a computer is connected to.\u00a0This setup will work where:<\/p>\n<ul>\n<li>You have a Windows PC with Winsock installed (Windows XP or later will do).<\/li>\n<li>You have a network card on your PC.<\/li>\n<li>You have the WinPcap runtime installed.<\/li>\n<li>You are connected to a switch, or router, that sends out LLDP or CDP packets &#8211; I would imagine most corporate networks with Cisco switches even at least 10 years old will be sending these packets out.<\/li>\n<\/ul>\n<p>The easiest way to get up and running with using WinPcap is to download the library\u00a0from <a href=\"http:\/\/www.winpcap.org\/devel.htm\">the WinPCap site<\/a> and follow <a href=\"https:\/\/www.winpcap.org\/docs\/docs_412\/html\/group__wpcap__tut.html\">this tutorial<\/a>, which gives you a good starting point for making an application using the WinPcap. (Also, it is worth noting that I&#8217;ve been using\u00a0<strong>Visual Studio 2015<\/strong> for this &#8211; not sure how non-Microsoft compilers would handle things but there shouldn&#8217;t be anything specific here that would cause any issues.)<\/p>\n<p>This first post deals with the specifics of getting\u00a0<strong>WinPcap\u00a0<\/strong>working in a way that we want it to. An important thing to note is that this codes provides absolutely no error checking &#8211; I stripped it all out to make the code more easy to follow, but you really should try and capture errors! Read on for the breakdown, or skip to the end to grab the whole code.<\/p>\n<h2>Headers, libraries and variables<\/h2>\n<p>So first of all, here are the\u00a0libraries, headers etc to use. Also a few global variables.<\/p>\n<pre class=\"lang:c++ decode:true\">#define HAVE_REMOTE\r\n#include \"pcap.h\"\r\n#pragma comment (lib, \"wpcap.lib\")\r\n#pragma comment (lib, \"Packet.lib\")\r\n#pragma comment (lib, \"Ws2_32.lib\")\r\n\r\n\/\/Used to store all the adapters in a list (by storing references to next adapters)\r\npcap_if_t * allAdapters; \r\n\r\n\/\/Used to store a single adapters from the above list.\r\npcap_if_t * adapter; \r\n\r\n\/\/Used for referencing a capture session\r\npcap_t * captureInstance;\r\n\r\n\/\/You'll need these later to store a reference to a packet and its header \r\nstruct pcap_pkthdr * packetHeader;\r\nconst u_char * packetData;\r\n\r\n\/\/This stores errors. We won't use it much in this example.\r\nchar errorBuffer[PCAP_ERRBUF_SIZE];\r\n<\/pre>\n<p>&nbsp;<\/p>\n<h2>Selecting the capture interface<\/h2>\n<p>First, within the main function, you need to\u00a0select the right<strong> adapter <\/strong>(in other words, the network card. A system can have lots of these. Some of ours definitely do) to initiate a capture on. First we need to store all devices and then iterate through them to find how many adapters there actually are.\u00a0<em>pcap_findalldevs_ex<\/em> will return the list to be stored in allAdapters.<\/p>\n<pre class=\"lang:c++ decode:true\"> int totalAdapters = 0;\r\n\r\n for (adapter = allAdapters; adapter; adapter = adapter-&gt;next)\r\n {\r\n    ++totalAdapters;   \r\n    printf(\"\\n%d %s) \", totalAdapters, adapter-&gt;name);\r\n    printf(\"-- %s\\n\", adapter-&gt;description);\r\n }\r\n \/\/Just add a blank line. Makes it look pretty.\r\n printf(\"\\n\");<\/pre>\n<p>&nbsp;<\/p>\n<p>The next step identifies the correct adapter number by incrementing <em>selectedAdapterNumber<\/em>\u00a0until a separate function, <em>testTargetNetwork<\/em>, returns\u00a0<strong>true<\/strong><\/p>\n<pre class=\"lang:c++ decode:true\"> int selectedAdapterNumber = 0;\r\n\r\n for (adapter = allAdapters; adapter; adapter = adapter-&gt;next)\r\n {\r\n    ++selectedAdapterNumber;\r\n    if (testTargetNetwork(adapter))\r\n    {\r\n        printf(\"\\nComputer is currently attached to the network through adapter %d\", selectedAdapterNumber);\r\n        printf(\"\\n\");\r\n    }\r\n }\r\n\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>The function that this code uses,\u00a0<em>testTargetNetwork<\/em> &#8211; below, stores the IP addresses of a single adapter.\u00a0We are only testing for the sa_family being AF_INET, so no MAC or IPv6 addresses get stored.\u00a0Then I decided to make a bit of a dodgy way to convert the IP address &#8211; stored as characters &#8211; to a decimal value, by storing each part as &#8220;hundreds&#8221;, &#8220;tens&#8221; and &#8220;units&#8221;, multiplying them by their position, and then adding them together to determine if the detected IP address is on the correct network we want (this last test returns true or false).<\/p>\n<pre class=\"lang:c++ decode:true\">bool testTargetNetwork(pcap_if_t * adapter)\r\n{\r\n char ipAddress[INET6_ADDRSTRLEN];\r\n pcap_addr_t * adapterAddress;\r\n\r\n for (adapterAddress = adapter-&gt;addresses; adapterAddress; adapterAddress = adapterAddress-&gt;next)\r\n {\r\n    if (adapterAddress-&gt;addr-&gt;sa_family == AF_INET)\r\n    { \r\n       inet_ntop(adapterAddress-&gt;addr-&gt;sa_family,\r\n       &amp;(((struct sockaddr_in*)adapterAddress-&gt;addr)-&gt;sin_addr),\r\n       ipAddress,\r\n       sizeof(ipAddress));\r\n    }\r\n }\r\n\r\n int hundreds;\r\n int tens;\r\n int units;\r\n\r\n if (ipAddress[2] == '.')\r\n {\r\n    hundreds = (ipAddress[0] - '0') * 10;\r\n    tens = (ipAddress[1] - '0') * 1;\r\n    units = 0;\r\n }\r\n else\r\n {\r\n    hundreds = (ipAddress[0] - '0') * 100;\r\n    tens = (ipAddress[1] - '0') * 10;\r\n    units = (ipAddress[2] - '0') * 1;\r\n }\r\n\r\n int temp4 = hundreds + tens + units;\r\n int network = 10;\r\n return (temp4 == network);\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<p>If this returns <strong>true<\/strong>, then we know the number of the adapter we want. To select that adapter, again, you can perform <em>allAdapters-&gt;next\u00a0<\/em>for that many times.<\/p>\n<h2>Preparing the\u00a0filter<\/h2>\n<p>The next step is go back to the main function to create a filter that will be used to capture only specific packets on the\u00a0adapter specified. <i>WinPCap\u00a0<\/i>does all this magic for you, you just have to specify the filter as a string of text, stored in\u00a0<em>packet_filter.<\/em><\/p>\n<pre class=\"lang:c++ decode:true\">captureInstance = pcap_open(adapter-&gt;name, 65535, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, errorBuffer);\r\n\r\nchar packet_filter[] = \"ether[12:2] = 0x88cc or ether[20:2] = 0x2000\";\r\n\r\nstruct bpf_program fcode;\r\npcap_compile(captureInstance, &amp;fcode, packet_filter, 1, ((struct sockaddr_in *)(adapter-&gt;addresses-&gt;netmask))-&gt;sin_addr.S_un.S_addr);\r\npcap_setfilter(captureInstance, &amp;fcode);<\/pre>\n<p>&nbsp;<\/p>\n<p>The two ethernet frames we are looking to capture are <em>CDP<\/em> and <em>LLDP. CDP\u00a0<\/em>data is stored in an Ethernet frame, in an LLC wrapper with SNAP extensions.\u00a0<em>LLDP\u00a0<\/em>data is stored in an Ethernet II frame. Because <em>CDP\u00a0<\/em>doesn&#8217;t use a (now standard) Ethernet II frame, we can&#8217;t specifically filter by saying &#8220;if the protocol is LLDP..&#8221; &#8211; because the place where the type is\u00a0expected to be determined is actually going to be the length of the packet, rather than the type. So we have to tell it where to look for the type and then work out for ourselves what that that type should be specified as.<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-342 aligncenter\" src=\"http:\/\/www.troliver.com\/wp-content\/uploads\/2015\/11\/proto.png\" alt=\"proto\" width=\"697\" height=\"190\" srcset=\"https:\/\/www.troliver.com\/wp-content\/uploads\/2015\/11\/proto.png 697w, https:\/\/www.troliver.com\/wp-content\/uploads\/2015\/11\/proto-300x82.png 300w\" sizes=\"(max-width: 697px) 100vw, 697px\" \/><\/p>\n<p>The string says that either bytes 12 and 13 of a packet should be 0x88cc (which is the type for LLDP) or bytes 20 and 21 should be 0x2000 (which is the protocol ID for CDP). The filter will then get compiled into binary and passed to the packet capture driver at runtime so that only the packets we want get passed to the application.<\/p>\n<h2>Initiating the capture<\/h2>\n<p>Now we need to actually capture a packet; the right adapter has been selected, the filter has been set up and the device opened &#8211; all we need to do now is to retrieve a packet. You can make a new function and call it from\u00a0<em>main()<\/em> after setting up the filter which, to begin with, will free the device list created earlier, since we don&#8217;t need it anymore.<\/p>\n<pre class=\"lang:c++ decode:true\">void capture()\r\n{\r\n        pcap_freealldevs(allAdapters);\r\n\r\n<\/pre>\n<p>Then add in code to start the loop off. What happens, is that the function\u00a0<em>pcap_next_ex<\/em> will attempt to retrieve the next packet available on the specified capture instance.<\/p>\n<p>When it receives a packet, it will store a pointer to the header of the packet, a pointer to the start of the packet&#8217;s data and return a value of &#8220;1&#8221; as its code, which we can use to mean &#8220;a packet was found&#8221;.<\/p>\n<p>If no packet is found, however,\u00a0the program should time out &#8211; which returns a 0 instead (well, it will actually return -1 as well, if there&#8217;s an error, which will break out of the\u00a0<em>while<\/em> loop altogether). Back above, when calling <em>pcap_open<\/em>,<em>\u00a0<\/em>a value of 1000 (ms) was specified for the timeout parameter, which means that &#8211; if no packets have been captured by the system in that time &#8211; a value of 0 is returned. Note that this timeout is more like a &#8220;countdown&#8221; &#8211; nothing will happen within that time, but several packets may have accumulated in that period, before being retrieved by the application.<\/p>\n<p>We can then handle it appropriately by exiting the loop if we want, with <span class=\"lang:c++ decode:true crayon-inline \">break;<\/span>\u00a0&#8211; although, in this instance, <span class=\"lang:c++ decode:true crayon-inline\">continue;<\/span>\u00a0is used instead, which continues the loop but steps over the rest of the code in the current cycle. It would probably be better to just increase the timeout to 1 minute, but even then, it could be a long time before CDP and LLDP packets are received (by default, they are sent out every 1 minute and 30 seconds, respectively, on Cisco devices).<\/p>\n<pre class=\"lang:c++ decode:true\">\tprintf(\"\\nCapture session started (%s)\\n\", adapter-&gt;name);\r\n\r\n\r\n\tint packetFound;\r\n\twhile ((packetFound = pcap_next_ex(captureInstance, &amp;packetHeader, &amp;packetData)) &gt;= 0)\r\n\t{\r\n\t\tif (packetFound == 0)\r\n\t\t{\r\n\t\t\tprintf(\"\\nPacket timed out! Trying again..\");\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\r\n\t\tprintf(\"\\n%d:%d:%d length of packet: %d\\n\", (packetHeader-&gt;ts.tv_sec % 86400) \/ 3600, (packetHeader-&gt;ts.tv_sec % 3600) \/ 60, packetHeader-&gt;ts.tv_sec % 60, packetHeader-&gt;len);\r\n\t\t\t\t\t\t\r\n\t\tprintf(\"\\n\");\r\n\r\n\t\tfor (int x = 0; x &lt; packetHeader-&gt;len; x++)\r\n\t\t{\r\n\t\t\tif ((packetData[x] == 0x0e) || (packetData[x] == '\\0'))\r\n\t\t\t{\r\n\t\t\t\tprintf(\"\\n\");\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tprintf(\"%c\", packetData[x]);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<p>When a packet is received, the date and length of the packet is printed out. I&#8217;m not sure if there&#8217;s an easier way to date and time from seconds, but the above way works fine. Anyway, all the program\u00a0then does is print out each byte of data, formatted to be read as a character. If the byte&#8217;s value is either &#8220;14&#8221; or &#8220;\\0&#8221;, then it is going to be a new line, otherwise, it will simply list the entire contents of the packet.<\/p>\n<h2>Summary (full code)<\/h2>\n<p>Here is the final code, put together, with a pause so you can see what happens. What you should get out is a load of gibberish characters and some human-readable ones. Next post will look at\u00a0how to properly handle the packets and get something far more readable on screen; until then, if anyone has any questions about the code or using WinPcap libraries (although I&#8217;m hardly an expert!), please feel free to ask in the comments!<\/p>\n<pre class=\"height-set:true lang:c++ decode:true\">#include \"stdafx.h\"\r\n\r\n#define HAVE_REMOTE\r\n\r\n\/\/WinPcap libraries. This can be done in the project C++\/Linker properties\r\n#include \"pcap.h\"\r\n#pragma comment (lib, \"wpcap.lib\")\r\n#pragma comment (lib, \"Packet.lib\")\r\n#pragma comment (lib, \"Ws2_32.lib\")\r\n\r\n\r\n\r\nint setAdapter();\r\nvoid capture();\r\nbool testTargetNetwork(pcap_if_t * adapter);\r\n\r\n\r\n\r\n\r\npcap_if_t\t\t\t*\tallAdapters;\t\t\/\/Used to store all the adapters in a list (by storing references to next adapters)\r\npcap_if_t\t\t\t*\tadapter;\t\t\t\/\/Used to store one of the adapters in a list.\r\npcap_t\t\t\t\t*\tcaptureInstance;\t\/\/Used to store a capture\r\nstruct pcap_pkthdr\t*\tpacketHeader;\t\t\/\/Used to store the header for a packet\r\nconst u_char\t\t*\tpacketData;\t\t\t\/\/Used to store the packet's data\r\n\r\nchar errorBuffer[PCAP_ERRBUF_SIZE];\r\n\r\nint _tmain(int argc, _TCHAR* argv[])\r\n{\r\n\tpcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &amp;allAdapters, errorBuffer);\r\n\r\n\r\n\tint totalAdapters = 0;\r\n\r\n\tfor (adapter = allAdapters; adapter; adapter = adapter-&gt;next)\r\n\t{\r\n\t\tprintf(\"\\n%d %s) \", ++totalAdapters, adapter-&gt;name);\r\n\t\tprintf(\"-- %s\\n\", adapter-&gt;description);\r\n\t}\r\n\t\/\/Just add a blank line. Makes it look pretty.\r\n\tprintf(\"\\n\");\r\n\r\n\t\r\n\tint selectedAdapterNumber = 0;\r\n\r\n\tfor (adapter = allAdapters; adapter; adapter = adapter-&gt;next)\r\n\t{\r\n\t\t++selectedAdapterNumber;\r\n\t\tif (testTargetNetwork(adapter))\r\n\t\t{\r\n\t\t\tprintf(\"\\nComputer is currently attached to the network through adapter %d\", selectedAdapterNumber);\r\n\t\t\tprintf(\"\\n\");\r\n\t\t}\r\n\t}\r\n\t\r\n\tadapter = allAdapters;\r\n\tfor (int i = 0; i &lt; selectedAdapterNumber - 1; i++)\r\n\t{\r\n\t\tadapter = adapter-&gt;next;\r\n\t}\r\n\t\r\n\r\n\r\n\tcaptureInstance = pcap_open(adapter-&gt;name, 65535, PCAP_OPENFLAG_PROMISCUOUS, 65000, NULL, errorBuffer);\r\n\r\n\tchar packet_filter[] = \"ether[12:2] = 0x88cc or ether[20:2] = 0x2000\";\r\n\r\n\tstruct bpf_program fcode;\r\n\tpcap_compile(captureInstance, &amp;fcode, packet_filter, 1, ((struct sockaddr_in *)(adapter-&gt;addresses-&gt;netmask))-&gt;sin_addr.S_un.S_addr);\r\n\tpcap_setfilter(captureInstance, &amp;fcode);\r\n\r\n\r\n\r\n\r\n\tcapture(); \r\n\r\n\r\n\tsystem(\"PAUSE\");\r\n\treturn 0;\r\n}\r\n\r\n\/\/This is what we use to compare the interfaces against our network identity.\r\nbool testTargetNetwork(pcap_if_t * adapter)\r\n{\r\n\tchar ipAddress[INET6_ADDRSTRLEN];\r\n\tpcap_addr_t * adapterAddress;\r\n\r\n\tfor (adapterAddress = adapter-&gt;addresses; adapterAddress; adapterAddress = adapterAddress-&gt;next)\r\n\t{\r\n\t\tif (adapterAddress-&gt;addr-&gt;sa_family == AF_INET)\r\n\t\t{\r\n\t\t\tinet_ntop(adapterAddress-&gt;addr-&gt;sa_family,\r\n\t\t\t\t&amp;(((struct sockaddr_in*)adapterAddress-&gt;addr)-&gt;sin_addr),\r\n\t\t\t\tipAddress,\r\n\t\t\t\tsizeof(ipAddress));\r\n\t\t}\r\n\t}\r\n\r\n\tint hundreds;\r\n\tint tens;\r\n\tint units;\r\n\r\n\tif (ipAddress[2] == '.')\r\n\t{\r\n\t\thundreds = (ipAddress[0] - '0') * 10;\r\n\t\ttens = (ipAddress[1] - '0') * 1;\r\n\t\tunits = 0;\r\n\t}\r\n\telse\r\n\t{\r\n\t\thundreds = (ipAddress[0] - '0') * 100;\r\n\t\ttens = (ipAddress[1] - '0') * 10;\r\n\t\tunits = (ipAddress[2] - '0') * 1;\r\n\t}\r\n\r\n\tint temp4 = hundreds + tens + units;\r\n\tint network = 10;\r\n\treturn (temp4 == network);\r\n}\r\n\r\n\/\/Capture code\r\nvoid capture()\r\n{\r\n\tpcap_freealldevs(allAdapters);\r\n\r\n\r\n\tprintf(\"\\nCapture session started (%s)\\n\", adapter-&gt;name);\r\n\r\n\r\n\tint packetFound;\r\n\twhile ((packetFound = pcap_next_ex(captureInstance, &amp;packetHeader, &amp;packetData)) &gt;= 0)\r\n\t{\r\n\t\tif (packetFound == 0)\r\n\t\t{\r\n\t\t\tprintf(\"\\nPacket timed out! Trying again..\");\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\r\n\t\tprintf(\"\\n%d:%d:%d length of packet: %d\\n\", (packetHeader-&gt;ts.tv_sec % 86400) \/ 3600, (packetHeader-&gt;ts.tv_sec % 3600) \/ 60, packetHeader-&gt;ts.tv_sec % 60, packetHeader-&gt;len);\r\n\r\n\t\tprintf(\"\\n\");\r\n\r\n\t\tfor (int x = 0; x &lt; packetHeader-&gt;len; x++)\r\n\t\t{\r\n\t\t\tif ((packetData[x] == 0x0e) || (packetData[x] == '\\0'))\r\n\t\t\t{\r\n\t\t\t\tprintf(\"\\n\");\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tprintf(\"%c\", packetData[x]);\r\n\t\t\t}\r\n\t\t}\r\n                \/\/Stop iterating and exit\r\n                break;\r\n\t}\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ll categorise and change the order of posts later perhaps, but here is the first in a series on how I made an application to record the switchport and vlan, of a switch, that a computer is connected to.\u00a0This setup will work where: You have a Windows PC with Winsock installed (Windows XP or later [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_newsletter_tier_id":0,"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false}}},"categories":[21],"tags":[30,34],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p6PQZ3-5p","_links":{"self":[{"href":"https:\/\/www.troliver.com\/index.php?rest_route=\/wp\/v2\/posts\/335"}],"collection":[{"href":"https:\/\/www.troliver.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.troliver.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.troliver.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.troliver.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=335"}],"version-history":[{"count":9,"href":"https:\/\/www.troliver.com\/index.php?rest_route=\/wp\/v2\/posts\/335\/revisions"}],"predecessor-version":[{"id":409,"href":"https:\/\/www.troliver.com\/index.php?rest_route=\/wp\/v2\/posts\/335\/revisions\/409"}],"wp:attachment":[{"href":"https:\/\/www.troliver.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=335"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.troliver.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=335"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.troliver.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=335"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}