Ask Your Question
0

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

asked 2018-11-19 21:02:35 +0000

GGFPC gravatar image

updated 2018-11-20 12:26:15 +0000

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?

edit retag flag offensive 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 gravatar imageGuy Harris ( 2018-11-20 04:44:05 +0000 )edit

It's just an InputStream directly from the Socket

GGFPC gravatar imageGGFPC ( 2018-11-20 12:23:59 +0000 )edit

2 Answers

Sort by ยป oldest newest most voted
0

answered 2018-11-20 07:17:00 +0000

Guy Harris gravatar image

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.

edit flag offensive delete link more

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 gravatar imageGGFPC ( 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 gravatar imageGuy 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 gravatar imageGGFPC ( 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 gravatar imageGuy 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 gravatar imageGGFPC ( 2018-11-20 22:18:34 +0000 )edit
0

answered 2018-11-19 21:53:14 +0000

Jaap gravatar image

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.

edit flag offensive delete link more

Comments

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

GGFPC gravatar imageGGFPC ( 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 gravatar imageGuy 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 gravatar imageJaap ( 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 gravatar imageGuy 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 gravatar imageGGFPC ( 2018-11-20 12:25:18 +0000 )edit

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools

1 follower

Stats

Asked: 2018-11-19 21:02:35 +0000

Seen: 59 times

Last updated: Nov 20