blob: 8748c0c2ee2f8fb7295fb96ae80100ced60d5992 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014, 2019 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Vincent Perot - Initial API and implementation
*******************************************************************************/
package org.eclipse.tracecompass.internal.pcap.core.protocol.ethernet2;
import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.internal.pcap.core.packet.BadPacketException;
import org.eclipse.tracecompass.internal.pcap.core.packet.Packet;
import org.eclipse.tracecompass.internal.pcap.core.protocol.PcapProtocol;
import org.eclipse.tracecompass.internal.pcap.core.protocol.ipv4.IPv4Packet;
import org.eclipse.tracecompass.internal.pcap.core.protocol.unknown.UnknownPacket;
import org.eclipse.tracecompass.internal.pcap.core.trace.PcapFile;
import org.eclipse.tracecompass.internal.pcap.core.util.ConversionHelper;
import org.eclipse.tracecompass.internal.pcap.core.util.EthertypeHelper;
import com.google.common.collect.ImmutableMap;
/**
* Class that represents an Ethernet II packet. This should be called an
* Ethernet frame, but in order to keep the nomenclature consistent, this is
* called a packet.
*
* @author Vincent Perot
*/
public class EthernetIIPacket extends Packet {
private final @Nullable Packet fChildPacket;
private final @Nullable ByteBuffer fPayload;
/* We store MAC addresses as byte arrays since
* there is no standard java class to store them. */
private final byte[] fSourceMacAddress;
private final byte[] fDestinationMacAddress;
private final int fType;
private @Nullable EthernetIIEndpoint fSourceEndpoint;
private @Nullable EthernetIIEndpoint fDestinationEndpoint;
private @Nullable Map<String, String> fFields;
/**
* Constructor of the Ethernet Packet class.
*
* @param file
* The file that contains this packet.
* @param parent
* The parent packet of this packet (the encapsulating packet).
* @param packet
* The entire packet (header and payload).
* @throws BadPacketException
* Thrown when the packet is erroneous.
*/
public EthernetIIPacket(PcapFile file, @Nullable Packet parent, ByteBuffer packet) throws BadPacketException {
super(file, parent, PcapProtocol.ETHERNET_II);
if (packet.limit() <= EthernetIIValues.ETHERNET_II_MIN_SIZE) {
throw new BadPacketException("An Ethernet II packet can't be smaller than 14 bytes."); //$NON-NLS-1$
}
// The endpoints are lazy loaded. They are defined in the get*Endpoint()
// methods.
fSourceEndpoint = null;
fDestinationEndpoint = null;
fFields = null;
fDestinationMacAddress = new byte[EthernetIIValues.MAC_ADDRESS_SIZE];
fSourceMacAddress = new byte[EthernetIIValues.MAC_ADDRESS_SIZE];
packet.order(ByteOrder.BIG_ENDIAN);
packet.position(0);
packet.get(fDestinationMacAddress);
packet.get(fSourceMacAddress);
fType = ConversionHelper.unsignedShortToInt(packet.getShort());
// Get payload if it exists.
if (packet.remaining() > 0) {
ByteBuffer payload = packet.slice();
payload.order(ByteOrder.BIG_ENDIAN);
fPayload = payload;
} else {
fPayload = null;
}
// Find child
fChildPacket = findChildPacket();
}
@Override
public @Nullable Packet getChildPacket() {
return fChildPacket;
}
@Override
public @Nullable ByteBuffer getPayload() {
return fPayload;
}
/**
* Getter method for the source MAC Address.
*
* @return The source MAC address.
*/
public byte[] getSourceMacAddress() {
return checkNotNull(Arrays.copyOf(fSourceMacAddress, fSourceMacAddress.length));
}
/**
* Getter method for the destination MAC Address.
*
* @return The destination MAC address.
*/
public byte[] getDestinationMacAddress() {
return checkNotNull(Arrays.copyOf(fDestinationMacAddress, fDestinationMacAddress.length));
}
/**
* Getter method for Ethertype. See
* http://standards.ieee.org/develop/regauth/ethertype/eth.txt
*
* @return The Ethertype. This is used to determine the child packet..
*/
public int getEthertype() {
return fType;
}
@Override
protected @Nullable Packet findChildPacket() throws BadPacketException {
// TODO Add more protocols.
ByteBuffer payload = fPayload;
if (payload == null) {
return null;
}
switch (fType) {
case EthertypeHelper.ETHERTYPE_IPV4:
return new IPv4Packet(getPcapFile(), this, payload);
default:
return new UnknownPacket(getPcapFile(), this, payload);
}
}
@Override
public String toString() {
String string = getProtocol().getName() + ", Source: " + ConversionHelper.toMacAddress(fSourceMacAddress) + //$NON-NLS-1$
", Destination: " + ConversionHelper.toMacAddress(fDestinationMacAddress) + ", Type: " + //$NON-NLS-1$ //$NON-NLS-2$
EthertypeHelper.toEtherType(fType) + "\n"; //$NON-NLS-1$
final Packet child = fChildPacket;
if (child != null) {
return string + child.toString();
}
return string;
}
@Override
public boolean validate() {
// Not yet implemented. ATM, we consider that all packets are valid.
// This is the case for all packets.
// TODO Implement it.
return true;
}
@Override
public EthernetIIEndpoint getSourceEndpoint() {
@Nullable EthernetIIEndpoint endpoint = fSourceEndpoint;
if (endpoint == null) {
endpoint = new EthernetIIEndpoint(this, true);
}
fSourceEndpoint = endpoint;
return fSourceEndpoint;
}
@Override
public EthernetIIEndpoint getDestinationEndpoint() {
@Nullable EthernetIIEndpoint endpoint = fDestinationEndpoint;
if (endpoint == null) {
endpoint = new EthernetIIEndpoint(this, false);
}
fDestinationEndpoint = endpoint;
return fDestinationEndpoint;
}
@Override
public Map<String, String> getFields() {
Map<String, String> map = fFields;
if (map == null) {
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
builder.put("Source MAC Address", ConversionHelper.toMacAddress(fSourceMacAddress)); //$NON-NLS-1$
builder.put("Destination MAC Address", ConversionHelper.toMacAddress(fDestinationMacAddress)); //$NON-NLS-1$
builder.put("Ethertype", String.valueOf(EthertypeHelper.toEtherType(fType))); //$NON-NLS-1$
fFields = builder.build();
return fFields;
}
return map;
}
@Override
public String getLocalSummaryString() {
return "Src: " + ConversionHelper.toMacAddress(fSourceMacAddress) + " , Dst: " + ConversionHelper.toMacAddress(fDestinationMacAddress); //$NON-NLS-1$ //$NON-NLS-2$
}
@Override
protected String getSignificationString() {
return "Source MAC: " + ConversionHelper.toMacAddress(fSourceMacAddress) + " , Destination MAC: " + ConversionHelper.toMacAddress(fDestinationMacAddress); //$NON-NLS-1$ //$NON-NLS-2$
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
final Packet child = fChildPacket;
if (child != null) {
result = prime * result + child.hashCode();
} else {
result = prime * result;
}
result = prime * result + Arrays.hashCode(fDestinationMacAddress);
if (child == null) {
result = prime * result + payloadHashCode(fPayload);
}
result = prime * result + Arrays.hashCode(fSourceMacAddress);
result = prime * result + fType;
return result;
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
EthernetIIPacket other = (EthernetIIPacket) obj;
if(!Objects.equals(fChildPacket, other.fChildPacket)) {
return false;
}
if (!Arrays.equals(fDestinationMacAddress, other.fDestinationMacAddress)) {
return false;
}
if (fChildPacket == null && !payloadEquals(fPayload, other.fPayload)) {
return false;
}
if (!Arrays.equals(fSourceMacAddress, other.fSourceMacAddress)) {
return false;
}
return (fType == other.fType);
}
}