From 59675c4a8517401db6b1efaa7f7b2b7a55bd8d1f Mon Sep 17 00:00:00 2001 From: jnngl Date: Thu, 18 May 2023 03:29:45 +1100 Subject: [PATCH 1/3] Fix sample code --- README.md | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) 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(); } }); From 4db763570ba2547fb5cdb88cb47baaf268ba5d9d Mon Sep 17 00:00:00 2001 From: jnngl Date: Thu, 18 May 2023 04:26:45 +1100 Subject: [PATCH 2/3] Add ICMP --- .../java/net/elytrium/pcap/layer/ICMP.java | 288 ++++++++++++++++++ .../elytrium/pcap/layer/data/IpProtocol.java | 3 +- 2 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 src/main/java/net/elytrium/pcap/layer/ICMP.java 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> enumClass; + + Type(int value, Class> enumClass) { + this.value = value; + this.enumClass = enumClass; + } + + public int getValue() { + return this.value; + } + + public Class> getEnumClass() { + return this.enumClass; + } + + public static Type getByValue(int value) { + return VALUE_REGISTRY.get(value); + } + + public static Type getByEnum(@SuppressWarnings("rawtypes") Class 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/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), From 7a9f2f8af8fc9a1539f3e01473472451c5194b80 Mon Sep 17 00:00:00 2001 From: jnngl Date: Thu, 18 May 2023 23:35:36 +1100 Subject: [PATCH 3/3] Add ARP --- .../java/net/elytrium/pcap/layer/ARP.java | 288 ++++++++++++++++++ .../pcap/layer/data/EthernetProtocol.java | 3 +- 2 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 src/main/java/net/elytrium/pcap/layer/ARP.java 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/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),