|
|
@@ -0,0 +1,562 @@
|
|
|
+/*
|
|
|
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
|
+ * or more contributor license agreements. Licensed under the Elastic License;
|
|
|
+ * you may not use this file except in compliance with the Elastic License.
|
|
|
+ */
|
|
|
+
|
|
|
+package org.elasticsearch.xpack.ingest;
|
|
|
+
|
|
|
+import org.elasticsearch.common.network.InetAddresses;
|
|
|
+import org.elasticsearch.ingest.AbstractProcessor;
|
|
|
+import org.elasticsearch.ingest.ConfigurationUtils;
|
|
|
+import org.elasticsearch.ingest.IngestDocument;
|
|
|
+import org.elasticsearch.ingest.Processor;
|
|
|
+
|
|
|
+import java.math.BigInteger;
|
|
|
+import java.net.InetAddress;
|
|
|
+import java.nio.ByteBuffer;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.security.MessageDigest;
|
|
|
+import java.security.NoSuchAlgorithmException;
|
|
|
+import java.util.Base64;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Locale;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+import static org.elasticsearch.ingest.ConfigurationUtils.newConfigurationException;
|
|
|
+import static org.elasticsearch.ingest.ConfigurationUtils.readBooleanProperty;
|
|
|
+
|
|
|
+public final class CommunityIdProcessor extends AbstractProcessor {
|
|
|
+
|
|
|
+ public static final String TYPE = "community_id";
|
|
|
+
|
|
|
+ private final String sourceIpField;
|
|
|
+ private final String sourcePortField;
|
|
|
+ private final String destinationIpField;
|
|
|
+ private final String destinationPortField;
|
|
|
+ private final String ianaNumberField;
|
|
|
+ private final String transportField;
|
|
|
+ private final String icmpTypeField;
|
|
|
+ private final String icmpCodeField;
|
|
|
+ private final String targetField;
|
|
|
+ private final MessageDigest messageDigest;
|
|
|
+ private final byte[] seed;
|
|
|
+ private final boolean ignoreMissing;
|
|
|
+
|
|
|
+ CommunityIdProcessor(
|
|
|
+ String tag,
|
|
|
+ String description,
|
|
|
+ String sourceIpField,
|
|
|
+ String sourcePortField,
|
|
|
+ String destinationIpField,
|
|
|
+ String destinationPortField,
|
|
|
+ String ianaNumberField,
|
|
|
+ String transportField,
|
|
|
+ String icmpTypeField,
|
|
|
+ String icmpCodeField,
|
|
|
+ String targetField,
|
|
|
+ MessageDigest messageDigest,
|
|
|
+ byte[] seed,
|
|
|
+ boolean ignoreMissing
|
|
|
+ ) {
|
|
|
+ super(tag, description);
|
|
|
+ this.sourceIpField = sourceIpField;
|
|
|
+ this.sourcePortField = sourcePortField;
|
|
|
+ this.destinationIpField = destinationIpField;
|
|
|
+ this.destinationPortField = destinationPortField;
|
|
|
+ this.ianaNumberField = ianaNumberField;
|
|
|
+ this.transportField = transportField;
|
|
|
+ this.icmpTypeField = icmpTypeField;
|
|
|
+ this.icmpCodeField = icmpCodeField;
|
|
|
+ this.targetField = targetField;
|
|
|
+ this.messageDigest = messageDigest;
|
|
|
+ this.seed = seed;
|
|
|
+ this.ignoreMissing = ignoreMissing;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getSourceIpField() {
|
|
|
+ return sourceIpField;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getSourcePortField() {
|
|
|
+ return sourcePortField;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getDestinationIpField() {
|
|
|
+ return destinationIpField;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getDestinationPortField() {
|
|
|
+ return destinationPortField;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getIanaNumberField() {
|
|
|
+ return ianaNumberField;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getTransportField() {
|
|
|
+ return transportField;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getIcmpTypeField() {
|
|
|
+ return icmpTypeField;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getIcmpCodeField() {
|
|
|
+ return icmpCodeField;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getTargetField() {
|
|
|
+ return targetField;
|
|
|
+ }
|
|
|
+
|
|
|
+ public MessageDigest getMessageDigest() {
|
|
|
+ return messageDigest;
|
|
|
+ }
|
|
|
+
|
|
|
+ public byte[] getSeed() {
|
|
|
+ return seed;
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean getIgnoreMissing() {
|
|
|
+ return ignoreMissing;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public IngestDocument execute(IngestDocument ingestDocument) throws Exception {
|
|
|
+ Flow flow = buildFlow(ingestDocument);
|
|
|
+ if (flow == null) {
|
|
|
+ if (ignoreMissing) {
|
|
|
+ return ingestDocument;
|
|
|
+ } else {
|
|
|
+ throw new IllegalArgumentException("unable to construct flow from document");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ingestDocument.setFieldValue(targetField, flow.toCommunityId(messageDigest, seed));
|
|
|
+ return ingestDocument;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Flow buildFlow(IngestDocument d) {
|
|
|
+ String sourceIpAddrString = d.getFieldValue(sourceIpField, String.class, ignoreMissing);
|
|
|
+ if (sourceIpAddrString == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ String destIpAddrString = d.getFieldValue(destinationIpField, String.class, ignoreMissing);
|
|
|
+ if (destIpAddrString == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ Flow flow = new Flow();
|
|
|
+ flow.source = InetAddresses.forString(sourceIpAddrString);
|
|
|
+ flow.destination = InetAddresses.forString(destIpAddrString);
|
|
|
+
|
|
|
+ Object protocol = d.getFieldValue(ianaNumberField, Object.class, true);
|
|
|
+ if (protocol == null) {
|
|
|
+ protocol = d.getFieldValue(transportField, Object.class, ignoreMissing);
|
|
|
+ if (protocol == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ flow.protocol = Transport.fromObject(protocol);
|
|
|
+
|
|
|
+ switch (flow.protocol) {
|
|
|
+ case Tcp:
|
|
|
+ case Udp:
|
|
|
+ case Sctp:
|
|
|
+ flow.sourcePort = parseIntFromObjectOrString(d.getFieldValue(sourcePortField, Object.class, ignoreMissing), "source port");
|
|
|
+ if (flow.sourcePort == 0) {
|
|
|
+ throw new IllegalArgumentException("invalid source port [0]");
|
|
|
+ }
|
|
|
+
|
|
|
+ flow.destinationPort = parseIntFromObjectOrString(
|
|
|
+ d.getFieldValue(destinationPortField, Object.class, ignoreMissing),
|
|
|
+ "destination port"
|
|
|
+ );
|
|
|
+ if (flow.destinationPort == 0) {
|
|
|
+ throw new IllegalArgumentException("invalid destination port [0]");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case Icmp:
|
|
|
+ case IcmpIpV6:
|
|
|
+ // tolerate missing or invalid ICMP types and codes
|
|
|
+ flow.icmpType = parseIntFromObjectOrString(d.getFieldValue(icmpTypeField, Object.class, true), "icmp type");
|
|
|
+ flow.icmpCode = parseIntFromObjectOrString(d.getFieldValue(icmpCodeField, Object.class, true), "icmp code");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return flow;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String getType() {
|
|
|
+ return TYPE;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Converts an integer in the range of an unsigned 16-bit integer to a big-endian byte pair
|
|
|
+ */
|
|
|
+ static byte[] toUint16(int num) {
|
|
|
+ if (num < 0 || num > 65535) {
|
|
|
+ throw new IllegalStateException("number [" + num + "] must be a value between 0 and 65535");
|
|
|
+ }
|
|
|
+ return new byte[] { (byte) (num >> 8), (byte) num };
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Attempts to coerce an object to an integer
|
|
|
+ */
|
|
|
+ static int parseIntFromObjectOrString(Object o, String fieldName) {
|
|
|
+ if (o == null) {
|
|
|
+ return 0;
|
|
|
+ } else if (o instanceof Number) {
|
|
|
+ return (int) o;
|
|
|
+ } else if (o instanceof String) {
|
|
|
+ try {
|
|
|
+ return Integer.parseInt((String) o);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ // fall through to IllegalArgumentException below
|
|
|
+ }
|
|
|
+ }
|
|
|
+ throw new IllegalArgumentException("unable to parse " + fieldName + " [" + o + "]");
|
|
|
+ }
|
|
|
+
|
|
|
+ public static final class Factory implements Processor.Factory {
|
|
|
+
|
|
|
+ static final String DEFAULT_SOURCE_IP = "source.ip";
|
|
|
+ static final String DEFAULT_SOURCE_PORT = "source.port";
|
|
|
+ static final String DEFAULT_DEST_IP = "destination.ip";
|
|
|
+ static final String DEFAULT_DEST_PORT = "destination.port";
|
|
|
+ static final String DEFAULT_IANA_NUMBER = "network.iana_number";
|
|
|
+ static final String DEFAULT_TRANSPORT = "network.transport";
|
|
|
+ static final String DEFAULT_ICMP_TYPE = "icmp.type";
|
|
|
+ static final String DEFAULT_ICMP_CODE = "icmp.code";
|
|
|
+ static final String DEFAULT_TARGET = "network.community_id";
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public CommunityIdProcessor create(
|
|
|
+ Map<String, Processor.Factory> registry,
|
|
|
+ String processorTag,
|
|
|
+ String description,
|
|
|
+ Map<String, Object> config
|
|
|
+ ) throws Exception {
|
|
|
+ String sourceIpField = ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "source_ip", DEFAULT_SOURCE_IP);
|
|
|
+ String sourcePortField = ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "source_port", DEFAULT_SOURCE_PORT);
|
|
|
+ String destIpField = ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "destination_ip", DEFAULT_DEST_IP);
|
|
|
+ String destPortField = ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "destination_port", DEFAULT_DEST_PORT);
|
|
|
+ String ianaNumberField = ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "iana_number", DEFAULT_IANA_NUMBER);
|
|
|
+ String transportField = ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "transport", DEFAULT_TRANSPORT);
|
|
|
+ String icmpTypeField = ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "icmp_type", DEFAULT_ICMP_TYPE);
|
|
|
+ String icmpCodeField = ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "icmp_code", DEFAULT_ICMP_CODE);
|
|
|
+ String targetField = ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "target_field", DEFAULT_TARGET);
|
|
|
+ int seedInt = ConfigurationUtils.readIntProperty(TYPE, processorTag, config, "seed", 0);
|
|
|
+ if (seedInt < 0 || seedInt > 65535) {
|
|
|
+ throw newConfigurationException(TYPE, processorTag, "seed", "must be a value between 0 and 65535");
|
|
|
+ }
|
|
|
+ MessageDigest messageDigest;
|
|
|
+ try {
|
|
|
+ messageDigest = MessageDigest.getInstance("SHA-1");
|
|
|
+ } catch (NoSuchAlgorithmException e) {
|
|
|
+ throw new IllegalStateException("unable to obtain SHA-1 hasher", e);
|
|
|
+ }
|
|
|
+
|
|
|
+ boolean ignoreMissing = readBooleanProperty(TYPE, processorTag, config, "ignore_missing", true);
|
|
|
+ return new CommunityIdProcessor(
|
|
|
+ processorTag,
|
|
|
+ description,
|
|
|
+ sourceIpField,
|
|
|
+ sourcePortField,
|
|
|
+ destIpField,
|
|
|
+ destPortField,
|
|
|
+ ianaNumberField,
|
|
|
+ transportField,
|
|
|
+ icmpTypeField,
|
|
|
+ icmpCodeField,
|
|
|
+ targetField,
|
|
|
+ messageDigest,
|
|
|
+ toUint16(seedInt),
|
|
|
+ ignoreMissing
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Represents flow data per https://github.com/corelight/community-id-spec
|
|
|
+ */
|
|
|
+ public static final class Flow {
|
|
|
+
|
|
|
+ private static final List<Transport> TRANSPORTS_WITH_PORTS = List.of(
|
|
|
+ Transport.Tcp,
|
|
|
+ Transport.Udp,
|
|
|
+ Transport.Sctp,
|
|
|
+ Transport.Icmp,
|
|
|
+ Transport.IcmpIpV6
|
|
|
+ );
|
|
|
+
|
|
|
+ InetAddress source;
|
|
|
+ InetAddress destination;
|
|
|
+ Transport protocol;
|
|
|
+ int sourcePort;
|
|
|
+ int destinationPort;
|
|
|
+ int icmpType;
|
|
|
+ int icmpCode;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return true iff the source address/port is numerically less than the destination address/port as described
|
|
|
+ * at https://github.com/corelight/community-id-spec
|
|
|
+ */
|
|
|
+ boolean isOrdered() {
|
|
|
+ int result = new BigInteger(1, source.getAddress()).compareTo(new BigInteger(1, destination.getAddress()));
|
|
|
+ return result < 0 || (result == 0 && sourcePort < destinationPort);
|
|
|
+ }
|
|
|
+
|
|
|
+ byte[] toBytes() {
|
|
|
+ boolean hasPort = TRANSPORTS_WITH_PORTS.contains(protocol);
|
|
|
+ int len = source.getAddress().length + destination.getAddress().length + 2 + (hasPort ? 4 : 0);
|
|
|
+ ByteBuffer bb = ByteBuffer.allocate(len);
|
|
|
+
|
|
|
+ boolean isOneWay = false;
|
|
|
+ if (protocol == Transport.Icmp || protocol == Transport.IcmpIpV6) {
|
|
|
+ // ICMP protocols populate port fields with ICMP data
|
|
|
+ Integer equivalent = IcmpType.codeEquivalent(icmpType, protocol == Transport.IcmpIpV6);
|
|
|
+ isOneWay = equivalent == null;
|
|
|
+ sourcePort = icmpType;
|
|
|
+ destinationPort = equivalent == null ? icmpCode : equivalent;
|
|
|
+ }
|
|
|
+
|
|
|
+ boolean keepOrder = isOrdered() || ((protocol == Transport.Icmp || protocol == Transport.IcmpIpV6) && isOneWay);
|
|
|
+ bb.put(keepOrder ? source.getAddress() : destination.getAddress());
|
|
|
+ bb.put(keepOrder ? destination.getAddress() : source.getAddress());
|
|
|
+ bb.put(toUint16(protocol.getTransportNumber() << 8));
|
|
|
+
|
|
|
+ if (hasPort) {
|
|
|
+ bb.put(keepOrder ? toUint16(sourcePort) : toUint16(destinationPort));
|
|
|
+ bb.put(keepOrder ? toUint16(destinationPort) : toUint16(sourcePort));
|
|
|
+ }
|
|
|
+
|
|
|
+ return bb.array();
|
|
|
+ }
|
|
|
+
|
|
|
+ String toCommunityId(MessageDigest md, byte[] seed) {
|
|
|
+ md.update(seed);
|
|
|
+ byte[] encodedBytes = Base64.getEncoder().encode(md.digest(toBytes()));
|
|
|
+ return "1:" + new String(encodedBytes, StandardCharsets.UTF_8);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public enum Transport {
|
|
|
+ Icmp(1),
|
|
|
+ Igmp(2),
|
|
|
+ Tcp(6),
|
|
|
+ Udp(17),
|
|
|
+ Gre(47),
|
|
|
+ IcmpIpV6(58),
|
|
|
+ Eigrp(88),
|
|
|
+ Ospf(89),
|
|
|
+ Pim(103),
|
|
|
+ Sctp(132);
|
|
|
+
|
|
|
+ private final int transportNumber;
|
|
|
+
|
|
|
+ private static final Map<String, Transport> TRANSPORT_NAMES;
|
|
|
+
|
|
|
+ static {
|
|
|
+ TRANSPORT_NAMES = new HashMap<>();
|
|
|
+ TRANSPORT_NAMES.put("icmp", Icmp);
|
|
|
+ TRANSPORT_NAMES.put("igmp", Igmp);
|
|
|
+ TRANSPORT_NAMES.put("tcp", Tcp);
|
|
|
+ TRANSPORT_NAMES.put("udp", Udp);
|
|
|
+ TRANSPORT_NAMES.put("gre", Gre);
|
|
|
+ TRANSPORT_NAMES.put("ipv6-icmp", IcmpIpV6);
|
|
|
+ TRANSPORT_NAMES.put("icmpv6", IcmpIpV6);
|
|
|
+ TRANSPORT_NAMES.put("eigrp", Eigrp);
|
|
|
+ TRANSPORT_NAMES.put("ospf", Ospf);
|
|
|
+ TRANSPORT_NAMES.put("pim", Pim);
|
|
|
+ TRANSPORT_NAMES.put("sctp", Sctp);
|
|
|
+ }
|
|
|
+
|
|
|
+ Transport(int transportNumber) {
|
|
|
+ this.transportNumber = transportNumber;
|
|
|
+ }
|
|
|
+
|
|
|
+ public int getTransportNumber() {
|
|
|
+ return transportNumber;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static Transport fromNumber(int transportNumber) {
|
|
|
+ switch (transportNumber) {
|
|
|
+ case 1:
|
|
|
+ return Icmp;
|
|
|
+ case 2:
|
|
|
+ return Igmp;
|
|
|
+ case 6:
|
|
|
+ return Tcp;
|
|
|
+ case 17:
|
|
|
+ return Udp;
|
|
|
+ case 47:
|
|
|
+ return Gre;
|
|
|
+ case 58:
|
|
|
+ return IcmpIpV6;
|
|
|
+ case 88:
|
|
|
+ return Eigrp;
|
|
|
+ case 89:
|
|
|
+ return Ospf;
|
|
|
+ case 103:
|
|
|
+ return Pim;
|
|
|
+ case 132:
|
|
|
+ return Sctp;
|
|
|
+ default:
|
|
|
+ throw new IllegalArgumentException("unknown transport protocol number [" + transportNumber + "]");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static Transport fromObject(Object o) {
|
|
|
+ if (o instanceof Number) {
|
|
|
+ return fromNumber(((Number) o).intValue());
|
|
|
+ } else if (o instanceof String) {
|
|
|
+ String protocolStr = (String) o;
|
|
|
+
|
|
|
+ // check if matches protocol name
|
|
|
+ if (TRANSPORT_NAMES.containsKey(protocolStr.toLowerCase(Locale.ROOT))) {
|
|
|
+ return TRANSPORT_NAMES.get(protocolStr.toLowerCase(Locale.ROOT));
|
|
|
+ }
|
|
|
+
|
|
|
+ // check if convertible to protocol number
|
|
|
+ try {
|
|
|
+ int protocolNumber = Integer.parseInt(protocolStr);
|
|
|
+ return fromNumber(protocolNumber);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ // fall through to IllegalArgumentException
|
|
|
+ }
|
|
|
+
|
|
|
+ throw new IllegalArgumentException("could not convert string [" + protocolStr + "] to transport protocol");
|
|
|
+ } else {
|
|
|
+ throw new IllegalArgumentException(
|
|
|
+ "could not convert value of type [" + o.getClass().getName() + "] to transport protocol"
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public enum IcmpType {
|
|
|
+ EchoReply(0),
|
|
|
+ EchoRequest(8),
|
|
|
+ RouterAdvertisement(9),
|
|
|
+ RouterSolicitation(10),
|
|
|
+ TimestampRequest(13),
|
|
|
+ TimestampReply(14),
|
|
|
+ InfoRequest(15),
|
|
|
+ InfoReply(16),
|
|
|
+ AddressMaskRequest(17),
|
|
|
+ AddressMaskReply(18),
|
|
|
+ V6EchoRequest(128),
|
|
|
+ V6EchoReply(129),
|
|
|
+ V6RouterSolicitation(133),
|
|
|
+ V6RouterAdvertisement(134),
|
|
|
+ V6NeighborSolicitation(135),
|
|
|
+ V6NeighborAdvertisement(136),
|
|
|
+ V6MLDv1MulticastListenerQueryMessage(130),
|
|
|
+ V6MLDv1MulticastListenerReportMessage(131),
|
|
|
+ V6WhoAreYouRequest(139),
|
|
|
+ V6WhoAreYouReply(140),
|
|
|
+ V6HomeAddressDiscoveryRequest(144),
|
|
|
+ V6HomeAddressDiscoveryResponse(145);
|
|
|
+
|
|
|
+ private static final Map<Integer, Integer> ICMP_V4_CODE_EQUIVALENTS;
|
|
|
+ private static final Map<Integer, Integer> ICMP_V6_CODE_EQUIVALENTS;
|
|
|
+
|
|
|
+ static {
|
|
|
+ ICMP_V4_CODE_EQUIVALENTS = new HashMap<>();
|
|
|
+ ICMP_V4_CODE_EQUIVALENTS.put(EchoRequest.getType(), EchoReply.getType());
|
|
|
+ ICMP_V4_CODE_EQUIVALENTS.put(EchoReply.getType(), EchoRequest.getType());
|
|
|
+ ICMP_V4_CODE_EQUIVALENTS.put(TimestampRequest.getType(), TimestampReply.getType());
|
|
|
+ ICMP_V4_CODE_EQUIVALENTS.put(TimestampReply.getType(), TimestampRequest.getType());
|
|
|
+ ICMP_V4_CODE_EQUIVALENTS.put(InfoRequest.getType(), InfoReply.getType());
|
|
|
+ ICMP_V4_CODE_EQUIVALENTS.put(RouterSolicitation.getType(), RouterAdvertisement.getType());
|
|
|
+ ICMP_V4_CODE_EQUIVALENTS.put(RouterAdvertisement.getType(), RouterSolicitation.getType());
|
|
|
+ ICMP_V4_CODE_EQUIVALENTS.put(AddressMaskRequest.getType(), AddressMaskReply.getType());
|
|
|
+ ICMP_V4_CODE_EQUIVALENTS.put(AddressMaskReply.getType(), AddressMaskRequest.getType());
|
|
|
+
|
|
|
+ ICMP_V6_CODE_EQUIVALENTS = new HashMap<>();
|
|
|
+ ICMP_V6_CODE_EQUIVALENTS.put(V6EchoRequest.getType(), V6EchoReply.getType());
|
|
|
+ ICMP_V6_CODE_EQUIVALENTS.put(V6EchoReply.getType(), V6EchoRequest.getType());
|
|
|
+ ICMP_V6_CODE_EQUIVALENTS.put(V6RouterSolicitation.getType(), V6RouterAdvertisement.getType());
|
|
|
+ ICMP_V6_CODE_EQUIVALENTS.put(V6RouterAdvertisement.getType(), V6RouterSolicitation.getType());
|
|
|
+ ICMP_V6_CODE_EQUIVALENTS.put(V6NeighborAdvertisement.getType(), V6NeighborSolicitation.getType());
|
|
|
+ ICMP_V6_CODE_EQUIVALENTS.put(V6NeighborSolicitation.getType(), V6NeighborAdvertisement.getType());
|
|
|
+ ICMP_V6_CODE_EQUIVALENTS.put(V6MLDv1MulticastListenerQueryMessage.getType(), V6MLDv1MulticastListenerReportMessage.getType());
|
|
|
+ ICMP_V6_CODE_EQUIVALENTS.put(V6WhoAreYouRequest.getType(), V6WhoAreYouReply.getType());
|
|
|
+ ICMP_V6_CODE_EQUIVALENTS.put(V6WhoAreYouReply.getType(), V6WhoAreYouRequest.getType());
|
|
|
+ ICMP_V6_CODE_EQUIVALENTS.put(V6HomeAddressDiscoveryRequest.getType(), V6HomeAddressDiscoveryResponse.getType());
|
|
|
+ ICMP_V6_CODE_EQUIVALENTS.put(V6HomeAddressDiscoveryResponse.getType(), V6HomeAddressDiscoveryRequest.getType());
|
|
|
+ }
|
|
|
+
|
|
|
+ private final int type;
|
|
|
+
|
|
|
+ IcmpType(int type) {
|
|
|
+ this.type = type;
|
|
|
+ }
|
|
|
+
|
|
|
+ public int getType() {
|
|
|
+ return type;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static IcmpType fromNumber(int type) {
|
|
|
+ switch (type) {
|
|
|
+ case 0:
|
|
|
+ return EchoReply;
|
|
|
+ case 8:
|
|
|
+ return EchoRequest;
|
|
|
+ case 9:
|
|
|
+ return RouterAdvertisement;
|
|
|
+ case 10:
|
|
|
+ return RouterSolicitation;
|
|
|
+ case 13:
|
|
|
+ return TimestampRequest;
|
|
|
+ case 14:
|
|
|
+ return TimestampReply;
|
|
|
+ case 15:
|
|
|
+ return InfoRequest;
|
|
|
+ case 16:
|
|
|
+ return InfoReply;
|
|
|
+ case 17:
|
|
|
+ return AddressMaskRequest;
|
|
|
+ case 18:
|
|
|
+ return AddressMaskReply;
|
|
|
+ case 128:
|
|
|
+ return V6EchoRequest;
|
|
|
+ case 129:
|
|
|
+ return V6EchoReply;
|
|
|
+ case 133:
|
|
|
+ return V6RouterSolicitation;
|
|
|
+ case 134:
|
|
|
+ return V6RouterAdvertisement;
|
|
|
+ case 135:
|
|
|
+ return V6NeighborSolicitation;
|
|
|
+ case 136:
|
|
|
+ return V6NeighborAdvertisement;
|
|
|
+ case 130:
|
|
|
+ return V6MLDv1MulticastListenerQueryMessage;
|
|
|
+ case 131:
|
|
|
+ return V6MLDv1MulticastListenerReportMessage;
|
|
|
+ case 139:
|
|
|
+ return V6WhoAreYouRequest;
|
|
|
+ case 140:
|
|
|
+ return V6WhoAreYouReply;
|
|
|
+ case 144:
|
|
|
+ return V6HomeAddressDiscoveryRequest;
|
|
|
+ case 145:
|
|
|
+ return V6HomeAddressDiscoveryResponse;
|
|
|
+ default:
|
|
|
+ // don't fail if the type is unknown
|
|
|
+ return EchoReply;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static Integer codeEquivalent(int icmpType, boolean isIpV6) {
|
|
|
+ return isIpV6 ? ICMP_V6_CODE_EQUIVALENTS.get(icmpType) : ICMP_V4_CODE_EQUIVALENTS.get(icmpType);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|