⭐ If you would like to buy me a coffee, well thank you very much that is mega kind! : https://www.buymeacoffee.com/honeyvig
Hire a web Developer and Designer to upgrade and boost your online presence with cutting edge Technologies

Monday, December 5, 2022

Decoding LLDP and CDP packets using a TLV reader for C++

This post details how to interpret network discovery packets – LLDP and CDP – that have been captured on a network interface. To do this, I’ll explain how to create a Type, Length and Value reader that reads an array of characters, determines the type of packet and its subsequent data fields and then outputs those values to a console window. 

This posts follows on from that previous one, but you won’t be using any WinPcap functions anymore. In fact, all you need is an array of chars to work with, since that is what will have been captured already.

 

Find out what the packet type is

The first time I tried to do this, I didn’t bother with working out where TLVs were at all. I simply relied on trying to work out which bytes would probably have which bits of data (this is true, also, of how I treated CDP packets) as I mentioned previously. Essentially, I looked at packets that had been captured with Wireshark – but the output varied between PCs, which ultimately depended on which optional data is included.


This is still very helpful with knowing which values I should expect, however. You can work out which parts of a packet correspond to which fields by selecting them in the middle pane. So its doing the same kind of thing we’re trying to do anyway; read a list of packets that have been captured on an interface and display them in a human-readable way. However, we’re just doing a very specific and chopped down form of this.

The first thing to do is to determine which packet was received on the interface. We already know it will be a CDP or LLDP packet, so an easy way to determine the type is to just check to see if it is one of them.

int testPacketType = (packetData[12] << 8 | packetData[13]);

if (testPacketType == 0x88cc)
		{
			getDataLLDP();				
		}

		else
		{
			getDataCDP();
		}

Because a packet’s type is represented over two bytes, we can “push” two bytes into one variable with the << operator. This will perform a bitshift on packetData[12], moving it by 8 places as specified, and add it together with packetData[13] before finally storing it in a two-byte variable; a standard int. Its worth mentioning that they don’t actually get added at all – both bytes gets logically or‘d, which results in all 16 bits being populated. Represented as a numerical value, you would see this as 35020 – the hexadecimal form is, therefore, 88cc.

If the value is different, then it must be a CDP packet instead. Either way, we will direct the packet to one of two functions; getDataLLDP() or getDataCDP(). Both packets are very similar in that the first 12 bytes are a destination and source address, followed by several blocks of data, spaced out by a length and identified by a type. Both protocols are also defined in their relevant specifications, which helps immensely with knowing what data you should be trying to read (int or char? single- or multi-byte values, or sub-byte values?)

Reading an LLDP packet

 

Aside from one error, which you can find at the end of this article, you can get all the TLV information you need from the IEEE 802.1AB-2009 specification. It says exactly what format the data should be in and how the TLVs should be formed. Since we are only reading – rather than forming or transmitting – a packet, we can get away with interpreting things in a way that’s easiest for us. Nevertheless, we still have to read all of the TLVs, because they are sequential and rely on the previous TLV to determine where exactly they start from.

  • To begin with, we can get the MAC addresses out of the way.
  • We know that bytes 13 and 14 (which are 12 and 13) are going to represent the type as LLDP already, so we can ignore them.
  • Anything else is going to be data, represented as TLVs. Now we can start running a count, because as we traverse the packet, we want to make sure we know where we are up to!
