First time here? Check out the FAQ!
THIS IS A TEST INSTANCE. Feel free to ask and answer questions, but take care to avoid triggering too many notifications.
0

Trying to write Java raw InputStream data as PCAP to view in Wireshark

I'm trying to build a transparent proxy in Java with the ability to record data that passed through to be viewed later in wireshark.

I was able to get the proxy working correctly with this snippet

private static final int BUFFER_SIZE = 8192;

...

public void run() {
    PcapHandle handle = null;
    PcapDumper dumper;
    try {
        InetAddress addr = InetAddress.getByName("localhost");
        PcapNetworkInterface nif = Pcaps.getDevByAddress(addr);
        int snapLen = 65536;
        PcapNetworkInterface.PromiscuousMode mode = PcapNetworkInterface.PromiscuousMode.PROMISCUOUS;
        int timeout = 10;
        handle = nif.openLive(snapLen, mode, timeout);
        dumper = handle.dumpOpen("cap.pcap");
        byte[] buffer = new byte[BUFFER_SIZE];
        try {
            while (true) {
                int bytesRead = mInputStream.read(buffer);
                if (bytesRead == -1)
                    break; // End of stream is reached --> exit
                mOutputStream.write(buffer, 0, bytesRead);
                dumper.dumpRaw(Arrays.copyOfRange(buffer, 0, bytesRead));
                mOutputStream.flush();
            }
        } catch (IOException e) {
            // Read/write failed --> connection is broken
        }
        dumper.close();
    } catch (PcapNativeException e) {
        e.printStackTrace();
    } catch (UnknownHostException e) {
        e.printStackTrace();
    } catch (NotOpenException e) {
        e.printStackTrace();
    }
}

As you may notice I'm using Pcap4J to store raw bytes into a pcap file. The saving of the bytes works well but when I try to open it on wireshark it shows this message:

Error

And every packet shows as malformed. Ideally I would be seeing TCP and CQL (Cassandra) packets.

Can anyone tell me what I'm doing wrong here?

GGFPC's avatar
1
GGFPC
asked 2018-11-19 21:02:35 +0000, updated 2018-11-20 12:26:15 +0000
edit flag offensive 0 remove flag close merge delete

Comments

What is mInputStream? What class is it an instance of?

It needs somehow to be divided into packets, so that each .read call returns one packet. And if a packet is bigger than 262144 bytes, neither libpcap nor Wireshark support reading or writing it.

Guy Harris's avatar Guy Harris (2018-11-20 04:44:05 +0000) edit

It's just an InputStream directly from the Socket

GGFPC's avatar GGFPC (2018-11-20 12:23:59 +0000) edit
add a comment see more comments

2 Answers

0

You are reading raw packet data through the use of Pcap4J, but are not writing PCAP format files. You simply dump the raw packet data into a file. You need to add the file format structures are well.

Jaap's avatar
13.7k
Jaap
answered 2018-11-19 21:53:14 +0000
edit flag offensive 0 remove flag delete link

Comments

Thanks for the reply. I thought pcap4j already did that. Can you point me to a resource on how to do that!

GGFPC's avatar GGFPC (2018-11-20 00:36:48 +0000) edit

You simply dump the raw packet data into a file.

They're also writing it through libpcap - see the handle.dumpOpen and dumper.dumpRaw calls.

Guy Harris's avatar Guy Harris (2018-11-20 04:40:33 +0000) edit

Oh, I see. It's encapsulating pcap_dump_fopen() then. Still not seeing dumpRaw encapsulating pcap_dump(), where's the packet header info (in particular the size)?

Jaap's avatar Jaap (2018-11-20 07:28:26 +0000) edit

where's the packet header info

Nowhere. dumpRaw generates it.

in particular the size

It's the size of the array that was handed to it. The dumpRaw code in pcap4j does:

pcap_pkthdr header = new pcap_pkthdr();
header.len = header.caplen = packet.length;
header.ts = new timeval();
header.ts.tv_sec = new NativeLong(timestamp.getTime() / 1000L);
switch (timestampPrecision) {
  case MICRO:
    header.ts.tv_usec = new NativeLong(timestamp.getNanos() / 1000L);       
    break;
  case NANO:
    header.ts.tv_usec = new NativeLong(timestamp.getNanos());
    break;
  default:
    throw new AssertionError("Never get here.");
}

The astute reader will note that the time stamp is the current time, and the packet length and captured length are the length of the array.

In theory, that length should be 8192; however, it appears to be 268435456, from the error message.

268435456 is 0x10000000; I'm not sure how 0x2000 - 8192 - turned into 0x10000000. pcap4j eventually ends up calling libpcap's pcap_dump(), so I ... (more)

Guy Harris's avatar Guy Harris (2018-11-20 07:44:33 +0000) edit

