I am reading packets from a pcap file. I want to keep it simple but I need to check for certain IP and TCP header options. I used the Pcap function nextEx to get a libpcap header and the packet contents as a JBuffer. What's the best way to now access the IP and TCP options? I looked around the examples and tried a few things but my code isn't able to detect if the packet is TCP correctly. Example is below.
Pcap pcapOffline = Pcap.openOffline(pcapFile, errbuf);
pcapOffline.nextEx(pkt_header, buffer);
Tcp tcpT = new Tcp();
PcapPacket offlinePacket = new PcapPacket(pkt_header, buffer);
if (offlinePacket.hasHeader(tcpT)) {
if (tcpT.flags_SYN() && !tcpT.flags_ACK())
numSyns++;
else if (tcpT.flags_FIN() || tcpT.flags_RST())
numCloses++;
else if (new String(buffer.getByteArray(tcpT.hlen(),
tcpT.getPayloadLength())).contains("GET http://"))
numGets++;
} else {
nonTCPPackets++;
}
The hasHeader check is always failing even when I know I have a TCP packet, why is that? Do I need to create a PcapPacket from the nextEx results before I can parse out the TCP options? If so why isn't there a nextEx call that will return a PcapPacket?
BTW, Is there a better way to get access to the TCP payload as a byte array so I can parse it for an HTTP get?
> The hasHeader check is always failing even when I know I have a TCP packet, why is that?
Its failing because the packet hasn't been scanned by the scanner yet. You need to add a line:
PcapPacket offlinePacket = new PcapPacket(pkt_header, buffer); offlinePacket.scan(JProtocol.ETHERNET_ID); // Or whatever DLT ID
I think if someone is creating a PcapPacket, there content should automatically be decoded. I'll take a closer look at the PcapPacket(PcapHeader, JBuffer) contract.
> Do I need to create a PcapPacket from the nextEx results before I can parse out the TCP options?
Yes, that is the proper way. You could also create a JMemoryPacket, but since you have the PcapHeader already, might as well create a PcapPacket.
> If so why isn't there a nextEx call that will return a PcapPacket?
Good question, I'll take a closer look and add one in if needed. Sounds like one is needed.
> BTW, Is there a better way to get access to the TCP payload as a byte array so I can parse it for an HTTP get?
You can access any part of the packet as anything you'd like with or without copies. Here is an example:
JBuffer tcpPayload = new JBuffer(JMemory.Type.POINTER); // Uninitialized buffer
int payloadOffset = tcpT.getOffset() + tcpT.size();
int payloadLength = tcpT.getPayloadLength();
tcpPayload.peer(offlinePacket, payloadOffset, payloadLength); // No copies, by native reference
System.out.printf("first payload byte=%d\n", tcpPayload.getByte(0));
System.out.printf("last payload byte=%d\n", tcpPayload.getByte(tcpPayload.size() -1));
I'm adding a JPayloadAccessor interface (see blog) to headers. This should make it easier to access more parts of the packets.
Thanks for the report and feedback. I'll take your issues under review and see if we need to modify the API.
Lastly, rc5 has support for Http header in package org.jnetpcap.protocol.tcpip. It also comes with analyzers, that are supposed to do all the hard work of reassembling the stream for you. They are not working properly, but that is how you are supposed to access the Http header, at least in theory. I'm fixing all the issues the we have found and will be releasing rc6 with the fixes soon.
I had tried the scan previously but I was trying to do it like this.
offlinePacket.scan(Tcp.ID);
Not sure what the difference is between JProtocol.TCP_ID and Tcp.ID?
Also the scan won't work for me until I used either Ethernet.ID or JProtocol.ETHERNET_ID.
I know the packet is a TCP packet so I'm not sure why the initial scan doesn't work.
Anther question I have is, once I have a TCP packet defined can I directly access the Ip4 packet header information or do I have to declare a whole new Ip4 packet? So do I have to actually do the following to get Ip4 header information.
offlinePacket.scan(Ethernet.ID); Tcp tcpT = new Tcp(); offlinePacket.hasHeader(tcpT); // Now that I have the TCP Packet and also need some IP header information, so do I need to do the following or is there a more direct way. Ip4 ipT = new Ip4(); offlinePacket.hasHeader(ipT);
Thanks a lot for the help.
> I had tried the scan previously but I was trying to do it like this.
> offlinePacket.scan(Tcp.ID);
> Not sure what the difference is between JProtocol.TCP_ID and Tcp.ID?
>
> Also the scan won't work for me until I used either Ethernet.ID or JProtocol.ETHERNET_ID.
The scan method needs the ID of the first header type found in the packet. Here is a typical packet that carries Http protocol:
[Ethernet][Ip4][Tcp][Http]
The first header in the packet is Ethernet (also called the DLT or Data Link Type since its usually the data link header that is first, but not neccessarily). So that is why you can't try to decode the packet starting with Tcp. Its not the first header. The decoder starts to decode the packet at byte offset 0 thinking its a Tcp header, it will fail quickly.
There is no difference between JProtocol.ETHENET_ID and Ethernet.ID. They are the same numerical id. If you look at the code in the ethernet header you will find this:
public static final int ID = JProtocol.ETHERNET_ID;
JProtocol enum table is the master controller for numerical IDs for the CORE PROTOCOLS ONLY. Core protocols are the protocols that are supplied with jNetPcap. User defined protocols, are assigned protocol IDs by a JRegistry class at the time a custom protocol is registered.
> I know the packet is a TCP packet so I'm not sure why the initial scan doesn't work.
Because the scan couldn't complete since it was trying to apply a Tcp.ID to the first header when its an Ethernet header. It doesn't report errors because it just figures that this is a partial packet with incomplete header and basically treats the entire packet as a payload of something. So basically your entire packet ends up with just one header type containing everything: Payload.
> Anther question I have is, once I have a TCP packet defined can I directly access the
> Ip4 packet header information or do I have to declare a whole new Ip4 packet? So do I have
> to actually do the following to get Ip4 header information.
Yes, but I think we have to define our terms here so we are both on the same page.
A Packet is both a buffer containing everything captured and also the java object that allows you access the headers, not as raw bytes, but as structured data.
A header decodes the structure of bytes found within a packet at certain offset into it.
So you are not creating a second packet, you are using Ip4 object to decode the Ip4 header within the packet you captured. You can create as many different types of headers as you want and then using methods getHeader and the shortcut method hasHeader setup the header object to point at the data within the packet. The packet/header objects figure out how to do. Once it suceeds you can just use accessor methods on the header to read the fields.
No data copies took place, the header is reading directly out of the buffer that contains the original packet. Also note, that the headers are reusable. You don't have to create a new instance for every iteration of you loop for example. Place the Ip4 and Tcp headers outside your loop, preferably as private fields, and just reuse them over and over.
final Tcp tcp = new Tcp();
final Ip4 ip = new Ip4();
while (keepGoing) {
PcapPacket packet = // pcap.nextEx, new PcapPacket, etc...
packet.scan(JProtocol.ETHERNET_ID); // or ETHERNET.ID if you prefer
if (packet.hasHeader(ip) && packet.hasHeader(tcp)) {
// Do processing, both ip and tcp have been initialized
}
}
Hope that helps and clarifies the usage.
Thanks, that clarifies the implementation considerably and yes I could have went poking through the code which I have done in the past.
Glad I could help. If you are up for some reading, take a look at the user guide as well:
(see #7 below)
I have a related problem. I'm capturing IPv4 packets (pings, etc) over ethernet with the following code but when I test for IPv4, it tells me the packets are not Ip4. Why ?
JBufferHandlerdumpHandler = new JBufferHandler () { public void nextPacket(PcapHeader header, JBuffer buffer, PcapDumper dumper ) { Ip4 ip4 = new Ip4(); try { PcapPacket packet = new PcapPacket( header, buffer ) ; packet.scan(JProtocol.ETHERNET_ID); if ( packet.hasHeader(ip4) ) { System.out.println( "The packet is an IP Packet!"); } else { System.out.println("Not IP"); } } catch (IllegalArgumentException e ) { System.out.printf("Exception: %s\n", e.getMessage() ); } System.out.printf( "Received %d-byte Packet!:\n", buffer.size() ); System.out.println( buffer.toHexdump() ); dumper.dump( header, buffer ); } };
First of, unless your handler is being called by multiple threads, move Ip4 declaration (line 5) outside the method as a private field. No need to instantiate new object, just reuse the same one over and over.
Second, make sure you are capturing on an ETHERNET NIC.
Third use some debug functions like: packet.toString() or packet.getState().toDebugString() or as last resort packet.toHexdump() and verify that it truely is an ethernet packet, no trailers or prefixes, etc...
Other than those checks, that I don't see anything wrong with your code.
The NIC is definitely an ethernet NIC. The packets are all pings (icmp). I have moved the Ip4 declaration.
1. When I print out the packet using "packet.toHexdump()", the ethernet packet is all there.
2. When I open the capture file produced by dumper.dump() in Wireshark, I see a valid ethernet packet.
3. The output from buffer.toHexdump() agrees with #1 above.
...but the Ip4 test still shows "Not IP".
?
Can you attach a small capture file so I can test your code with it. I have tested the scanner with my files and they all work.
Can you attach a small capture file so I can test your code with it. I have tested the scanner with my files and they all work.
The capture file has extension ".cap", of course, and this forum is setup to allow upload only of file extensions "jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp". Can you add "cap" to the list? Otherwise I could change the extension for the transmission but then it might be blocked by a firewall or other check (if there is one).
Sorry about that
Its fixed. Also before you upload did you try
packet.getState().toDebugString()
This will print out all the headers by ID that have been decoded. It dumps the native packet state structure to a String.