void getDataLLDP()
{
	printf("\tDestination MAC address:\t %02x:%02x:%02x:%02x:%02x:%02x", packetData[0], packetData[1], packetData[2], packetData[3], packetData[4], packetData[5]);

	printf("\n\tSource MAC address:\t\t %02x:%02x:%02x:%02x:%02x:%02x", packetData[6], packetData[7], packetData[8], packetData[9], packetData[10], packetData[11]);


	printf("\n\tContents: ");

        unsigned int count = 14;

...

So far so good. Notice how you reference data in a packet? You simply say packetData[x], where x is the specific byte you want to read. Then you can format it as a hexadecimal character (formatting the output with %x, or %02x to ensure that you pad it out with up to two hexadecimal 0s, to make it look like a proper MAC address), character (%c) or anything else you might want.

Things get a bit more involved when you don’t work with whole bytes. With an LLDP TLV, the first two bytes represent “type” and “length”, with all subsequent bytes being the value (for as long as specified by, of course, the length). So look at this simple diagram below of how you would think a TLV should look, with regards to how the bytes represent “type” and “length” data:


..and then forget it again, because it actually looks like this:


Instead of having one byte for the length, it has 9 bits; 1-and-a-bit bytes. So the type is actually only 7 bits. Looks like we have to do some more bit-shift magic to make things work how we want them to. Make a for loop as below, although this one may as well be a while loop, since we aren’t initialising or doing any logic in the statement other than checking to see if count is less than the length of the packet’s header (this stops us starting to read beyond the end of the packet).

...
int mask = 0x01FF;

for (; count < packetHeader->len; )
	{
		unsigned char type = (packetData[count] >> 1);
		unsigned int length = (packetData[count] << 8 | packetData[count + 1]);
		length &= mask;
		count += 2;

...

The first thing we do is get the type. To do this, we copy the value from the first byte into a single byte type (e.g. a char), but this time we bit shift to the right. This means we lose the first bit of data; but that’s fine, because it didn’t belong to this anyway. Now, all the bits have shifted to their correct position (although we could probably have just divided everything by two and got the same result, because this is how you’d perform a division operation in binary anyway).

The next thing to do is to get the length. This time, we need two bytes to store the value in, so we can use an int (note how both of these are unsigned – this is to prevent values being read as negative by accident, indicated by the left-most bit). We can store both bytes’ values in here like we did earlier, when trying to see if the packet was LLDP or CDP, although this time, we want to remove 7 of the 8 bits. To do this, we can use a mask; so you could either minus 65024 from the final value, which represents 11111110 0000000, or we could perform a bit more maths by anding. 0x01FF represents the bits we want to keep – so performing an and will preserve only the bits we want.

Finally, we need to get the value. A few things to mention first:

  • As I stated earlier, the IEEE specs specify what values to expect for a given “type”.
  • So, starting two bytes after the type and length have been specified, we can read length amount of bytes into the packet where we can then expect the next type and length to be specified.
  • In this way, we can know exactly how many fields there are, what fields there are and how big they are.
  • The IEEE specification is also useful in knowing how to format a specific TLV; sometimes, there are subtypes, sometimes the value is hexadecimal, somethings it is text data.
  • There are specific TLVs that must always be included that you can expect to be present; “Chassis ID”, “Port ID”, “Time-to-Live” and a terminating TLV that basically indicates that this is the end of the packet (You can find more info about this in section 9.1 of the 2005 specification)
  • There are optional TLVs that might also be included, too, as well as organisationally-specific TLVs that may or may not be included

Luckily, it looks as though the specs are quite well-known and easy to get ahold of. With that out of the way, I will give an example of how you can implement this. So, straight after you have got the type and length, you can check what the type is:

		unsigned char value[512] = "";


		switch (type)
		{

			case 1:
			{
                printf("\nChassis ID:"); 
				int subtype = packetData[count];
				count += 1;
				length -= 1;

				if (subtype == 4)
				{
					printf("\n\tMAC address: %02x:%02x:%02x:%02x:%02x:%02x", packetData[count], packetData[count + 1], packetData[count + 2], packetData[count + 3], packetData[count + 4], packetData[count + 5]);
					count += 6;
				}

				if (subtype == 6)
				{
					printf("\n\tInterface name:");

					for (UINT16 x = 0; x < length; x++)
					{
						value[x] = packetData[count];
						count++;
					}
				}
				break;
			}
		}

First of all, we make a new character array to hold the value of the current TLV, up to the maximum value that we would expect (and length cannot exceed 512).

Next, we examine the type. From the specs, we know that a type of 1 is “Chassis ID” and that it also contains a subtype. I’m only going to list handling for two subtypes here; an interface name and a MAC address, since these are what seem to be the most often used values that I have seen so far. However, for a more complete implementation, you’ll want to include code to capture all conditions. Important: in the way this has been implemented, after you process a character, you must remember to increase the count. This tells you how many characters have been processed so far, which tells you where you should be along the packet. At the same time, the length is decreased by one when we know the subtype, because length is later used as a measurement of how many characters should be printed out from the position indicated by count.

I will probably publish a more full implementation for handling LLDP packets at a later date, but one thing that I included, regardless, was a default case for anything that I haven’t written an implementation for. What it does is to simply attempt to print out the value of a given TLV as a string of characters.

default:
		{
			printf("\nUnimplemented type");

			for (UINT16 x = 0; x < length; x++)
			{
				value[x] = packetData[count];
				count++;
			}
			printf("\n\t%s", value); 
                        break;
		}

 

Reading a CDP packet

Cisco don’t seem to be as obliging on the specifications front; trying to find anything will return nothing nearly as useful as the IEEE specs for LLDP. There seem to be a few references to now-defuct links to useful links that no longer exist. You can probably find the right TLVs and formats from various open-source projects, but I really wanted to find something I could reference that was a bit more “official”.

As with LLDP, my first stop was to look at the CDP entry over on the Wireshark site. It seems that the relevant information on CDP packet information could once have been viewed here and here – however, these now redirect to fairly useless pages on how to configure switches, instead (and the second link, from what I can find on archive.org, doesn’t seem to have been much help in the first place for this).

Eventually, however, I managed to find a mirror of the first link amongst the bountiful wilderness of the internet, on some obscure Ukrainian site. The document appears to be titled “Catalyst Token Ring Switching Implementation Guide”, but the format of CDP remains the same since it is encapsulated within the media access specific gubbins. Combined with a few other useful links, as well as a few packet captures, I worked out what the various TLVs should be.

Thankfully, Cisco are more sensible with how they allocate bytes and decided that they’d give two bytes to length and two to value. There are also no organisation-specific TLVs, either, so the list is quite short.

  • As with LLDP, we can get the MAC addresses out of the way.
  • There’s then a load of useless stuff to us
  • Bytes 20 and 21 are the protocol ID, which is how we knew this was CDP in the first place. 22, 23, 24 and 25 are more stuff we don’t need.
  • Anything beyond byte 26 will be TLV data, as with LLDP
void getDataCDP()
{
	printf("\tDestination MAC address:\t %02x:%02x:%02x:%02x:%02x:%02x", packetData[0], packetData[1], packetData[2], packetData[3], packetData[4], packetData[5]);

	printf("\n\tSource MAC address:\t\t %02x:%02x:%02x:%02x:%02x:%02x", packetData[6], packetData[7], packetData[8], packetData[9], packetData[10], packetData[11]);
	



 unsigned int count = 26;

Maybe the first thing to explain is why so many bytes pass, compared to LLDP, before you get any data. CDP is encapsulated in an Ethernet frame, rather than Ethernet 2 (and you can tell the difference because the length field of an Ethernet frame can’t exceed 1536, so if it does, it becomes an Ethernet II frame and represents type instead), with 802.2 LLC and SNAP headers. 8 bytes following the length in bytes 12 and 13, which contains LLC information for 3 bytes, the OUI for 3 bytes and then the protocol ID for the next two. Finally, bytes 24 and 25 are the checksum.

...

for (; count < packetHeader->len; )
	{
		unsigned int type = (packetData[count] <<8 | packetData[count + 1]);
		unsigned int length = (packetData[count + 2] <<8 | packetData[count + 3]); 
		count += 4;
		length -= 4;

		unsigned char value[512] = "";

Although the above is very similar to the LLDP code, its worth noting that, unlike LLDP, the length of a TLV indicates total length of the CDP, including the length and type, rather than just the length of the data that follows; hence removing 4 from the length, whilst adding 4 to the count. Also note that bit shift operations are applied to both the type and length to place them both into integers.

switch (type)
		{
			case 0x01 :
			{
				printf("\n\tDevice ID: ");
                                break;
                        }

                        default :
			{
                                break;
                        }
                       
}
for (UINT16 x = 0; x < length; x++) 
{
 value[x] = packetData[count];
 count++;
 } 
printf("%s", value, count - length);

What I’ve done here is to really simplify things by only making the switch statement determine what label the TLV is given and – regardless of type – handle every TLV as a string of characters. In the document I found, only a few TLVs were specified – however, Wireshark’s source code provides a much better insight into the types you can expect to see (no idea where they were originally sourced).

So it looks like there are almost 32 different TLVs that could be included in a CDP packet. However, not all of them read directly as strings of characters and do need additional formatting. With what I have posted here, you should be able to do some digging and work out how they need to be formatted, but I will post the full implementation at a future date.

Here’s one example; the second TLV type is addresses, which can contain more than one address, specified in the first four bytes. To get that, we just do some bit shifting (24, 16 and 8 bits) and then loop through that value for the rest of the TLV. Again, this only breaks down one address – I may well paste a more full implementation later.

case 0x02 :
			{
				printf("\n\n\tAddress: ");
				
				long numberOfAddresses = (packetData[count] << 24 | packetData[count] << 16 | packetData[count + 2] << 8 | packetData[count + 3]);
				count += 4;
				length -= 4;

				for (UINT16 x = 0; x < numberOfAddresses; x++)
				{
					count += 2; 

					//This is an IP address
					if (packetData[count] == 0xcc)
				{
						printf("IP Address: ");
						count++;		
						int addressLength = ( packetData[count] << 8 | packetData[count + 1]);
						count += 2;


						printf_s("%i.%i.%i.%i", packetData[count], packetData[count + 1], packetData[count + 2], packetData[count + 3]);
						count += addressLength;
					}			
					
				}
				
				break;
			}

 

.

Conclusion

There are a number of implementations of ways to read the LLDP and CDP TLVs already in existence in open-source projects and you will find that there are some common ways in which they’ve been implemented: for example, a nice way to handle LLDP TLVs can be found in tcpdump, an implementation of which can be foundover on github or on Apple’s open source repository. Another good project I found was The Assimilation Project, that has a good implementation of both CDP and LLDP packet handlers (not just the CDP information which I linked to). These applications all give nice structures for the names of the TLVs and subtypes and can save a lot of time in formatting things. However, they may be too cumbersome for certain applications, especially if you don’t need to actually print out the data and, instead, just want to pass it on to something else.

Whilst finding out information about how to capture CDP and LLDP data in the first place, I came across a number of blog posts that simply use command-line tcpdump/windump. The amount of sources that have delved deeper into actually pulling apart these packets seems to be a lot less numerous; so hopefully someone will come across this post and find it of use. Let me know if it is and if you have any questions!

 

 

Final note – wrong IEEE specs?

Its worth pointing out that the IEEE 802.1AB-2009 specification is wrong for part of this. It suggests that – for a “System Capabilities” TLV – the third byte is going to represent the “Chassis ID subtype”, for some unknown and bizarre reason. It is as though they’ve copy-pasted it from another one – Chassis ID in section 8.5.2 (page 26).

The previous spec, 802.1AB-2005, however, has the correct TLV format listed for a “System Capabilities” TLV on 9.5.8 (Page 26). It looks like they’ve realised this already but, hey, why bother taking down a 6 year old standard when its been up all that time harmlessly – its not like anyone is going to even read the document ever, right?

Capturing LLDP and CDP packets using C++ and WinPcap

 

I’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. This setup will work where:

  • You have a Windows PC with Winsock installed (Windows XP or later will do).
  • You have a network card on your PC.
  • You have the WinPcap runtime installed.
  • You are connected to a switch, or router, that sends out LLDP or CDP packets – I would imagine most corporate networks with Cisco switches even at least 10 years old will be sending these packets out.

The easiest way to get up and running with using WinPcap is to download the library from the WinPCap site and follow this tutorial, which gives you a good starting point for making an application using the WinPcap. (Also, it is worth noting that I’ve been using Visual Studio 2015 for this – not sure how non-Microsoft compilers would handle things but there shouldn’t be anything specific here that would cause any issues.)

This first post deals with the specifics of getting WinPcap working in a way that we want it to. An important thing to note is that this codes provides absolutely no error checking – 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.

Headers, libraries and variables

So first of all, here are the libraries, headers etc to use. Also a few global variables.

#define HAVE_REMOTE
#include "pcap.h"
#pragma comment (lib, "wpcap.lib")
#pragma comment (lib, "Packet.lib")
#pragma comment (lib, "Ws2_32.lib")

//Used to store all the adapters in a list (by storing references to next adapters)
pcap_if_t * allAdapters; 

//Used to store a single adapters from the above list.
pcap_if_t * adapter; 

//Used for referencing a capture session
pcap_t * captureInstance;

//You'll need these later to store a reference to a packet and its header 
struct pcap_pkthdr * packetHeader;
const u_char * packetData;

//This stores errors. We won't use it much in this example.
char errorBuffer[PCAP_ERRBUF_SIZE];

 

Selecting the capture interface

First, within the main function, you need to select the right adapter (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. pcap_findalldevs_ex will return the list to be stored in allAdapters.

 int totalAdapters = 0;

 for (adapter = allAdapters; adapter; adapter = adapter->next)
 {
    ++totalAdapters;   
    printf("\n%d %s) ", totalAdapters, adapter->name);
    printf("-- %s\n", adapter->description);
 }
 //Just add a blank line. Makes it look pretty.
 printf("\n");

 

The next step identifies the correct adapter number by incrementing selectedAdapterNumber until a separate function, testTargetNetwork, returns true

 int selectedAdapterNumber = 0;

 for (adapter = allAdapters; adapter; adapter = adapter->next)
 {
    ++selectedAdapterNumber;
    if (testTargetNetwork(adapter))
    {
        printf("\nComputer is currently attached to the network through adapter %d", selectedAdapterNumber);
        printf("\n");
    }
 }

 

The function that this code uses, testTargetNetwork – below, stores the IP addresses of a single adapter. We are only testing for the sa_family being AF_INET, so no MAC or IPv6 addresses get stored. Then I decided to make a bit of a dodgy way to convert the IP address – stored as characters – to a decimal value, by storing each part as “hundreds”, “tens” and “units”, 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).

bool testTargetNetwork(pcap_if_t * adapter)
{
 char ipAddress[INET6_ADDRSTRLEN];
 pcap_addr_t * adapterAddress;

 for (adapterAddress = adapter->addresses; adapterAddress; adapterAddress = adapterAddress->next)
 {
    if (adapterAddress->addr->sa_family == AF_INET)
    { 
       inet_ntop(adapterAddress->addr->sa_family,
       &(((struct sockaddr_in*)adapterAddress->addr)->sin_addr),
       ipAddress,
       sizeof(ipAddress));
    }
 }

 int hundreds;
 int tens;
 int units;

 if (ipAddress[2] == '.')
 {
    hundreds = (ipAddress[0] - '0') * 10;
    tens = (ipAddress[1] - '0') * 1;
    units = 0;
 }
 else
 {
    hundreds = (ipAddress[0] - '0') * 100;
    tens = (ipAddress[1] - '0') * 10;
    units = (ipAddress[2] - '0') * 1;
 }

 int temp4 = hundreds + tens + units;
 int network = 10;
 return (temp4 == network);
}

 

If this returns true, then we know the number of the adapter we want. To select that adapter, again, you can perform allAdapters->next for that many times.

Preparing the filter

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 adapter specified. WinPCap does all this magic for you, you just have to specify the filter as a string of text, stored in packet_filter.

captureInstance = pcap_open(adapter->name, 65535, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, errorBuffer);

char packet_filter[] = "ether[12:2] = 0x88cc or ether[20:2] = 0x2000";

struct bpf_program fcode;
pcap_compile(captureInstance, &fcode, packet_filter, 1, ((struct sockaddr_in *)(adapter->addresses->netmask))->sin_addr.S_un.S_addr);
pcap_setfilter(captureInstance, &fcode);

 

The two ethernet frames we are looking to capture are CDP and LLDP. CDP data is stored in an Ethernet frame, in an LLC wrapper with SNAP extensions. LLDP data is stored in an Ethernet II frame. Because CDP doesn’t use a (now standard) Ethernet II frame, we can’t specifically filter by saying “if the protocol is LLDP..” – because the place where the type is expected 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.


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.

Initiating the capture

Now we need to actually capture a packet; the right adapter has been selected, the filter has been set up and the device opened – all we need to do now is to retrieve a packet. You can make a new function and call it from main() after setting up the filter which, to begin with, will free the device list created earlier, since we don’t need it anymore.

void capture()
{
        pcap_freealldevs(allAdapters);

Then add in code to start the loop off. What happens, is that the function pcap_next_ex will attempt to retrieve the next packet available on the specified capture instance.

When it receives a packet, it will store a pointer to the header of the packet, a pointer to the start of the packet’s data and return a value of “1” as its code, which we can use to mean “a packet was found”.

If no packet is found, however, the program should time out – which returns a 0 instead (well, it will actually return -1 as well, if there’s an error, which will break out of the while loop altogether). Back above, when calling pcap_open, a value of 1000 (ms) was specified for the timeout parameter, which means that – if no packets have been captured by the system in that time – a value of 0 is returned. Note that this timeout is more like a “countdown” – nothing will happen within that time, but several packets may have accumulated in that period, before being retrieved by the application.

We can then handle it appropriately by exiting the loop if we want, with break; – although, in this instance, continue; is 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).

	printf("\nCapture session started (%s)\n", adapter->name);


	int packetFound;
	while ((packetFound = pcap_next_ex(captureInstance, &packetHeader, &packetData)) >= 0)
	{
		if (packetFound == 0)
		{
			printf("\nPacket timed out! Trying again..");
			continue;
		}


		printf("\n%d:%d:%d length of packet: %d\n", (packetHeader->ts.tv_sec % 86400) / 3600, (packetHeader->ts.tv_sec % 3600) / 60, packetHeader->ts.tv_sec % 60, packetHeader->len);
						
		printf("\n");

		for (int x = 0; x < packetHeader->len; x++)
		{
			if ((packetData[x] == 0x0e) || (packetData[x] == '\0'))
			{
				printf("\n");
			}
			else
			{
				printf("%c", packetData[x]);
			}
		}
	}
}

When a packet is received, the date and length of the packet is printed out. I’m not sure if there’s an easier way to date and time from seconds, but the above way works fine. Anyway, all the program then does is print out each byte of data, formatted to be read as a character. If the byte’s value is either “14” or “\0”, then it is going to be a new line, otherwise, it will simply list the entire contents of the packet.

Summary (full code)

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 how 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’m hardly an expert!), please feel free to ask in the comments!

#include "stdafx.h"

#define HAVE_REMOTE

//WinPcap libraries. This can be done in the project C++/Linker properties
#include "pcap.h"
#pragma comment (lib, "wpcap.lib")
#pragma comment (lib, "Packet.lib")
#pragma comment (lib, "Ws2_32.lib")



int setAdapter();
void capture();
bool testTargetNetwork(pcap_if_t * adapter);




pcap_if_t			*	allAdapters;		//Used to store all the adapters in a list (by storing references to next adapters)
pcap_if_t			*	adapter;			//Used to store one of the adapters in a list.
pcap_t				*	captureInstance;	//Used to store a capture
struct pcap_pkthdr	*	packetHeader;		//Used to store the header for a packet
const u_char		*	packetData;			//Used to store the packet's data

char errorBuffer[PCAP_ERRBUF_SIZE];

int _tmain(int argc, _TCHAR* argv[])
{
	pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &allAdapters, errorBuffer);


	int totalAdapters = 0;

	for (adapter = allAdapters; adapter; adapter = adapter->next)
	{
		printf("\n%d %s) ", ++totalAdapters, adapter->name);
		printf("-- %s\n", adapter->description);
	}
	//Just add a blank line. Makes it look pretty.
	printf("\n");

	
	int selectedAdapterNumber = 0;

	for (adapter = allAdapters; adapter; adapter = adapter->next)
	{
		++selectedAdapterNumber;
		if (testTargetNetwork(adapter))
		{
			printf("\nComputer is currently attached to the network through adapter %d", selectedAdapterNumber);
			printf("\n");
		}
	}
	
	adapter = allAdapters;
	for (int i = 0; i < selectedAdapterNumber - 1; i++)
	{
		adapter = adapter->next;
	}
	


	captureInstance = pcap_open(adapter->name, 65535, PCAP_OPENFLAG_PROMISCUOUS, 65000, NULL, errorBuffer);

	char packet_filter[] = "ether[12:2] = 0x88cc or ether[20:2] = 0x2000";

	struct bpf_program fcode;
	pcap_compile(captureInstance, &fcode, packet_filter, 1, ((struct sockaddr_in *)(adapter->addresses->netmask))->sin_addr.S_un.S_addr);
	pcap_setfilter(captureInstance, &fcode);




	capture(); 


	system("PAUSE");
	return 0;
}

//This is what we use to compare the interfaces against our network identity.
bool testTargetNetwork(pcap_if_t * adapter)
{
	char ipAddress[INET6_ADDRSTRLEN];
	pcap_addr_t * adapterAddress;

	for (adapterAddress = adapter->addresses; adapterAddress; adapterAddress = adapterAddress->next)
	{
		if (adapterAddress->addr->sa_family == AF_INET)
		{
			inet_ntop(adapterAddress->addr->sa_family,
				&(((struct sockaddr_in*)adapterAddress->addr)->sin_addr),
				ipAddress,
				sizeof(ipAddress));
		}
	}

	int hundreds;
	int tens;
	int units;

	if (ipAddress[2] == '.')
	{
		hundreds = (ipAddress[0] - '0') * 10;
		tens = (ipAddress[1] - '0') * 1;
		units = 0;
	}
	else
	{
		hundreds = (ipAddress[0] - '0') * 100;
		tens = (ipAddress[1] - '0') * 10;
		units = (ipAddress[2] - '0') * 1;
	}

	int temp4 = hundreds + tens + units;
	int network = 10;
	return (temp4 == network);
}

//Capture code
void capture()
{
	pcap_freealldevs(allAdapters);


	printf("\nCapture session started (%s)\n", adapter->name);


	int packetFound;
	while ((packetFound = pcap_next_ex(captureInstance, &packetHeader, &packetData)) >= 0)
	{
		if (packetFound == 0)
		{
			printf("\nPacket timed out! Trying again..");
			continue;
		}


		printf("\n%d:%d:%d length of packet: %d\n", (packetHeader->ts.tv_sec % 86400) / 3600, (packetHeader->ts.tv_sec % 3600) / 60, packetHeader->ts.tv_sec % 60, packetHeader->len);

		printf("\n");

		for (int x = 0; x < packetHeader->len; x++)
		{
			if ((packetData[x] == 0x0e) || (packetData[x] == '\0'))
			{
				printf("\n");
			}
			else
			{
				printf("%c", packetData[x]);
			}
		}
                //Stop iterating and exit
                break;
	}
}