diff --git a/README.md b/README.md
index 2d66f75..2309d31 100644
--- a/README.md
+++ b/README.md
@@ -22,22 +22,16 @@ Sample code:
handle.setFilter(filter);
filter.free();
- LinkType datalink = this.handle.datalink();
- new Thread(() -> {
+ LinkType datalink = handle.datalink();
+ handle.loop(-1, (packetHeader, rawPacket) -> {
try {
- this.handle.loop(-1, (packetHeader, rawPacket) -> {
- try {
- Packet packet = new Packet();
- packet.decode(rawPacket, datalink);
- System.out.println(packet);
- dumper.dump(packetHeader, rawPacket);
- dumper.flush();
- } catch (LayerDecodeException | PcapException e) {
- e.printStackTrace();
- }
- });
- } catch (PcapException e) {
- e.printStackTrace();
+ Packet packet = new Packet();
+ packet.decode(rawPacket, datalink);
+ System.out.println(packet);
+ dumper.dump(packetHeader, rawPacket);
+ dumper.flush();
+ } catch (LayerDecodeException | PcapException e) {
+ e.printStackTrace();
}
});
diff --git a/src/main/java/net/elytrium/pcap/layer/ARP.java b/src/main/java/net/elytrium/pcap/layer/ARP.java
new file mode 100644
index 0000000..6652729
--- /dev/null
+++ b/src/main/java/net/elytrium/pcap/layer/ARP.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2022 - 2023 Elytrium
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package net.elytrium.pcap.layer;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Supplier;
+import net.elytrium.pcap.layer.data.EthernetProtocol;
+import net.elytrium.pcap.layer.exception.LayerDecodeException;
+import net.elytrium.pcap.layer.exception.LayerEncodeException;
+
+public class ARP implements Layer {
+
+ public enum HardwareType {
+ ETHERNET(1),
+ EXPERIMENTAL_ETHERNET(2),
+ AMATEUR_RADIO_AX_25(3),
+ PROTEON_PRONET_TOKEN_RING(4),
+ CHAOS(5),
+ IEEE802_NETWORKS(6),
+ ARCNET(7),
+ HYPERCHANNEL(8),
+ LANSTAR(9),
+ AUTONET_SHORT_ADDRESS(10),
+ LOCALTALK(11),
+ LOCALNET(12),
+ ULTRA_LINK(13),
+ SMDS(14),
+ FRAME_RELAY(15),
+ HDLC(17),
+ FIBRE_CHANNEL(18),
+ ATM(19),
+ SERIAL_LINE(20),
+ MIL_STD_188_220(22),
+ METRICOM(23),
+ IEEE1394_1995(24),
+ MAPOS(25),
+ TWINAXIAL(26),
+ EUI_64(27),
+ HIPARP(28),
+ IP_ARP_OVER_ISO_7816_3(29),
+ ARPSEC(30),
+ IPSEC_TUNNEL(31),
+ INFINIBAND(32),
+ TIA_102_PROJECT_25_CAI(33),
+ WIEGAND_INTERFACE(34),
+ PURE_IP(35),
+ HW_EXP1(36),
+ HFI(37),
+ UNIFIED_BUS(38),
+ HW_EXP2(256),
+ AETHERNET(257);
+
+ private static final Map REGISTRY = new HashMap<>();
+
+ static {
+ for (HardwareType type : values()) {
+ REGISTRY.put(type.getValue(), type);
+ }
+ }
+
+ final int value;
+
+ HardwareType(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return this.value;
+ }
+
+ public static HardwareType getByValue(int value) {
+ return REGISTRY.get(value);
+ }
+ }
+
+ public enum Operation {
+ REQUEST(1),
+ REPLY(2),
+ REQUEST_REVERSE(3),
+ REPLY_REVERSE(4),
+ DRARP_REQUEST(5),
+ DRARP_REPLY(6),
+ DRARP_ERROR(7),
+ INARP_REQUEST(8),
+ INARP_REPLY(9),
+ ARP_NAK(10),
+ MARS_REQUEST(11),
+ MARS_MULTI(12),
+ MARS_MSERV(13),
+ MARS_JOIN(14),
+ MARS_LEAVE(15),
+ MARS_NAK(16),
+ MARS_UNSERV(17),
+ MARS_SJOIN(18),
+ MARS_SLEAVE(19),
+ MARS_GROUPLIST_REQUEST(20),
+ MARS_GROUPLIST_REPLY(21),
+ MARS_REDIRECT_MAP(22),
+ MAPOS_UNARP(23),
+ OP_EXP1(24),
+ OP_EXP2(25);
+
+ private static final Map REGISTRY = new HashMap<>();
+
+ static {
+ for (Operation operation : values()) {
+ REGISTRY.put(operation.getValue(), operation);
+ }
+ }
+
+ final int value;
+
+ Operation(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return this.value;
+ }
+
+ public static Operation getByValue(int value) {
+ return REGISTRY.get(value);
+ }
+ }
+
+ private HardwareType hardwareType;
+ private EthernetProtocol protocolType;
+ private int hardwareLength;
+ private int protocolLength;
+ private Operation operation;
+ private byte[] senderHardwareAddress;
+ private byte[] senderProtocolAddress;
+ private byte[] targetHardwareAddress;
+ private byte[] targetProtocolAddress;
+
+ @Override
+ public void decode(ByteBuffer buffer) throws LayerDecodeException {
+ if (buffer.remaining() < 8) {
+ throw new LayerDecodeException("ARP packet is too small.");
+ }
+
+ this.hardwareType = HardwareType.getByValue(Short.toUnsignedInt(buffer.getShort()));
+ this.protocolType = EthernetProtocol.getByValue(Short.toUnsignedInt(buffer.getShort()));
+ this.hardwareLength = Byte.toUnsignedInt(buffer.get());
+ this.protocolLength = Byte.toUnsignedInt(buffer.get());
+ this.operation = Operation.getByValue(Short.toUnsignedInt(buffer.getShort()));
+ this.senderHardwareAddress = new byte[this.hardwareLength];
+ buffer.get(this.senderHardwareAddress);
+ this.senderProtocolAddress = new byte[this.protocolLength];
+ buffer.get(this.protocolLength);
+ this.targetHardwareAddress = new byte[this.hardwareLength];
+ buffer.get(this.targetHardwareAddress);
+ this.targetProtocolAddress = new byte[this.protocolLength];
+ buffer.get(this.targetProtocolAddress);
+ }
+
+ @Override
+ public void encode(ByteBuffer buffer) throws LayerEncodeException {
+ if (buffer.remaining() < this.getSize()) {
+ throw new LayerEncodeException("ByteBuffer is too small.");
+ }
+
+ buffer.putShort((short) this.hardwareType.getValue());
+ buffer.putShort((short) this.protocolType.getValue());
+ buffer.put((byte) this.hardwareLength);
+ buffer.put((byte) this.protocolLength);
+ buffer.putShort((short) this.operation.getValue());
+ buffer.put(this.senderHardwareAddress);
+ buffer.put(this.senderProtocolAddress);
+ buffer.put(this.targetHardwareAddress);
+ buffer.put(this.targetProtocolAddress);
+ }
+
+ @Override
+ public int getSize() {
+ return 8 + this.hardwareLength * 2 + this.protocolLength * 2;
+ }
+
+ @Override
+ public Supplier nextLayer() {
+ return null;
+ }
+
+ public HardwareType getHardwareType() {
+ return this.hardwareType;
+ }
+
+ public void setHardwareType(HardwareType hardwareType) {
+ this.hardwareType = hardwareType;
+ }
+
+ public EthernetProtocol getProtocolType() {
+ return this.protocolType;
+ }
+
+ public void setProtocolType(EthernetProtocol protocolType) {
+ this.protocolType = protocolType;
+ }
+
+ public int getHardwareLength() {
+ return this.hardwareLength;
+ }
+
+ public void setHardwareLength(int hardwareLength) {
+ this.hardwareLength = hardwareLength;
+ }
+
+ public int getProtocolLength() {
+ return this.protocolLength;
+ }
+
+ public void setProtocolLength(int protocolLength) {
+ this.protocolLength = protocolLength;
+ }
+
+ public Operation getOperation() {
+ return this.operation;
+ }
+
+ public void setOperation(Operation operation) {
+ this.operation = operation;
+ }
+
+ public byte[] getSenderHardwareAddress() {
+ return this.senderHardwareAddress;
+ }
+
+ public void setSenderHardwareAddress(byte[] senderHardwareAddress) {
+ this.senderHardwareAddress = senderHardwareAddress;
+ }
+
+ public byte[] getSenderProtocolAddress() {
+ return this.senderProtocolAddress;
+ }
+
+ public void setSenderProtocolAddress(byte[] senderProtocolAddress) {
+ this.senderProtocolAddress = senderProtocolAddress;
+ }
+
+ public byte[] getTargetHardwareAddress() {
+ return this.targetHardwareAddress;
+ }
+
+ public void setTargetHardwareAddress(byte[] targetHardwareAddress) {
+ this.targetHardwareAddress = targetHardwareAddress;
+ }
+
+ public byte[] getTargetProtocolAddress() {
+ return this.targetProtocolAddress;
+ }
+
+ public void setTargetProtocolAddress(byte[] targetProtocolAddress) {
+ this.targetProtocolAddress = targetProtocolAddress;
+ }
+
+ @Override
+ public String toString() {
+ return "ARP{"
+ + "hardwareType=" + this.hardwareType
+ + ", protocolType=" + this.protocolType
+ + ", hardwareLength=" + this.hardwareLength
+ + ", protocolLength=" + this.protocolLength
+ + ", operation=" + this.operation
+ + ", senderHardwareAddress=" + Arrays.toString(this.senderHardwareAddress)
+ + ", senderProtocolAddress=" + Arrays.toString(this.senderProtocolAddress)
+ + ", targetHardwareAddress=" + Arrays.toString(this.targetHardwareAddress)
+ + ", targetProtocolAddress=" + Arrays.toString(this.targetProtocolAddress)
+ + '}';
+ }
+}
diff --git a/src/main/java/net/elytrium/pcap/layer/ICMP.java b/src/main/java/net/elytrium/pcap/layer/ICMP.java
new file mode 100644
index 0000000..f0c3c23
--- /dev/null
+++ b/src/main/java/net/elytrium/pcap/layer/ICMP.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2022 - 2023 Elytrium
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package net.elytrium.pcap.layer;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Supplier;
+import net.elytrium.pcap.layer.exception.LayerDecodeException;
+import net.elytrium.pcap.layer.exception.LayerEncodeException;
+
+public class ICMP implements Layer {
+
+ public enum Type {
+ ECHO_REPLY(0, EchoReply.class),
+ DESTINATION_UNREACHABLE(3, DestinationUnreachable.class),
+ SOURCE_QUENCH(4, SourceQuench.class),
+ REDIRECT_MESSAGE(5, RedirectMessage.class),
+ ECHO_REQUEST(8, EchoRequest.class),
+ ROUTER_ADVERTISEMENT(9, RouterAdvertisement.class),
+ ROUTER_SOLICITATION(10, RouterSolicitation.class),
+ TIME_EXCEEDED(11, TimeExceeded.class),
+ BAD_IP_HEADER(12, BadIPHeader.class),
+ TIMESTAMP(13, Timestamp.class),
+ TIMESTAMP_REPLY(14, TimestampReply.class),
+ INFORMATION_REQUEST(15, InformationRequest.class),
+ INFORMATION_REPLY(16, InformationReply.class),
+ ADDRESS_MASK_REQUEST(17, AddressMaskRequest.class),
+ ADDRESS_MASK_REPLY(18, AddressMaskReply.class),
+ TRACEROUTE(30, Traceroute.class),
+ EXTENDED_ECHO_REQUEST(42, ExtendedEchoRequest.class),
+ EXTENDED_ECHO_RESPONSE(43, ExtendedEchoResponse.class);
+
+ private static final Map VALUE_REGISTRY = new HashMap<>();
+ private static final Map>, Type> ENUM_REGISTRY = new HashMap<>();
+
+ static {
+ for (Type type : values()) {
+ VALUE_REGISTRY.put(type.getValue(), type);
+ ENUM_REGISTRY.put(type.getEnumClass(), type);
+ }
+ }
+
+ private final int value;
+ private final Class extends Enum>> enumClass;
+
+ Type(int value, Class extends Enum>> enumClass) {
+ this.value = value;
+ this.enumClass = enumClass;
+ }
+
+ public int getValue() {
+ return this.value;
+ }
+
+ public Class extends Enum>> getEnumClass() {
+ return this.enumClass;
+ }
+
+ public static Type getByValue(int value) {
+ return VALUE_REGISTRY.get(value);
+ }
+
+ public static Type getByEnum(@SuppressWarnings("rawtypes") Class extends Enum> cls) {
+ return ENUM_REGISTRY.get(cls);
+ }
+
+ public static Type getByEnum(Enum> value) {
+ return value == null ? null : getByEnum(value.getClass());
+ }
+ }
+
+ public enum EchoReply {
+ ECHO_REPLY
+ }
+
+ public enum DestinationUnreachable {
+ DESTINATION_NETWORK_UNREACHABLE,
+ DESTINATION_HOST_UNREACHABLE,
+ DESTINATION_PROTOCOL_UNREACHABLE,
+ DESTINATION_PORT_UNREACHABLE,
+ FRAGMENTATION_REQUIRED,
+ SOURCE_ROUTE_FAILED,
+ DESTINATION_NETWORK_UNKNOWN,
+ DESTINATION_HOST_UNKNOWN,
+ SOURCE_HOST_ISOLATED,
+ NETWORK_ADMINISTRATIVELY_PROHIBITED,
+ HOST_ADMINISTRATIVELY_PROHIBITED,
+ NETWORK_UNREACHABLE_TOS,
+ HOST_UNREACHABLE_TOS,
+ COMMUNICATION_ADMINISTRATIVELY_PROHIBITED,
+ HOST_PRECEDENCE_VIOLATION,
+ PRECEDENCE_CUTOFF_IN_EFFECT
+ }
+
+ public enum SourceQuench {
+ SOURCE_QUENCH
+ }
+
+ public enum RedirectMessage {
+ REDIRECT_DATAGRAM_NETWORK,
+ REDIRECT_DATAGRAM_HOST,
+ REDIRECT_DATAGRAM_NETWORK_TOS,
+ REDIRECT_DATAGRAM_HOST_TOS
+ }
+
+ public enum EchoRequest {
+ ECHO_REQUEST
+ }
+
+ public enum RouterAdvertisement {
+ ROUTER_ADVERTISEMENT
+ }
+
+ public enum RouterSolicitation {
+ ROUTER_SOLICITATION
+ }
+
+ public enum TimeExceeded {
+ TTL_EXPIRED,
+ FRAGMENT_REASSEMBLY
+ }
+
+ public enum BadIPHeader {
+ MISSING_REQUIRED_OPTION,
+ BAD_LENGTH
+ }
+
+ public enum Timestamp {
+ TIMESTAMP
+ }
+
+ public enum TimestampReply {
+ TIMESTAMP_REPLY
+ }
+
+ public enum InformationRequest {
+ INFORMATION_REQUEST
+ }
+
+ public enum InformationReply {
+ INFORMATION_REPLY
+ }
+
+ public enum AddressMaskRequest {
+ ADDRESS_MASK_REQUEST
+ }
+
+ public enum AddressMaskReply {
+ ADDRESS_MASK_REPLY
+ }
+
+ public enum Traceroute {
+ TRACEROUTE
+ }
+
+ public enum ExtendedEchoRequest {
+ EXTENDED_ECHO_REQUEST
+ }
+
+ public enum ExtendedEchoResponse {
+ NO_ERROR,
+ MALFORMED_QUERY,
+ NO_SUCH_INTERFACE,
+ NO_SUCH_TABLE_ENTRY,
+ MULTIPLE_INTERFACES_SATISFY_QUERY
+ }
+
+ private static final int SIZE = 8;
+
+ private Type type;
+ private int code;
+ private short checksum;
+ private int data;
+
+ @Override
+ public void decode(ByteBuffer buffer) throws LayerDecodeException {
+ if (buffer.remaining() < SIZE) {
+ throw new LayerDecodeException("ICMP header is too small.");
+ }
+
+ this.type = Type.getByValue(Byte.toUnsignedInt(buffer.get()));
+ this.code = Byte.toUnsignedInt(buffer.get());
+ this.checksum = buffer.getShort();
+ this.data = buffer.getInt();
+ }
+
+ @Override
+ public void encode(ByteBuffer buffer) throws LayerEncodeException {
+ if (buffer.remaining() < SIZE) {
+ throw new LayerEncodeException("ByteBuffer is too small.");
+ }
+
+ buffer.put((byte) this.type.getValue());
+ buffer.put((byte) this.code);
+ buffer.putShort(this.checksum);
+ buffer.putInt(this.data);
+ }
+
+ @Override
+ public int getSize() {
+ return SIZE;
+ }
+
+ @Override
+ public Supplier nextLayer() {
+ return null;
+ }
+
+ public Type getType() {
+ return this.type;
+ }
+
+ public void setType(Type type) {
+ this.type = type;
+ }
+
+ public int getCodeId() {
+ return this.code;
+ }
+
+ public void setCodeId(int code) {
+ this.code = code;
+ }
+
+ public Enum> getCode() {
+ if (this.type == null) {
+ return null;
+ }
+
+ Enum>[] values = this.type.getEnumClass().getEnumConstants();
+ if (this.code >= values.length) {
+ return null;
+ }
+
+ return values[this.code];
+ }
+
+ public void setCode(Enum> code) {
+ this.code = code.ordinal();
+ }
+
+ public void setTypeAndCode(Enum> code) {
+ this.type = Type.getByEnum(code);
+ this.code = code.ordinal();
+ }
+
+ public short getChecksum() {
+ return this.checksum;
+ }
+
+ public void setChecksum(short checksum) {
+ this.checksum = checksum;
+ }
+
+ public int getData() {
+ return this.data;
+ }
+
+ public void setData(int data) {
+ this.data = data;
+ }
+
+ @Override
+ public String toString() {
+ return "ICMP{"
+ + "type=" + this.type
+ + ", code=" + this.code
+ + ", checksum=" + this.checksum
+ + ", data=" + this.data
+ + '}';
+ }
+}
diff --git a/src/main/java/net/elytrium/pcap/layer/data/EthernetProtocol.java b/src/main/java/net/elytrium/pcap/layer/data/EthernetProtocol.java
index 49240fe..61521ca 100644
--- a/src/main/java/net/elytrium/pcap/layer/data/EthernetProtocol.java
+++ b/src/main/java/net/elytrium/pcap/layer/data/EthernetProtocol.java
@@ -20,6 +20,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
+import net.elytrium.pcap.layer.ARP;
import net.elytrium.pcap.layer.IPv4;
import net.elytrium.pcap.layer.IPv6;
import net.elytrium.pcap.layer.Layer;
@@ -34,7 +35,7 @@ public enum EthernetProtocol {
ERSPAN2(0x22EB),
IP(0x0800, IPv4::new),
X25(0x0805),
- ARP(0x0806),
+ ARP(0x0806, ARP::new),
BPQ(0x08FF),
IEEEPUP(0x0A00),
IEEEPUPAT(0x0A01),
diff --git a/src/main/java/net/elytrium/pcap/layer/data/IpProtocol.java b/src/main/java/net/elytrium/pcap/layer/data/IpProtocol.java
index 942f8ee..5eb4bc9 100644
--- a/src/main/java/net/elytrium/pcap/layer/data/IpProtocol.java
+++ b/src/main/java/net/elytrium/pcap/layer/data/IpProtocol.java
@@ -18,6 +18,7 @@
package net.elytrium.pcap.layer.data;
import java.util.function.Supplier;
+import net.elytrium.pcap.layer.ICMP;
import net.elytrium.pcap.layer.IPv4;
import net.elytrium.pcap.layer.IPv6;
import net.elytrium.pcap.layer.IPv6Destination;
@@ -30,7 +31,7 @@
public enum IpProtocol {
HOPOPT(IPv6HopByHop::new),
- ICMP,
+ ICMP(ICMP::new),
IGMP,
GGP,
IP_IN_IP(IPv4::new),