diff --git a/src/main/java/net/floodlightcontroller/packet/Ethernet.java b/src/main/java/net/floodlightcontroller/packet/Ethernet.java index 1e935a63f2d5479f77731e3002333c8971fb2d48..fe74e9e0db5b7dc4fda483108521a985810dcd0e 100644 --- a/src/main/java/net/floodlightcontroller/packet/Ethernet.java +++ b/src/main/java/net/floodlightcontroller/packet/Ethernet.java @@ -22,6 +22,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import net.floodlightcontroller.util.MACAddress; import org.openflow.util.HexString; /** @@ -29,7 +30,6 @@ import org.openflow.util.HexString; * @author David Erickson (daviderickson@cs.stanford.edu) */ public class Ethernet extends BasePacket { - private static String HEXES = "0123456789ABCDEF"; public static final short TYPE_ARP = 0x0806; public static final short TYPE_IPv4 = 0x0800; public static final short TYPE_LLDP = (short) 0x88cc; @@ -43,8 +43,8 @@ public class Ethernet extends BasePacket { etherTypeClassMap.put(TYPE_LLDP, LLDP.class); } - protected byte[] destinationMACAddress; - protected byte[] sourceMACAddress; + protected MACAddress destinationMACAddress; + protected MACAddress sourceMACAddress; protected byte priorityCode; protected short vlanID; protected short etherType; @@ -62,14 +62,14 @@ public class Ethernet extends BasePacket { * @return the destinationMACAddress */ public byte[] getDestinationMACAddress() { - return destinationMACAddress; + return destinationMACAddress.toBytes(); } /** * @param destinationMACAddress the destinationMACAddress to set */ public Ethernet setDestinationMACAddress(byte[] destinationMACAddress) { - this.destinationMACAddress = destinationMACAddress; + this.destinationMACAddress = MACAddress.valueOf(destinationMACAddress); return this; } @@ -77,8 +77,7 @@ public class Ethernet extends BasePacket { * @param destinationMACAddress the destinationMACAddress to set */ public Ethernet setDestinationMACAddress(String destinationMACAddress) { - this.destinationMACAddress = Ethernet - .toMACAddress(destinationMACAddress); + this.destinationMACAddress = MACAddress.valueOf(destinationMACAddress); return this; } @@ -86,14 +85,14 @@ public class Ethernet extends BasePacket { * @return the sourceMACAddress */ public byte[] getSourceMACAddress() { - return sourceMACAddress; + return sourceMACAddress.toBytes(); } /** * @param sourceMACAddress the sourceMACAddress to set */ public Ethernet setSourceMACAddress(byte[] sourceMACAddress) { - this.sourceMACAddress = sourceMACAddress; + this.sourceMACAddress = MACAddress.valueOf(sourceMACAddress); return this; } @@ -101,7 +100,7 @@ public class Ethernet extends BasePacket { * @param sourceMACAddress the sourceMACAddress to set */ public Ethernet setSourceMACAddress(String sourceMACAddress) { - this.sourceMACAddress = Ethernet.toMACAddress(sourceMACAddress); + this.sourceMACAddress = MACAddress.valueOf(sourceMACAddress); return this; } @@ -154,22 +153,15 @@ public class Ethernet extends BasePacket { * @return True if the Ethernet frame is broadcast, false otherwise */ public boolean isBroadcast() { - assert(destinationMACAddress.length == 6); - for (byte b : destinationMACAddress) { - if (b != -1) // checks if equal to 0xff - return false; - } - return true; + assert(destinationMACAddress.length() == 6); + return destinationMACAddress.isBroadcast(); } /** * @return True is the Ethernet frame is multicast, False otherwise */ public boolean isMulticast() { - if (this.isBroadcast()) { - return false; - } - return (destinationMACAddress[0] & 0x01) != 0; + return destinationMACAddress.isMulticast(); } /** * Pad this packet to 60 bytes minimum, filling with zeros? @@ -201,8 +193,8 @@ public class Ethernet extends BasePacket { } byte[] data = new byte[length]; ByteBuffer bb = ByteBuffer.wrap(data); - bb.put(destinationMACAddress); - bb.put(sourceMACAddress); + bb.put(destinationMACAddress.toBytes()); + bb.put(sourceMACAddress.toBytes()); if (vlanID != VLAN_UNTAGGED) { bb.putShort((short) 0x8100); bb.putShort((short) ((priorityCode << 13) | (vlanID & 0x0fff))); @@ -220,12 +212,16 @@ public class Ethernet extends BasePacket { public IPacket deserialize(byte[] data, int offset, int length) { ByteBuffer bb = ByteBuffer.wrap(data, offset, length); if (this.destinationMACAddress == null) - this.destinationMACAddress = new byte[6]; - bb.get(this.destinationMACAddress); + this.destinationMACAddress = MACAddress.valueOf(new byte[6]); + byte[] dstAddr = new byte[MACAddress.MAC_ADDRESS_LENGTH]; + bb.get(dstAddr); + this.destinationMACAddress = MACAddress.valueOf(dstAddr); if (this.sourceMACAddress == null) - this.sourceMACAddress = new byte[6]; - bb.get(this.sourceMACAddress); + this.sourceMACAddress = MACAddress.valueOf(new byte[6]); + byte[] srcAddr = new byte[MACAddress.MAC_ADDRESS_LENGTH]; + bb.get(srcAddr); + this.sourceMACAddress = MACAddress.valueOf(srcAddr); short etherType = bb.getShort(); if (etherType == (short) 0x8100) { @@ -254,20 +250,6 @@ public class Ethernet extends BasePacket { return this; } - public static boolean isMACAddress(String macAddress) { - String[] macBytes = macAddress.split(":"); - if (macBytes.length != 6) - return false; - for (int i = 0; i < 6; ++i) { - if (HEXES.indexOf(macBytes[i].toUpperCase().charAt(0)) == -1 || - HEXES.indexOf(macBytes[i].toUpperCase().charAt(1)) == -1) { - return false; - } - } - - return true; - } - /** * Accepts a MAC address of the form 00:aa:11:bb:22:cc, case does not * matter, and returns a corresponding byte[]. @@ -275,24 +257,10 @@ public class Ethernet extends BasePacket { * @return */ public static byte[] toMACAddress(String macAddress) { - byte[] address = new byte[6]; - - if (!Ethernet.isMACAddress(macAddress)) { - throw new IllegalArgumentException( - "Specified MAC Address must contain 12 hex digits" + - " separated pairwise by :'s."); - } - String[] macBytes = macAddress.split(":"); - for (int i = 0; i < 6; ++i) { - address[i] = (byte) ((HEXES.indexOf(macBytes[i].toUpperCase() - .charAt(0)) << 4) | HEXES.indexOf(macBytes[i].toUpperCase() - .charAt(1))); - } - - return address; + return MACAddress.valueOf(macAddress).toBytes(); } - + /** * Accepts a MAC address and returns the corresponding long, where the * MAC bytes are set on the lower order bytes of the long. @@ -300,28 +268,16 @@ public class Ethernet extends BasePacket { * @return a long containing the mac address bytes */ public static long toLong(byte[] macAddress) { - long mac = 0; - for (int i = 0; i < 6; i++) { - long t = (macAddress[i] & 0xffL) << ((5-i)*8); - mac |= t; - } - return mac; + return MACAddress.valueOf(macAddress).toLong(); } - + /** * Convert a long MAC address to a byte array * @param macAddress * @return the bytes of the mac address */ public static byte[] toByteArray(long macAddress) { - return new byte[] { - (byte)((macAddress >> 40) & 0xff), - (byte)((macAddress >> 32) & 0xff), - (byte)((macAddress >> 24) & 0xff), - (byte)((macAddress >> 16) & 0xff), - (byte)((macAddress >> 8 ) & 0xff), - (byte)((macAddress >> 0) & 0xff) - }; + return MACAddress.valueOf(macAddress).toBytes(); } /* (non-Javadoc) @@ -331,10 +287,10 @@ public class Ethernet extends BasePacket { public int hashCode() { final int prime = 7867; int result = super.hashCode(); - result = prime * result + Arrays.hashCode(destinationMACAddress); + result = prime * result + destinationMACAddress.hashCode(); result = prime * result + etherType; result = prime * result + (pad ? 1231 : 1237); - result = prime * result + Arrays.hashCode(sourceMACAddress); + result = prime * result + sourceMACAddress.hashCode(); return result; } @@ -350,7 +306,7 @@ public class Ethernet extends BasePacket { if (!(obj instanceof Ethernet)) return false; Ethernet other = (Ethernet) obj; - if (!Arrays.equals(destinationMACAddress, other.destinationMACAddress)) + if (!destinationMACAddress.equals(other.destinationMACAddress)) return false; if (priorityCode != other.priorityCode) return false; @@ -360,7 +316,7 @@ public class Ethernet extends BasePacket { return false; if (pad != other.pad) return false; - if (!Arrays.equals(sourceMACAddress, other.sourceMACAddress)) + if (!sourceMACAddress.equals(other.sourceMACAddress)) return false; return true; } diff --git a/src/main/java/net/floodlightcontroller/util/MACAddress.java b/src/main/java/net/floodlightcontroller/util/MACAddress.java new file mode 100644 index 0000000000000000000000000000000000000000..f80f110d504b511b8f0b6e3962c985f2ca1be6b3 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/util/MACAddress.java @@ -0,0 +1,157 @@ +package net.floodlightcontroller.util; + +import java.util.Arrays; + +/** + * The class representing MAC address. + * + * @author Sho Shimizu (sho.shimizu@gmail.com) + */ +public class MACAddress { + public static final int MAC_ADDRESS_LENGTH = 6; + private byte[] address = new byte[MAC_ADDRESS_LENGTH]; + + private MACAddress(byte[] address) { + this.address = Arrays.copyOf(address, MAC_ADDRESS_LENGTH); + } + + /** + * Returns a MAC address instance representing the value of the specified {@code String}. + * @param address the String representation of the MAC Address to be parsed. + * @return a MAC Address instance representing the value of the specified {@code String}. + * @throws IllegalArgumentException if the string cannot be parsed as a MAC address. + */ + public static MACAddress valueOf(String address) { + String[] elements = address.split(":"); + if (elements.length != MAC_ADDRESS_LENGTH) { + throw new IllegalArgumentException( + "Specified MAC Address must contain 12 hex digits" + + " separated pairwise by :'s."); + } + + byte[] addressInBytes = new byte[MAC_ADDRESS_LENGTH]; + for (int i = 0; i < MAC_ADDRESS_LENGTH; i++) { + String element = elements[i]; + addressInBytes[i] = (byte)Integer.parseInt(element, 16); + } + + return new MACAddress(addressInBytes); + } + + /** + * Returns a MAC address instance representing the specified {@code byte} array. + * @param address the byte array to be parsed. + * @return a MAC address instance representing the specified {@code byte} array. + * @throws IllegalArgumentException if the byte array cannot be parsed as a MAC address. + */ + public static MACAddress valueOf(byte[] address) { + if (address.length != MAC_ADDRESS_LENGTH) { + throw new IllegalArgumentException("the length is not " + MAC_ADDRESS_LENGTH); + } + + return new MACAddress(address); + } + + /** + * Returns a MAC address instance representing the specified {@code long} value. + * The lower 48 bits of the long value are used to parse as a MAC address. + * @param address the long value to be parsed. The lower 48 bits are used for a MAC address. + * @return a MAC address instance representing the specified {@code long} value. + * @throws IllegalArgumentException if the long value cannot be parsed as a MAC address. + */ + public static MACAddress valueOf(long address) { + byte[] addressInBytes = new byte[] { + (byte)((address >> 40) & 0xff), + (byte)((address >> 32) & 0xff), + (byte)((address >> 24) & 0xff), + (byte)((address >> 16) & 0xff), + (byte)((address >> 8 ) & 0xff), + (byte)((address >> 0) & 0xff) + }; + + return new MACAddress(addressInBytes); + } + + /** + * Returns the length of the {@code MACAddress}. + * @return the length of the {@code MACAddress}. + */ + public int length() { + return address.length; + } + + /** + * Returns the value of the {@code MACAddress} as a {@code byte} array. + * @return the numeric value represented by this object after conversion to type {@code byte} array. + */ + public byte[] toBytes() { + return Arrays.copyOf(address, address.length); + } + + /** + * Returns the value of the {@code MACAddress} as a {@code long}. + * @return the numeric value represented by this object after conversion to type {@code long}. + */ + public long toLong() { + long mac = 0; + for (int i = 0; i < 6; i++) { + long t = (address[i] & 0xffL) << ((5 - i) * 8); + mac |= t; + } + return mac; + } + + /** + * Returns {@code true} if the MAC address is the broadcast address. + * @return {@code true} if the MAC address is the broadcast address. + */ + public boolean isBroadcast() { + for (byte b : address) { + if (b != -1) // checks if equal to 0xff + return false; + } + return true; + } + + /** + * Returns {@code true} if the MAC address is the multicast address. + * @return {@code true} if the MAC address is the multicast address. + */ + public boolean isMulticast() { + if (isBroadcast()) { + return false; + } + return (address[0] & 0x01) != 0; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof MACAddress)) { + return false; + } + + MACAddress other = (MACAddress)o; + return Arrays.equals(this.address, other.address); + } + + @Override + public int hashCode() { + return Arrays.hashCode(this.address); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + for (byte b: address) { + if (builder.length() > 0) { + builder.append(":"); + } + builder.append(String.format("%02X", b & 0xFF)); + } + return builder.toString(); + } +} diff --git a/src/test/java/net/floodlightcontroller/util/MACAddressTest.java b/src/test/java/net/floodlightcontroller/util/MACAddressTest.java new file mode 100644 index 0000000000000000000000000000000000000000..158f321d0bbbad1684d51a59c2ece8798ff51068 --- /dev/null +++ b/src/test/java/net/floodlightcontroller/util/MACAddressTest.java @@ -0,0 +1,85 @@ +package net.floodlightcontroller.util; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * @author Sho Shimizu (sho.shimizu@gmail.com) + */ +public class MACAddressTest { + @Test + public void testValueOf() { + MACAddress address = MACAddress.valueOf("00:01:02:03:04:05"); + assertEquals(address, + MACAddress.valueOf(new byte[]{0x00, 0x01, 0x02, 0x03, 0x04, 0x05})); + assertEquals("00:01:02:03:04:05", address.toString()); + + address = MACAddress.valueOf("FF:FE:FD:10:20:30"); + assertEquals(address, + MACAddress.valueOf(new byte[]{(byte) 0xFF, (byte) 0xFE, (byte) 0xFD, 0x10, 0x20, 0x30})); + assertEquals("FF:FE:FD:10:20:30", address.toString()); + + address = MACAddress.valueOf("00:11:22:aa:bb:cc"); + assertEquals(address, + MACAddress.valueOf(new byte[]{0x00, 0x11, 0x22, (byte)0xaa, (byte)0xbb, (byte)0xcc})); + } + + @Test(expected=NumberFormatException.class) + public void testIllegalFormat() { + MACAddress address = MACAddress.valueOf("0T:00:01:02:03:04"); + } + + @Test(expected=IllegalArgumentException.class) + public void testLongStringFields() { + MACAddress address = MACAddress.valueOf("00:01:02:03:04:05:06"); + } + + @Test(expected=IllegalArgumentException.class) + public void testShortStringFields() { + MACAddress address = MACAddress.valueOf("00:01:02:03:04"); + } + + @Test(expected=IllegalArgumentException.class) + public void testLongByteFields() { + MACAddress address = MACAddress.valueOf(new byte[]{0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06}); + } + + @Test(expected=IllegalArgumentException.class) + public void testShortByteField() { + MACAddress address = MACAddress.valueOf(new byte[]{0x01, 0x01, 0x02, 0x03, 0x04}); + } + + // Test data is imported from net.floodlightcontroller.packet.EthernetTest + @Test + public void testToLong() { + assertEquals( + 281474976710655L, + MACAddress.valueOf(new byte[]{(byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff}).toLong()); + + assertEquals( + 1103823438081L, + MACAddress.valueOf(new byte[] { (byte) 0x01, (byte) 0x01, + (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01 }).toLong()); + + assertEquals( + 141289400074368L, + MACAddress.valueOf(new byte[] { (byte) 0x80, (byte) 0x80, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80 }).toLong()); + + } + + @Test + public void testIsBroadcast() { + assertTrue(MACAddress.valueOf("FF:FF:FF:FF:FF:FF").isBroadcast()); + assertFalse(MACAddress.valueOf("11:22:33:44:55:66").isBroadcast()); + } + + @Test + public void testIsMulticast() { + assertTrue(MACAddress.valueOf("01:80:C2:00:00:00").isMulticast()); + assertFalse(MACAddress.valueOf("FF:FF:FF:FF:FF:FF").isMulticast()); + assertFalse(MACAddress.valueOf("11:22:33:44:55:66").isBroadcast()); + } +}