Okay I managed to overcome the error by writing to the PcapDumper only the exact number of bytes that were read. But now everything shows as Ethernet on Wireshark. How does libpcap decode the protocol when capturing?

GGFPC's avatar GGFPC (2018-11-20 12:25:18 +0000) edit
add a comment see more comments
0

You would have to structure the InputStream data so that it could be divided into packets. For example, if you're writing raw packet data to the stream, before each packet you would write a number giving the size of the packet, in bytes.

Then, when reading the stream and writing a pcap file, you would read the number first, and then read that number of bytes of packet data. You would also have to ensure, in each iteration of the loop, the buffer would have to have exactly that number of bytes, so that dumper.dumpRaw knows how many bytes are in the packet.

This probably means that you should allocate the packet buffer separately, for every packet, and allocate it so that it's exactly the size of the packet data.

Without doing that, your code will NOT work.

Guy Harris's avatar
19.9k
Guy Harris
answered 2018-11-20 07:17:00 +0000
edit flag offensive 0 remove flag delete link

Comments

Hi, thanks for the answer.

I'm trying to write a transparent proxy for any protocol to record the data sent to a specific port. This is so I don't have to sniff all traffic with libpcap as I'm trying to write a monitoring tool and I want to reduce the overhead. This means I cant change what is being sent.

I changed my code to write to the file only the amount of data that has been read from the stream in that iteration, which I feel would be okay since only one packet is sent at a time.

Right now wireshark opens the capture without errors but everything appears as an ethernet frame.

How does wireshark or libpcap decode the packet protocol when sniffing directly from the interface?

GGFPC's avatar GGFPC (2018-11-20 12:34:57 +0000) edit

I changed my code to write to the file only the amount of data that has been read from the stream in that iteration, which I feel would be okay since only one packet is sent at a time.

If you're reading from a TCP socket, there is no guarantee that a single read will read a single TCP segment received from the remote host.

Right now wireshark opens the capture without errors but everything appears as an ethernet frame.

The PcapNetworkInterface you got from Pcaps.getDevByAddress is, on Linux, probably going to have "Ethernet" as its link-layer header type (that's the link-layer header type for the loopback interface on Linux). That's what you used to create the PcapDumper, so that's the link-layer header type it will have.

How does wireshark or libpcap decode the packet protocol when sniffing directly from the interface?

The OS indicates ... (more)

Guy Harris's avatar Guy Harris (2018-11-20 18:10:09 +0000) edit

Thanks once again.

Yes it is a TCP socket. In that case I think I'm out of options, other than sniffing with libpcap right?

GGFPC's avatar GGFPC (2018-11-20 19:10:17 +0000) edit

You could try

  1. using the openDead method of PcapHandle to create a fake handle for link-layer type DLT_RAW;
  2. using that to create the PcapDumper;
  3. for each chunk of data you read from the TCP socket, putting a fake IP header (with the right contents) and a fake TCP header in front of the chunk of data, and writing that to the file.
Guy Harris's avatar Guy Harris (2018-11-20 19:51:41 +0000) edit

I'm trying that with the following code

IpV4Packet p = new IpV4Packet.Builder()
                        .dstAddr((Inet4Address) Inet4Address.getByName("172.0.0.1"))
                        .srcAddr((Inet4Address) Inet4Address.getByName("172.0.0.2"))
                        .version(IpVersion.IPV4)
                        .protocol(IpNumber.TCP)
                        .ttl(new Integer(64).byteValue())
                        .options(Collections.singletonList(IpV4NoOperationOption.getInstance()))
                        .tos(new IpV4Rfc1349Tos.Builder().tos(IpV4TosTos.DEFAULT).precedence(IpV4TosPrecedence.ROUTINE).build())
                        .build();

                TcpPacket t = new TcpPacket.Builder()
                        .dstPort(new TcpPort((short)9042, "CQL"))
                        .srcPort(new TcpPort((short)47014, "Host"))
                        .correctLengthAtBuild(true)
                        .sequenceNumber(6)
                        .ack(true)
                        .psh(true)
                        .urgentPointer((short) 0)
                        .window((short) 229)
                        .build();

                byte[] arr = ArrayUtils.addAll(ArrayUtils.addAll(p.getHeader().getRawData(), t.getHeader().getRawData()),Arrays.copyOfRange(buffer, 0, bytesRead ));

'arr' is what I'm writing the file, but for some reason wireshark loads the packets as "Bogus Length, must be at least 20". If I add correctLengthAtBuild(true) it shows length 20 but malformed tcp packets.

Is this what you meant?

GGFPC's avatar GGFPC (2018-11-20 22:18:34 +0000) edit
add a comment see more comments

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account. This space is reserved only for answers. If you would like to engage in a discussion, please instead post a comment under the question or an answer that you would like to discuss.

Add Answer