/*******************************************************************************
* gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/
* Copyright (C) 2014 SVS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
package staticContent.evaluation.traceParser.engine.protocolHeaderParser;
import java.util.Arrays;
import java.util.zip.CRC32;
import staticContent.framework.util.Util;
//format:
//0-5: dest mac
//6-11: src mac
//12-13: type field (or vlan-tag)
//14-17: payload or vlan field
//18-: payload
//pad + crc: ignored
public class EthernetFrame {
public static final int ETHER_TYPE_IP_V4 = 0x0800;
public static final int ETHER_TYPE_IP_V6 = 0x86DD;
public static final int ETHER_TYPE_ARP = 0x0806;
public static final int ETHER_TYPE_VLAN = 0x8100;
public static final int ETHER_TYPE_WOL = 0x0842;
public static byte[] getEtherTypeRaw(byte[] frame) {
byte[] type = Arrays.copyOfRange(frame, 12, 14);
int etherType = Util.unsignedShortToInt(type);
if (etherType != ETHER_TYPE_VLAN)
return type;
else
return Arrays.copyOfRange(frame, 16, 18);
}
public static int getEtherType(byte[] frame) {
return Util.unsignedShortToInt(getEtherTypeRaw(frame));
}
public static String getEtherTypeAsString(byte[] frame) {
return etherTypeToString(getEtherType(frame));
}
public static boolean isVLAN(byte[] frame) {
byte[] type = Arrays.copyOfRange(frame, 12, 14);
return Util.unsignedShortToInt(type) == ETHER_TYPE_VLAN ? true : false;
}
public static byte[] getVLANtag(byte[] frame) {
if (!isVLAN(frame))
throw new RuntimeException("this is not a VLAN frame");
return Arrays.copyOfRange(frame, 14, 16);
}
public static boolean isIEEE_802_3_Frame(byte[] frame) {
return (getEtherType(frame) <= 1500) ? true : false;
}
public static boolean isEthernet2Frame(byte[] frame) {
return (getEtherType(frame) > 1500) ? true : false;
}
/**
* in case of an ethernet II frame, the result MAY contain padding bytes.
* in case of an IEEE 802.3 frame, the result will NEVER contain padding.
* @param frame
* @return
*/
public static byte[] getPayloadStat(byte[] frame) {
int etherType = getEtherType(frame);
int payloadLength;
if (etherType <= 1500) { // IEEE 802.3 frame
payloadLength = etherType;
int startOffset = isVLAN(frame) ? 18 : 14;
return (payloadLength < 1) ? null : Arrays.copyOfRange(frame, startOffset, startOffset + payloadLength);
} else { // ethernet II frame
payloadLength = isVLAN(frame) ? frame.length - 18 : frame.length - 14;
int crcLength = (frame.length < 64) ? 0 : 4; // (frame.length < 64) indicates a truncated packet; 4 is the normal length of the crc field
return (payloadLength < 1) ? null : Arrays.copyOfRange(frame, frame.length - payloadLength, frame.length -crcLength);
}
}
public static int getLengthStat(byte[] frame) {
int etherType = getEtherType(frame);
if (etherType <= 1500) { // IEEE 802.3 frame
return etherType;
} else { // ethernet II frame
return frame.length;
}
}
public static int getHeaderLengthStat(byte[] frame) {
return isVLAN(frame) ? 18 : 14;
}
public static int getPayloadLengthStat(byte[] frame) {
int etherType = getEtherType(frame);
int payloadLength;
if (etherType <= 1500) { // IEEE 802.3 frame
return etherType;
} else { // ethernet II frame
payloadLength = isVLAN(frame) ? frame.length - 18 : frame.length - 14;
int crcLength = (frame.length < 64) ? 0 : 4; // (frame.length < 64) indicates a truncated packet; 4 is the normal length of the crc field
return payloadLength - crcLength;
}
}
public static byte[] calculateCRC(byte[] frame) {
CRC32 crcImpl = new CRC32();
crcImpl.update(Arrays.copyOf(frame, frame.length - 4));
return Arrays.copyOfRange(Util.longToByteArray(crcImpl.getValue()), 0, 4);
}
public static byte[] getCRC(byte[] frame) {
return Arrays.copyOfRange(frame, frame.length-4, frame.length);
}
public static boolean isCRCcorrect(byte[] frame) {
return Arrays.equals(getCRC(frame), calculateCRC(frame));
}
public static byte[] getDstMac(byte[] frame) {
return Arrays.copyOfRange(frame, 0, 6);
}
public static String getDstMacAsString(byte[] frame) {
return Util.toHex(getDstMac(frame));
}
public static byte[] getSrcMac(byte[] frame) {
return Arrays.copyOfRange(frame, 6, 12);
}
public static String getSrcMacAsString(byte[] frame) {
return Util.toHex(getSrcMac(frame));
}
public static boolean getIsIP(byte[] frame) {
int etherType = getEtherType(frame);
return etherType == ETHER_TYPE_IP_V4 || etherType == ETHER_TYPE_IP_V6;
}
public static boolean getIsIPv4(byte[] frame) {
return getEtherType(frame) == ETHER_TYPE_IP_V4;
}
public static boolean getIsIPv6(byte[] frame) {
return getEtherType(frame) == ETHER_TYPE_IP_V6;
}
public static boolean getIsARP(byte[] frame) {
return getEtherType(frame) == ETHER_TYPE_ARP;
}
public static boolean getIsWOL(byte[] frame) {
return getEtherType(frame) == ETHER_TYPE_WOL;
}
public static String etherTypeToString(int etherType) {
switch (etherType) {
case ETHER_TYPE_IP_V4:
return "ETHER_TYPE_IP_V4";
case ETHER_TYPE_IP_V6:
return "ETHER_TYPE_IP_V6";
case ETHER_TYPE_ARP:
return "ETHER_TYPE_JUMBO_FRAMES";
case ETHER_TYPE_VLAN:
return "ETHER_TYPE_VLAN";
case ETHER_TYPE_WOL:
return "ETHER_TYPE_WOL";
default:
System.err.println("warning: unknown ether type: " +etherType);
return ""+etherType;
}
}
public static String toString(byte[] packet) {
StringBuffer sb = new StringBuffer();
sb.append("ethernet frame header: \n");
sb.append(" source mac: " +EthernetFrame.getSrcMacAsString(packet) +"\n");
sb.append(" destination mac: " +EthernetFrame.getDstMacAsString(packet) +"\n");
sb.append(" type: " +EthernetFrame.getEtherTypeAsString(packet) +"\n");
sb.append(" is IEEE 802.3 Frame: " +EthernetFrame.isIEEE_802_3_Frame(packet) +"\n");
sb.append(" is Ethernet II Frame: " +EthernetFrame.isEthernet2Frame(packet) +"\n");
sb.append(" crc correct: " +EthernetFrame.isCRCcorrect(packet) +"\n");
boolean isVlan = EthernetFrame.isVLAN(packet);
sb.append(" is vlan: " +isVlan +"\n");
if (isVlan)
sb.append("\n vlan-tag: " +Util.toHex(EthernetFrame.getVLANtag(packet)) +"\n");
byte[] payload = EthernetFrame.getPayloadStat(packet);
if (payload == null)
sb.append(" payload: none");
else
sb.append(" payload (" +payload.length +" bytes): " +Util.toHex(payload));
return sb.toString();
}
}