blob: b73db431ca8f8313e0688d2a7a0f47fc12c5665e [file] [log] [blame]
Yotam Harcholf3f11152013-09-05 16:47:16 -07001package org.projectfloodlight.openflow.types;
2
Andreas Wundsam5f71b412014-02-18 12:56:35 -08003import java.util.Arrays;
4
Andreas Wundsam3700d162014-03-11 04:43:38 -07005import javax.annotation.Nonnull;
6
Yotam Harcholf3f11152013-09-05 16:47:16 -07007import org.jboss.netty.buffer.ChannelBuffer;
8import org.projectfloodlight.openflow.exceptions.OFParseError;
9import org.projectfloodlight.openflow.util.HexString;
10
Sovietaced5b75b7c2015-07-21 18:39:53 -040011import com.google.common.base.Preconditions;
Andreas Wundsam22ba3af2013-10-04 16:00:30 -070012import com.google.common.hash.PrimitiveSink;
Andreas Wundsam85c961f2013-09-29 21:22:12 -070013import com.google.common.primitives.Longs;
14
Yotam Harcholf3f11152013-09-05 16:47:16 -070015/**
16 * Wrapper around a 6 byte mac address.
17 *
kjwon157bc85402015-02-12 15:07:42 +090018 * @author Andreas Wundsam {@literal <}andreas.wundsam@bigswitch.com{@literal >}
Yotam Harcholf3f11152013-09-05 16:47:16 -070019 */
20
21public class MacAddress implements OFValueType<MacAddress> {
22 static final int MacAddrLen = 6;
23 private final long rawValue;
24
Andreas Wundsamb75c4ad2013-09-23 14:45:35 -070025 private final static long NONE_VAL = 0x0L;
26 public static final MacAddress NONE = new MacAddress(NONE_VAL);
27
Rob Vaterlaus83f40ab2013-10-14 15:44:46 -070028 private final static long BROADCAST_VAL = 0x0000FFFFFFFFFFFFL;
29 public static final MacAddress BROADCAST = new MacAddress(BROADCAST_VAL);
30
Yotam Harcholf3f11152013-09-05 16:47:16 -070031 public static final MacAddress NO_MASK = MacAddress.of(0xFFFFFFFFFFFFFFFFl);
32 public static final MacAddress FULL_MASK = MacAddress.of(0x0);
33
Shudong Zhoua4fb4c62014-02-10 16:57:45 -080034 private static final long LLDP_MAC_ADDRESS_MASK = 0xfffffffffff0L;
35 private static final long LLDP_MAC_ADDRESS_VALUE = 0x0180c2000000L;
Shudong Zhoue5a36802014-02-09 14:54:14 -080036
Sovietaced5b75b7c2015-07-21 18:39:53 -040037 private static final String FORMAT_ERROR = "Mac address is not well-formed. " +
38 "It must consist of 6 hex digit pairs separated by colons: ";
Sovietaced8fe84f32015-07-22 22:17:31 -040039 private static final int MAC_STRING_LENGTH = 6 * 2 + 5;
Sovietaced5b75b7c2015-07-21 18:39:53 -040040
Yotam Harcholf3f11152013-09-05 16:47:16 -070041 private MacAddress(final long rawValue) {
42 this.rawValue = rawValue;
43 }
44
45 public static MacAddress of(final byte[] address) {
Rob Vaterlaus83f40ab2013-10-14 15:44:46 -070046 if (address.length != MacAddrLen)
47 throw new IllegalArgumentException(
48 "Mac address byte array must be exactly 6 bytes long; length = " + address.length);
Yotam Harcholf3f11152013-09-05 16:47:16 -070049 long raw =
50 (address[0] & 0xFFL) << 40 | (address[1] & 0xFFL) << 32
51 | (address[2] & 0xFFL) << 24 | (address[3] & 0xFFL) << 16
52 | (address[4] & 0xFFL) << 8 | (address[5] & 0xFFL);
53 return MacAddress.of(raw);
54 }
55
Rob Vaterlaus83f40ab2013-10-14 15:44:46 -070056 public static MacAddress of(long raw) {
57 raw &= BROADCAST_VAL;
Andreas Wundsamb75c4ad2013-09-23 14:45:35 -070058 if(raw == NONE_VAL)
59 return NONE;
Rob Vaterlaus83f40ab2013-10-14 15:44:46 -070060 if (raw == BROADCAST_VAL)
61 return BROADCAST;
Yotam Harcholf3f11152013-09-05 16:47:16 -070062 return new MacAddress(raw);
63 }
64
Andreas Wundsam3700d162014-03-11 04:43:38 -070065 /** Parse a mac adress from the canonical string representation as
66 * 6 hex bytes separated by colons (01:02:03:04:05:06).
67 *
68 * @param macString - a mac address in canonical string representation
69 * @return the parsed MacAddress
70 * @throws IllegalArgumentException if macString is not a valid mac adddress
71 */
72 @Nonnull
73 public static MacAddress of(@Nonnull final String macString) throws IllegalArgumentException {
Sovietaced8fe84f32015-07-22 22:17:31 -040074 if (macString == null) {
75 throw new NullPointerException("macString must not be null");
76 }
Sovietaced5b75b7c2015-07-21 18:39:53 -040077
Yotam Harcholf3f11152013-09-05 16:47:16 -070078 int index = 0;
79 int shift = 40;
Yotam Harcholf3f11152013-09-05 16:47:16 -070080 long raw = 0;
Yotam Harcholf3f11152013-09-05 16:47:16 -070081
Sovietaced8fe84f32015-07-22 22:17:31 -040082 if (macString.length() != MAC_STRING_LENGTH) {
83 throw new IllegalArgumentException(FORMAT_ERROR + macString);
84 }
85
Yotam Harcholf3f11152013-09-05 16:47:16 -070086 while (shift >= 0) {
Andreas Wundsam3700d162014-03-11 04:43:38 -070087 int digit1 = Character.digit(macString.charAt(index++), 16);
88 int digit2 = Character.digit(macString.charAt(index++), 16);
Rob Vaterlaus83f40ab2013-10-14 15:44:46 -070089 if ((digit1 < 0) || (digit2 < 0))
Andreas Wundsam3700d162014-03-11 04:43:38 -070090 throw new IllegalArgumentException(FORMAT_ERROR + macString);
Rob Vaterlaus83f40ab2013-10-14 15:44:46 -070091 raw |= ((long) (digit1 << 4 | digit2)) << shift;
Yotam Harcholf3f11152013-09-05 16:47:16 -070092
93 if (shift == 0)
94 break;
Andreas Wundsam3700d162014-03-11 04:43:38 -070095 if (macString.charAt(index++) != ':')
96 throw new IllegalArgumentException(FORMAT_ERROR + macString);
Yotam Harcholf3f11152013-09-05 16:47:16 -070097 shift -= 8;
98 }
99 return MacAddress.of(raw);
100 }
101
Sovietaced5b75b7c2015-07-21 18:39:53 -0400102 /**
103 * Creates a {@link MacAddress} from a {@link DatapathId}. This factory
Sovietaced8fe84f32015-07-22 22:17:31 -0400104 * method assumes that the first two bytes of the {@link DatapathId} are 0 bytes.
Sovietaced5b75b7c2015-07-21 18:39:53 -0400105 * @param dpid the {@link DatapathId} to create the {@link MacAddress} from
106 * @return a {@link MacAddress} derived from the supplied {@link DatapathId}
107 */
108 public static MacAddress of(@Nonnull DatapathId dpid) {
109 Preconditions.checkNotNull(dpid, "dpid must not be null");
110
Sovietaced729bc172015-07-23 21:06:04 -0400111 long raw = dpid.getLong();
Sovietaced8fe84f32015-07-22 22:17:31 -0400112
Sovietaced729bc172015-07-23 21:06:04 -0400113 // Mask out valid bytes
114 if( (raw & ~BROADCAST_VAL) != 0L) {
Sovietaced8fe84f32015-07-22 22:17:31 -0400115 throw new IllegalArgumentException("First two bytes of supplied "
Sovietaced729bc172015-07-23 21:06:04 -0400116 + "Datapathid must be 0");
Sovietaced8fe84f32015-07-22 22:17:31 -0400117 }
Sovietaced729bc172015-07-23 21:06:04 -0400118 return of(raw);
Sovietaced5b75b7c2015-07-21 18:39:53 -0400119 }
120
Andreas Wundsam4e2469e2014-02-17 15:32:43 -0800121 private volatile byte[] bytesCache = null;
Yotam Harcholf3f11152013-09-05 16:47:16 -0700122
123 public byte[] getBytes() {
124 if (bytesCache == null) {
125 synchronized (this) {
126 if (bytesCache == null) {
127 bytesCache =
128 new byte[] { (byte) ((rawValue >> 40) & 0xFF),
129 (byte) ((rawValue >> 32) & 0xFF),
130 (byte) ((rawValue >> 24) & 0xFF),
131 (byte) ((rawValue >> 16) & 0xFF),
132 (byte) ((rawValue >> 8) & 0xFF),
133 (byte) ((rawValue >> 0) & 0xFF) };
134 }
135 }
136 }
Andreas Wundsam5f71b412014-02-18 12:56:35 -0800137 return Arrays.copyOf(bytesCache, bytesCache.length);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700138 }
139
Rob Vaterlaus83f40ab2013-10-14 15:44:46 -0700140 /**
141 * Returns {@code true} if the MAC address is the broadcast address.
142 * @return {@code true} if the MAC address is the broadcast address.
143 */
144 public boolean isBroadcast() {
145 return this == BROADCAST;
146 }
147
148 /**
149 * Returns {@code true} if the MAC address is a multicast address.
150 * @return {@code true} if the MAC address is a multicast address.
151 */
152 public boolean isMulticast() {
153 if (isBroadcast()) {
154 return false;
155 }
156 return (rawValue & (0x01L << 40)) != 0;
157 }
158
Shudong Zhoue5a36802014-02-09 14:54:14 -0800159 /**
Shudong Zhoua4fb4c62014-02-10 16:57:45 -0800160 * Returns {@code true} if the MAC address is an LLDP mac address.
161 * @return {@code true} if the MAC address is an LLDP mac address.
Shudong Zhoue5a36802014-02-09 14:54:14 -0800162 */
Shudong Zhoua4fb4c62014-02-10 16:57:45 -0800163 public boolean isLLDPAddress() {
164 return (rawValue & LLDP_MAC_ADDRESS_MASK) == LLDP_MAC_ADDRESS_VALUE;
Shudong Zhoue5a36802014-02-09 14:54:14 -0800165 }
166
Yotam Harcholf3f11152013-09-05 16:47:16 -0700167 @Override
168 public int getLength() {
169 return MacAddrLen;
170 }
171
172 @Override
173 public String toString() {
174 return HexString.toHexString(rawValue, 6);
175 }
176
177 @Override
178 public int hashCode() {
179 final int prime = 31;
180 int result = 1;
181 result = prime * result + (int) (rawValue ^ (rawValue >>> 32));
182 return result;
183 }
184
185 @Override
186 public boolean equals(final Object obj) {
187 if (this == obj)
188 return true;
189 if (obj == null)
190 return false;
191 if (getClass() != obj.getClass())
192 return false;
193 MacAddress other = (MacAddress) obj;
194 if (rawValue != other.rawValue)
195 return false;
196 return true;
197 }
198
199 public long getLong() {
200 return rawValue;
201 }
202
203 public void write6Bytes(ChannelBuffer c) {
204 c.writeInt((int) (this.rawValue >> 16));
205 c.writeShort((int) this.rawValue & 0xFFFF);
206 }
207
208 public static MacAddress read6Bytes(ChannelBuffer c) throws OFParseError {
209 long raw = c.readUnsignedInt() << 16 | c.readUnsignedShort();
210 return MacAddress.of(raw);
211 }
212
213 @Override
214 public MacAddress applyMask(MacAddress mask) {
215 return MacAddress.of(this.rawValue & mask.rawValue);
216 }
217
Andreas Wundsam85c961f2013-09-29 21:22:12 -0700218 @Override
219 public int compareTo(MacAddress o) {
220 return Longs.compare(rawValue, o.rawValue);
221 }
222
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700223 @Override
224 public void putTo(PrimitiveSink sink) {
225 sink.putInt((int) (this.rawValue >> 16));
226 sink.putShort((short) (this.rawValue & 0xFFFF));
227 }
228
Yotam Harcholf3f11152013-09-05 16:47:16 -0700229
230
231}