diff --git a/codec/src/main/java/pcap/codec/ethernet/Ethernet.java b/codec/src/main/java/pcap/codec/ethernet/Ethernet.java index 419ce021..60c41b81 100644 --- a/codec/src/main/java/pcap/codec/ethernet/Ethernet.java +++ b/codec/src/main/java/pcap/codec/ethernet/Ethernet.java @@ -53,7 +53,7 @@ public static Ethernet newInstance(int size, PacketBuffer buffer) { * @since 1.0.0 */ public MacAddress destination() { - return MacAddress.valueOf((superBuffer.getLong(destinationOffset) >> 16) & 0xffffffffffffL); + return MacAddress.fromLong((superBuffer.getLong(destinationOffset) >> 16) & 0xffffffffffffL); } /** @@ -75,7 +75,7 @@ public Ethernet destination(MacAddress macAddress) { * @since 1.0.0 */ public MacAddress source() { - return MacAddress.valueOf((superBuffer.getLong(sourceOffset) >> 16) & 0xffffffffffffL); + return MacAddress.fromLong((superBuffer.getLong(sourceOffset) >> 16) & 0xffffffffffffL); } /** diff --git a/common/src/main/java/pcap/common/net/MacAddress.java b/common/src/main/java/pcap/common/net/MacAddress.java index b9399d35..f05e7a4c 100644 --- a/common/src/main/java/pcap/common/net/MacAddress.java +++ b/common/src/main/java/pcap/common/net/MacAddress.java @@ -23,19 +23,19 @@ public final class MacAddress implements Serializable { private static final Pattern REGEX = Pattern.compile("[:-]"); /** Zero MAC Address (00:00:00:00:00:00). */ - public static final MacAddress ZERO = valueOf("00:00:00:00:00:00"); + public static final MacAddress ZERO = fromString("00:00:00:00:00:00"); /** Dummy MAC Address (de:ad:be:ef:c0:fe). */ - public static final MacAddress DUMMY = valueOf("de:ad:be:ef:c0:fe"); + public static final MacAddress DUMMY = fromString("de:ad:be:ef:c0:fe"); /** Broadcast MAC Address (ff:ff:ff:ff:ff:ff). */ - public static final MacAddress BROADCAST = valueOf("ff:ff:ff:ff:ff:ff"); + public static final MacAddress BROADCAST = fromString("ff:ff:ff:ff:ff:ff"); /** Multicast Address. */ - public static final MacAddress IPV4_MULTICAST = valueOf("01:00:5e:00:00:00"); + public static final MacAddress IPV4_MULTICAST = fromString("01:00:5e:00:00:00"); /** Multicast mask. */ - public static final MacAddress IPV4_MULTICAST_MASK = valueOf("ff:ff:ff:80:00:00"); + public static final MacAddress IPV4_MULTICAST_MASK = fromString("ff:ff:ff:80:00:00"); private final byte[] address; @@ -99,7 +99,7 @@ public static MacAddress fromString(String stringAddress) { bytes[offset] = (byte) ((hi << 4) + lo); offset += 1; } - return valueOf(bytes); + return fromBytes(bytes); } /** @@ -109,6 +109,7 @@ public static MacAddress fromString(String stringAddress) { * @return an Mac address object. * @since 1.0.0 */ + @Deprecated public static MacAddress valueOf(String stringAddress) { Validate.notIllegalArgument( !Strings.blank(stringAddress), "Address must be not empty or blank."); @@ -130,6 +131,7 @@ public static MacAddress valueOf(String stringAddress) { * @return an Mac address object. * @since 1.0.0 */ + @Deprecated public static MacAddress valueOf(final byte[] bytesAddress) { return fromBytes(bytesAddress); } @@ -152,6 +154,7 @@ public static MacAddress fromBytes(final byte[] bytes) { * @return an Mac address object. * @since 1.0.0 */ + @Deprecated public static MacAddress valueOf(final long longAddress) { return fromLong(longAddress); } @@ -175,7 +178,7 @@ public static MacAddress fromLong(final long longAddress) { (byte) (longAddress >> 8 & 0xff), (byte) (longAddress & 0xff) }; - return valueOf(bytes); + return fromBytes(bytes); } /** diff --git a/jdk7/src/main/java/pcap/jdk7/internal/BerkeleyPacketFilter.java b/jdk7/src/main/java/pcap/jdk7/internal/BerkeleyPacketFilter.java index 035ec022..06543c69 100644 --- a/jdk7/src/main/java/pcap/jdk7/internal/BerkeleyPacketFilter.java +++ b/jdk7/src/main/java/pcap/jdk7/internal/BerkeleyPacketFilter.java @@ -8,12 +8,16 @@ import com.sun.jna.Structure; import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; +import java.nio.ByteBuffer; import java.util.Collections; import java.util.HashSet; import java.util.Set; + +import pcap.common.util.Strings; import pcap.spi.PacketBuffer; import pcap.spi.PacketFilter; import pcap.spi.exception.ErrorException; +import pcap.spi.util.Consumer; class BerkeleyPacketFilter implements PacketFilter { @@ -72,6 +76,42 @@ public boolean filter(PacketBuffer packetBuffer) { return r != 0 && r != 4294967295L; } + @Override + public void dump(Consumer consumer) { + checkOpenState(); + fp.bf_insns.read(); + final int n = fp.bf_len; + NativeMappings.bpf_insn.ByReference insn = fp.bf_insns; + int size = insn.size(); + for (int i = 0; i < n; i++) { + consumer.accept(NativeMappings.bpf_image(insn, i)); + final Pointer pointer = insn.getPointer().share(size); + insn = new NativeMappings.bpf_insn.ByReference(pointer); + insn.read(); + } + } + + @Override + public byte[] bytes() { + checkOpenState(); + fp.bf_insns.read(); + final int n = fp.bf_len; + NativeMappings.bpf_insn.ByReference insn = fp.bf_insns; + final int size = insn.size(); + final ByteBuffer buffer = ByteBuffer.allocate(size * n + 4); + buffer.putInt(n); + for (int i = 0; i < n; i++) { + buffer.putShort(insn.code); + buffer.put(insn.jt); + buffer.put(insn.jf); + buffer.putInt(insn.k); + final Pointer pointer = insn.getPointer().share(size); + insn = new NativeMappings.bpf_insn.ByReference(pointer); + insn.read(); + } + return buffer.array(); + } + @Override public void close() throws Exception { checkOpenState(); @@ -79,6 +119,15 @@ public void close() throws Exception { cleaner.pointer = 0L; } + @Override + public String toString() { + try { + return Strings.hex(bytes()); + } catch (Exception e) { + return ""; + } + } + void checkOpenState() { if (cleaner.pointer == 0L) { throw new IllegalStateException("Bpf program is closed."); diff --git a/jdk7/src/main/java/pcap/jdk7/internal/NativeMappings.java b/jdk7/src/main/java/pcap/jdk7/internal/NativeMappings.java index 1bfb6b6d..f7c6da1c 100644 --- a/jdk7/src/main/java/pcap/jdk7/internal/NativeMappings.java +++ b/jdk7/src/main/java/pcap/jdk7/internal/NativeMappings.java @@ -320,6 +320,11 @@ static native Pointer pcap_open_live( static native long bpf_filter( NativeMappings.bpf_insn insn, Pointer packet, int oriPktLen, int pktLen); + @NativeSignature( + signature = "char * bpf_image(const struct bpf_insn *p, int n)", + since = @Version(major = 0, minor = 4)) + static native String bpf_image(NativeMappings.bpf_insn insn, int n); + static InetAddress inetAddress(sockaddr sockaddr) { if (sockaddr == null) { return null; @@ -775,6 +780,10 @@ public bpf_insn() { // public constructor } + public bpf_insn(Pointer ptr) { + super(ptr); + } + @Override protected List getFieldOrder() { return Arrays.asList( @@ -784,7 +793,16 @@ protected List getFieldOrder() { "k"); } - public static final class ByReference extends bpf_insn implements Structure.ByReference {} + public static final class ByReference extends bpf_insn implements Structure.ByReference { + + public ByReference() { + super(); + } + + public ByReference(Pointer ptr) { + super(ptr); + } + } } public static class sockaddr extends Structure { diff --git a/jdk7/src/test/java/pcap/jdk7/internal/BerkleyPacketFilterTest.java b/jdk7/src/test/java/pcap/jdk7/internal/BerkleyPacketFilterTest.java index 81e3c9a1..c254535c 100644 --- a/jdk7/src/test/java/pcap/jdk7/internal/BerkleyPacketFilterTest.java +++ b/jdk7/src/test/java/pcap/jdk7/internal/BerkleyPacketFilterTest.java @@ -8,12 +8,14 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.function.Executable; import pcap.common.util.Hexs; +import pcap.common.util.Strings; import pcap.spi.Interface; import pcap.spi.PacketBuffer; import pcap.spi.PacketFilter; import pcap.spi.Pcap; import pcap.spi.Service; import pcap.spi.option.DefaultLiveOptions; +import pcap.spi.util.Consumer; class BerkleyPacketFilterTest extends BaseTest { @@ -28,6 +30,38 @@ void clean() throws Exception { } } + @Test + void dump() throws Exception { + Service service = Service.Creator.create("PcapService"); + Interface lo = loopbackInterface(service); + try (Pcap live = service.live(lo, new DefaultLiveOptions())) { + try (BerkeleyPacketFilter bpf = (BerkeleyPacketFilter) live.compile("icmp", false)) { + final StringBuilder sb = new StringBuilder(); + bpf.dump( + new Consumer() { + @Override + public void accept(String s) { + sb.append(s).append('\n'); + } + }); + if (live.datalink() == 1) { + final String str = "(000) ldh [12]\n" + + "(001) jeq #0x800 jt 2\tjf 5\n" + + "(002) ldb [23]\n" + + "(003) jeq #0x1 jt 4\tjf 5\n" + + "(004) ret #65535\n" + + "(005) ret #0\n"; + Assertions.assertEquals(str, sb.toString()); + } else if (live.datalink() == 113) { + + } else if (live.datalink() == 0) { + + } + Assertions.assertNotNull(bpf); + } + } + } + @Test void close() throws Exception { Service service = Service.Creator.create("PcapService"); @@ -47,6 +81,20 @@ public void execute() throws Throwable { } } + @Test + void bytes() throws Exception { + final Service service = Service.Creator.create("PcapService"); + final Interface lo = loopbackInterface(service); + try (final Pcap live = service.live(lo, new DefaultLiveOptions())) { + try (final BerkeleyPacketFilter bpf = (BerkeleyPacketFilter) live.compile("icmp", false)) { + final byte[] bytes = bpf.bytes(); + final String str = Strings.hex(bytes); + Assertions.assertNotNull(str); + Assertions.assertNotNull(bpf); + } + } + } + @Test void filter() throws Exception { String hexStream = diff --git a/spi/src/main/java/pcap/spi/PacketFilter.java b/spi/src/main/java/pcap/spi/PacketFilter.java index 80d5e215..1594f8dd 100644 --- a/spi/src/main/java/pcap/spi/PacketFilter.java +++ b/spi/src/main/java/pcap/spi/PacketFilter.java @@ -4,6 +4,9 @@ */ package pcap.spi; +import pcap.spi.annotation.Incubating; +import pcap.spi.util.Consumer; + /** * Packet filter. * @@ -19,4 +22,22 @@ public interface PacketFilter extends AutoCloseable { * @since 1.5.0 */ boolean filter(PacketBuffer packetBuffer); + + /** + * Dump the compiled packet-matching code in a human readable form. Please mind that although code + * compilation is always DLT-specific, typically it is impossible (and unnecessary) to specify + * which DLT to use for the dump. + * + * @param consumer consumer. + */ + @Incubating + void dump(Consumer consumer); + + /** + * Get byte code. + * + * @return returns byte code. + */ + @Incubating + byte[] bytes(); } diff --git a/tests/src/test/java/pcap/tests/BytesToHexBenchmark.java b/tests/src/test/java/pcap/tests/BytesToHexBenchmark.java new file mode 100644 index 00000000..bba802da --- /dev/null +++ b/tests/src/test/java/pcap/tests/BytesToHexBenchmark.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2020-2023 Pcap Project + * SPDX-License-Identifier: MIT OR Apache-2.0 + */ +package pcap.tests; + +import java.util.Random; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import pcap.common.util.Strings; + +public class BytesToHexBenchmark { + + private static final byte[] BYTES8 = new byte[8]; + private static final byte[] BYTES16 = new byte[16]; + private static final byte[] BYTES32 = new byte[32]; + private static final byte[] BYTES64 = new byte[64]; + private static final byte[] BYTES128 = new byte[128]; + private static final byte[] BYTES256 = new byte[256]; + private static final byte[] BYTES512 = new byte[512]; + private static final byte[] BYTES1024 = new byte[1024]; + private static final byte[] BYTES2048 = new byte[2048]; + private static final byte[] BYTES4096 = new byte[4096]; + private static final byte[] BYTES8192 = new byte[8192]; + + static { + final Random random = new Random(); + random.nextBytes(BYTES8); + random.nextBytes(BYTES16); + random.nextBytes(BYTES32); + random.nextBytes(BYTES64); + random.nextBytes(BYTES128); + random.nextBytes(BYTES256); + random.nextBytes(BYTES512); + random.nextBytes(BYTES1024); + random.nextBytes(BYTES2048); + random.nextBytes(BYTES4096); + random.nextBytes(BYTES8192); + } + + public static void main(String[] args) throws RunnerException { + Options opt = + new OptionsBuilder().include(BytesToHexBenchmark.class.getSimpleName()).forks(1).build(); + new Runner(opt).run(); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void bytes8ToHex() { + Strings.hex(BYTES8); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void bytes16ToHex() { + Strings.hex(BYTES16); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void bytes32ToHex() { + Strings.hex(BYTES32); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void bytes64ToHex() { + Strings.hex(BYTES64); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void bytes128ToHex() { + Strings.hex(BYTES128); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void bytes256ToHex() { + Strings.hex(BYTES256); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void bytes512ToHex() { + Strings.hex(BYTES512); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void bytes1024ToHex() { + Strings.hex(BYTES1024); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void bytes2048ToHex() { + Strings.hex(BYTES2048); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void bytes4096ToHex() { + Strings.hex(BYTES4096); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void bytes8192ToHex() { + Strings.hex(BYTES8192); + } +} diff --git a/tests/src/test/java/pcap/tests/HexToBytesBenchmark.java b/tests/src/test/java/pcap/tests/HexToBytesBenchmark.java new file mode 100644 index 00000000..177e7c8e --- /dev/null +++ b/tests/src/test/java/pcap/tests/HexToBytesBenchmark.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2020-2023 Pcap Project + * SPDX-License-Identifier: MIT OR Apache-2.0 + */ +package pcap.tests; + +import java.util.Random; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import pcap.common.util.Hexs; +import pcap.common.util.Strings; + +public class HexToBytesBenchmark { + + private static final String STR8; + private static final String STR16; + private static final String STR32; + private static final String STR64; + private static final String STR128; + private static final String STR256; + private static final String STR512; + private static final String STR1024; + private static final String STR2048; + private static final String STR4096; + private static final String STR8192; + + static { + final byte[] BYTES8 = new byte[8]; + final byte[] BYTES16 = new byte[16]; + final byte[] BYTES32 = new byte[32]; + final byte[] BYTES64 = new byte[64]; + final byte[] BYTES128 = new byte[128]; + final byte[] BYTES256 = new byte[256]; + final byte[] BYTES512 = new byte[512]; + final byte[] BYTES1024 = new byte[1024]; + final byte[] BYTES2048 = new byte[2048]; + final byte[] BYTES4096 = new byte[4096]; + final byte[] BYTES8192 = new byte[8192]; + final Random random = new Random(); + random.nextBytes(BYTES8); + random.nextBytes(BYTES16); + random.nextBytes(BYTES32); + random.nextBytes(BYTES64); + random.nextBytes(BYTES128); + random.nextBytes(BYTES256); + random.nextBytes(BYTES512); + random.nextBytes(BYTES1024); + random.nextBytes(BYTES2048); + random.nextBytes(BYTES4096); + random.nextBytes(BYTES8192); + STR8 = Strings.hex(BYTES8); + STR16 = Strings.hex(BYTES8); + STR32 = Strings.hex(BYTES8); + STR64 = Strings.hex(BYTES8); + STR128 = Strings.hex(BYTES8); + STR256 = Strings.hex(BYTES8); + STR512 = Strings.hex(BYTES8); + STR1024 = Strings.hex(BYTES8); + STR2048 = Strings.hex(BYTES8); + STR4096 = Strings.hex(BYTES8); + STR8192 = Strings.hex(BYTES8); + } + + public static void main(String[] args) throws RunnerException { + Options opt = + new OptionsBuilder().include(HexToBytesBenchmark.class.getSimpleName()).forks(1).build(); + new Runner(opt).run(); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void str8ToBytes() { + Hexs.parseHex(STR8); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void str16ToBytes() { + Hexs.parseHex(STR16); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void str32ToBytes() { + Hexs.parseHex(STR32); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void str64ToBytes() { + Hexs.parseHex(STR64); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void str128ToBytes() { + Hexs.parseHex(STR128); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void str256ToBytes() { + Hexs.parseHex(STR256); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void str512ToBytes() { + Hexs.parseHex(STR512); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void str1024ToBytes() { + Hexs.parseHex(STR1024); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void str2048ToBytes() { + Hexs.parseHex(STR2048); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void str4096ToBytes() { + Hexs.parseHex(STR4096); + } + + @Warmup(iterations = 2) // Warmup Iteration = 3 + @Measurement(iterations = 3) + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void str8192ToBytes() { + Hexs.parseHex(STR8192); + } +}