package com.netifera.platform.net.packets.link; import java.util.Arrays; import com.netifera.platform.net.packets.AbstractPacket; import com.netifera.platform.net.packets.IPacketHeader; import com.netifera.platform.net.packets.PacketPayload; import com.netifera.platform.util.NetworkConstants; import com.netifera.platform.util.addresses.IHardwareAddress; import com.netifera.platform.util.addresses.INetworkAddress; import com.netifera.platform.util.addresses.MACAddress; import com.netifera.platform.util.addresses.inet.IPv4Address; import com.netifera.platform.util.addresses.inet.IPv6Address; public class ARP extends AbstractPacket implements EthernetEncapsulable { /** * 0 1 2 3 4 5 6 7 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Hardware Type | Protocol Type |HW Size|Pr Size| Operation | * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Sender Hardware Address ..|.. Sender Protocol Address ..| * +-------+-------+-------+-------+-------+-------+-------+-------+ * |.. Target Hardware Address ..|.. Target Protocol Address ..| * +-------+-------+-------+-------+-------+-------+-------+-------+ */ private int hardwareType; private int protocolType; private int hardwareSize; private int protocolSize; private int operation; private byte[] senderHardwareAddressBytes; private byte[] senderProtocolAddressBytes; private byte[] targetHardwareAddressBytes; private byte[] targetProtocolAddressBytes; private boolean unpackFailed; @Override protected int minimumHeaderLength() { return 8; } @Override protected void packHeader() { pack16(hardwareType); pack16(protocolType); pack8(hardwareSize); pack8(protocolSize); pack16(operation); packBytes(senderHardwareAddressBytes); packBytes(senderProtocolAddressBytes); if (targetHardwareAddressBytes == null) { targetHardwareAddressBytes = MACAddress.ANY.toBytes(); } packBytes(targetHardwareAddressBytes); packBytes(targetProtocolAddressBytes); } @Override protected void unpackHeader() { hardwareType = unpack16(); protocolType = unpack16(); hardwareSize = unpack8(); protocolSize = unpack8(); operation = unpack16(); if(remaining() < (2 * hardwareSize) + (2 * protocolSize)) { unpackFailed = true; return; } senderHardwareAddressBytes = unpackBytes(hardwareSize); senderProtocolAddressBytes = unpackBytes(protocolSize); targetHardwareAddressBytes = unpackBytes(hardwareSize); targetProtocolAddressBytes = unpackBytes(protocolSize); } protected boolean isValidHeader() { return !unpackFailed; } public ARP() { } public ARP(IPacketHeader pkt) { super(pkt); } public IHardwareAddress getSenderHardwareAddress() { if (hardwareType == NetworkConstants.ETHER_TYPE) return new MACAddress(senderHardwareAddressBytes); throw new RuntimeException("Unknown address type "+hardwareType); } public void setSenderHardwareAddress(MACAddress address) { senderHardwareAddressBytes = address.toBytes(); setHardwareEthernet(); } public IHardwareAddress getTargetHardwareAddress() { if (hardwareType == NetworkConstants.ETHER_TYPE) return new MACAddress(targetHardwareAddressBytes); throw new RuntimeException("Unknown hardware type "+hardwareType); } public void setTargetHardwareAddress(MACAddress address) { targetHardwareAddressBytes = address.toBytes(); setHardwareEthernet(); } public INetworkAddress getSenderProtocolAddress() { if (protocolType == NetworkConstants.ETHERTYPE_IPv4) return new IPv4Address(senderProtocolAddressBytes); else if (protocolType == NetworkConstants.ETHERTYPE_IPv6) return new IPv6Address(senderProtocolAddressBytes); throw new RuntimeException("Unknown protocol type "+protocolType); } public void setSenderProtocolAddress(INetworkAddress address) { senderProtocolAddressBytes = address.toBytes(); setProtocolIP(); } public INetworkAddress getTargetProtocolAddress() { if (protocolType == NetworkConstants.ETHERTYPE_IPv4) return new IPv4Address(targetProtocolAddressBytes); else if (protocolType == NetworkConstants.ETHERTYPE_IPv6) return new IPv6Address(targetProtocolAddressBytes); throw new RuntimeException("Unknown protocol type "+protocolType); } public void setTargetProtocolAddress(INetworkAddress address) { targetProtocolAddressBytes = address.toBytes(); if (address instanceof IPv6Address) setProtocolIPv6(); else setProtocolIP(); } private void setHardwareEthernet() { hardwareType = NetworkConstants.ETHER_TYPE; hardwareSize = 6; } private void setProtocolIP() { protocolType = NetworkConstants.ETHERTYPE_IPv4; protocolSize = 4; } private void setProtocolIPv6() { protocolType = NetworkConstants.ETHERTYPE_IPv6; protocolSize = 16; } void setOperation(int operation) { this.operation = operation; } public static ARP whoHas(INetworkAddress targetProtocolAddress, MACAddress senderHardwareAddress, INetworkAddress senderProtocolAddress) { ARP arp = new ARP(PacketPayload.emptyPayload()); arp.setSenderHardwareAddress(senderHardwareAddress); arp.setSenderProtocolAddress(senderProtocolAddress); arp.setTargetProtocolAddress(targetProtocolAddress); arp.setOperation(NetworkConstants.ARPOP_REQUEST); return arp; } public static ARP isAt(INetworkAddress targetProtocolAddress, MACAddress targetHardwareAddress, INetworkAddress senderProtocolAddress, MACAddress senderHardwareAddress) { ARP arp = new ARP(PacketPayload.emptyPayload()); arp.setSenderHardwareAddress(senderHardwareAddress); arp.setSenderProtocolAddress(senderProtocolAddress); arp.setTargetHardwareAddress(targetHardwareAddress); arp.setTargetProtocolAddress(targetProtocolAddress); arp.setOperation(NetworkConstants.ARPOP_REPLY); return arp; } @Override public int headerLength() { return 8 + hardwareSize*2 + protocolSize*2; } public boolean isRequest() { return operation == NetworkConstants.ARPOP_REQUEST; } public boolean isReply() { return operation == NetworkConstants.ARPOP_REPLY; } // Answer true if this is a gratuitous ARP public boolean isAnnouncement() { return Arrays.equals(targetProtocolAddressBytes, senderProtocolAddressBytes); } @Override public String toString() { StringBuffer out = new StringBuffer(); out.append("ARP "); if (isRequest()) { out.append(" who-has "); out.append(getTargetProtocolAddress()); out.append(" tell "); out.append(getSenderProtocolAddress()); } else if (isReply()) { out.append(getSenderProtocolAddress()); out.append(" is-at "); out.append(getSenderHardwareAddress()); } else { out.append(" (opcode "); out.append(operation); out.append(")"); } return out.toString(); } public int protocolOverEthernet() { return NetworkConstants.ETHERTYPE_ARP; } }