/* * Copyright 2014-present Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onlab.packet; import java.nio.ByteBuffer; import java.util.Arrays; import static com.google.common.base.MoreObjects.toStringHelper; import static org.onlab.packet.PacketUtils.checkHeaderLength; import static org.onlab.packet.PacketUtils.checkInput; /** * Representation of an ARP Packet. */ public class ARP extends BasePacket { public static final short HW_TYPE_ETHERNET = 0x1; public static final short PROTO_TYPE_IP = 0x800; public static final short OP_REQUEST = 0x1; public static final short OP_REPLY = 0x2; public static final short OP_RARP_REQUEST = 0x3; public static final short OP_RARP_REPLY = 0x4; public static final short INITIAL_HEADER_LENGTH = 8; protected short hardwareType; protected short protocolType; protected byte hardwareAddressLength; protected byte protocolAddressLength; protected short opCode; protected byte[] senderHardwareAddress; protected byte[] senderProtocolAddress; protected byte[] targetHardwareAddress; protected byte[] targetProtocolAddress; /** * @return the hardwareType */ public short getHardwareType() { return this.hardwareType; } /** * @param hwType * the hardwareType to set * @return this */ public ARP setHardwareType(final short hwType) { this.hardwareType = hwType; return this; } /** * @return the protocolType */ public short getProtocolType() { return this.protocolType; } /** * @param protoType * the protocolType to set * @return this */ public ARP setProtocolType(final short protoType) { this.protocolType = protoType; return this; } /** * @return the hardwareAddressLength */ public byte getHardwareAddressLength() { return this.hardwareAddressLength; } /** * @param hwAddressLength * the hardwareAddressLength to set * @return this */ public ARP setHardwareAddressLength(final byte hwAddressLength) { this.hardwareAddressLength = hwAddressLength; return this; } /** * @return the protocolAddressLength */ public byte getProtocolAddressLength() { return this.protocolAddressLength; } /** * @param protoAddressLength * the protocolAddressLength to set * @return this */ public ARP setProtocolAddressLength(final byte protoAddressLength) { this.protocolAddressLength = protoAddressLength; return this; } /** * @return the opCode */ public short getOpCode() { return this.opCode; } /** * @param op * the opCode to set * @return this */ public ARP setOpCode(final short op) { this.opCode = op; return this; } /** * @return the senderHardwareAddress */ public byte[] getSenderHardwareAddress() { return this.senderHardwareAddress; } /** * @param senderHWAddress * the senderHardwareAddress to set * @return this */ public ARP setSenderHardwareAddress(final byte[] senderHWAddress) { this.senderHardwareAddress = senderHWAddress; return this; } /** * @return the senderProtocolAddress */ public byte[] getSenderProtocolAddress() { return this.senderProtocolAddress; } /** * @param senderProtoAddress * the senderProtocolAddress to set * @return this */ public ARP setSenderProtocolAddress(final byte[] senderProtoAddress) { this.senderProtocolAddress = senderProtoAddress; return this; } public ARP setSenderProtocolAddress(final int address) { this.senderProtocolAddress = ByteBuffer.allocate(4).putInt(address) .array(); return this; } /** * @return the targetHardwareAddress */ public byte[] getTargetHardwareAddress() { return this.targetHardwareAddress; } /** * @param targetHWAddress * the targetHardwareAddress to set * @return this */ public ARP setTargetHardwareAddress(final byte[] targetHWAddress) { this.targetHardwareAddress = targetHWAddress; return this; } /** * @return the targetProtocolAddress */ public byte[] getTargetProtocolAddress() { return this.targetProtocolAddress; } /** * @return True if gratuitous ARP (SPA = TPA), false otherwise */ public boolean isGratuitous() { assert this.senderProtocolAddress.length == this.targetProtocolAddress.length; int indx = 0; while (indx < this.senderProtocolAddress.length) { if (this.senderProtocolAddress[indx] != this.targetProtocolAddress[indx]) { return false; } indx++; } return true; } /** * @param targetProtoAddress * the targetProtocolAddress to set * @return this */ public ARP setTargetProtocolAddress(final byte[] targetProtoAddress) { this.targetProtocolAddress = targetProtoAddress; return this; } public ARP setTargetProtocolAddress(final int address) { this.targetProtocolAddress = ByteBuffer.allocate(4).putInt(address) .array(); return this; } @Override public byte[] serialize() { final int length = 8 + 2 * (0xff & this.hardwareAddressLength) + 2 * (0xff & this.protocolAddressLength); final byte[] data = new byte[length]; final ByteBuffer bb = ByteBuffer.wrap(data); bb.putShort(this.hardwareType); bb.putShort(this.protocolType); bb.put(this.hardwareAddressLength); bb.put(this.protocolAddressLength); bb.putShort(this.opCode); bb.put(this.senderHardwareAddress, 0, 0xff & this.hardwareAddressLength); bb.put(this.senderProtocolAddress, 0, 0xff & this.protocolAddressLength); bb.put(this.targetHardwareAddress, 0, 0xff & this.hardwareAddressLength); bb.put(this.targetProtocolAddress, 0, 0xff & this.protocolAddressLength); return data; } @Override public IPacket deserialize(final byte[] data, final int offset, final int length) { final ByteBuffer bb = ByteBuffer.wrap(data, offset, length); this.hardwareType = bb.getShort(); this.protocolType = bb.getShort(); this.hardwareAddressLength = bb.get(); this.protocolAddressLength = bb.get(); this.opCode = bb.getShort(); this.senderHardwareAddress = new byte[0xff & this.hardwareAddressLength]; bb.get(this.senderHardwareAddress, 0, this.senderHardwareAddress.length); this.senderProtocolAddress = new byte[0xff & this.protocolAddressLength]; bb.get(this.senderProtocolAddress, 0, this.senderProtocolAddress.length); this.targetHardwareAddress = new byte[0xff & this.hardwareAddressLength]; bb.get(this.targetHardwareAddress, 0, this.targetHardwareAddress.length); this.targetProtocolAddress = new byte[0xff & this.protocolAddressLength]; bb.get(this.targetProtocolAddress, 0, this.targetProtocolAddress.length); return this; } /* * (non-Javadoc) * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 13121; int result = super.hashCode(); result = prime * result + this.hardwareAddressLength; result = prime * result + this.hardwareType; result = prime * result + this.opCode; result = prime * result + this.protocolAddressLength; result = prime * result + this.protocolType; result = prime * result + Arrays.hashCode(this.senderHardwareAddress); result = prime * result + Arrays.hashCode(this.senderProtocolAddress); result = prime * result + Arrays.hashCode(this.targetHardwareAddress); result = prime * result + Arrays.hashCode(this.targetProtocolAddress); return result; } /* * (non-Javadoc) * * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (!(obj instanceof ARP)) { return false; } final ARP other = (ARP) obj; if (this.hardwareAddressLength != other.hardwareAddressLength) { return false; } if (this.hardwareType != other.hardwareType) { return false; } if (this.opCode != other.opCode) { return false; } if (this.protocolAddressLength != other.protocolAddressLength) { return false; } if (this.protocolType != other.protocolType) { return false; } if (!Arrays.equals(this.senderHardwareAddress, other.senderHardwareAddress)) { return false; } if (!Arrays.equals(this.senderProtocolAddress, other.senderProtocolAddress)) { return false; } if (!Arrays.equals(this.targetHardwareAddress, other.targetHardwareAddress)) { return false; } if (!Arrays.equals(this.targetProtocolAddress, other.targetProtocolAddress)) { return false; } return true; } /** * Builds an ARP request using the supplied parameters. * * @param senderMacAddress the mac address of the sender * @param senderIpAddress the ip address of the sender * @param targetAddress the address to resolve * @param vlanId the vlan id * @return the Ethernet frame containing the ARP request */ public static Ethernet buildArpRequest(byte[] senderMacAddress, byte[] senderIpAddress, byte[] targetAddress, short vlanId) { if (senderMacAddress.length != MacAddress.MAC_ADDRESS_LENGTH || senderIpAddress.length != Ip4Address.BYTE_LENGTH || targetAddress.length != Ip4Address.BYTE_LENGTH) { return null; } ARP arpRequest = new ARP(); arpRequest.setHardwareType(ARP.HW_TYPE_ETHERNET) .setProtocolType(ARP.PROTO_TYPE_IP) .setHardwareAddressLength( (byte) Ethernet.DATALAYER_ADDRESS_LENGTH) .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH) .setOpCode(ARP.OP_REQUEST) .setSenderHardwareAddress(senderMacAddress) .setTargetHardwareAddress(MacAddress.ZERO.toBytes()) .setSenderProtocolAddress(senderIpAddress) .setTargetProtocolAddress(targetAddress); Ethernet eth = new Ethernet(); eth.setDestinationMACAddress(MacAddress.BROADCAST.toBytes()) .setSourceMACAddress(senderMacAddress) .setEtherType(Ethernet.TYPE_ARP) .setVlanID(vlanId) .setPayload(arpRequest); return eth; } /** * Builds an ARP reply based on a request. * * @param srcIp the IP address to use as the reply source * @param srcMac the MAC address to use as the reply source * @param request the ARP request we got * @return an Ethernet frame containing the ARP reply */ public static Ethernet buildArpReply(Ip4Address srcIp, MacAddress srcMac, Ethernet request) { Ethernet eth = new Ethernet(); eth.setDestinationMACAddress(request.getSourceMAC()); eth.setSourceMACAddress(srcMac); eth.setEtherType(Ethernet.TYPE_ARP); eth.setVlanID(request.getVlanID()); ARP arp = new ARP(); arp.setOpCode(ARP.OP_REPLY); arp.setProtocolType(ARP.PROTO_TYPE_IP); arp.setHardwareType(ARP.HW_TYPE_ETHERNET); arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH); arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH); arp.setSenderHardwareAddress(srcMac.toBytes()); arp.setTargetHardwareAddress(request.getSourceMACAddress()); arp.setTargetProtocolAddress(((ARP) request.getPayload()) .getSenderProtocolAddress()); arp.setSenderProtocolAddress(srcIp.toInt()); eth.setPayload(arp); return eth; } /** * Deserializer function for ARP packets. * * @return deserializer function */ public static Deserializer<ARP> deserializer() { return (data, offset, length) -> { checkInput(data, offset, length, INITIAL_HEADER_LENGTH); ARP arp = new ARP(); final ByteBuffer bb = ByteBuffer.wrap(data, offset, length); arp.setHardwareType(bb.getShort()); arp.setProtocolType(bb.getShort()); byte hwAddressLength = bb.get(); arp.setHardwareAddressLength(hwAddressLength); byte protocolAddressLength = bb.get(); arp.setProtocolAddressLength(protocolAddressLength); arp.setOpCode(bb.getShort()); // Check we have enough space for the addresses checkHeaderLength(length, INITIAL_HEADER_LENGTH + 2 * hwAddressLength + 2 * protocolAddressLength); arp.senderHardwareAddress = new byte[0xff & hwAddressLength]; bb.get(arp.senderHardwareAddress, 0, arp.senderHardwareAddress.length); arp.senderProtocolAddress = new byte[0xff & protocolAddressLength]; bb.get(arp.senderProtocolAddress, 0, arp.senderProtocolAddress.length); arp.targetHardwareAddress = new byte[0xff & hwAddressLength]; bb.get(arp.targetHardwareAddress, 0, arp.targetHardwareAddress.length); arp.targetProtocolAddress = new byte[0xff & protocolAddressLength]; bb.get(arp.targetProtocolAddress, 0, arp.targetProtocolAddress.length); return arp; }; } @Override public String toString() { return toStringHelper(getClass()) .add("hardwareType", Short.toString(hardwareType)) .add("protocolType", Short.toString(protocolType)) .add("hardwareAddressLength", Byte.toString(hardwareAddressLength)) .add("protocolAddressLength", Byte.toString(protocolAddressLength)) .add("opCode", Short.toString(opCode)) .add("senderHardwareAddress", MacAddress.valueOf(senderHardwareAddress)) .add("senderProtocolAddress", Ip4Address.valueOf(senderProtocolAddress)) .add("targetHardwareAddress", MacAddress.valueOf(targetHardwareAddress)) .add("targetProtocolAddress", Ip4Address.valueOf(targetProtocolAddress)) .toString(); } }