blob: 3db7e3ae88c238abf06ca7972f8ef474f70935c0 [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
111 byte[] dpidBytes = dpid.getBytes();
Sovietaced8fe84f32015-07-22 22:17:31 -0400112
113 if (dpidBytes[0] == 0
114 && dpidBytes[1] == 0) {
115 return MacAddress.of(dpid.getLong());
116 } else {
117 throw new IllegalArgumentException("First two bytes of supplied "
118 + "Datapathid must be 0");
119 }
Sovietaced5b75b7c2015-07-21 18:39:53 -0400120 }
121
Andreas Wundsam4e2469e2014-02-17 15:32:43 -0800122 private volatile byte[] bytesCache = null;
Yotam Harcholf3f11152013-09-05 16:47:16 -0700123
124 public byte[] getBytes() {
125 if (bytesCache == null) {
126 synchronized (this) {
127 if (bytesCache == null) {
128 bytesCache =
129 new byte[] { (byte) ((rawValue >> 40) & 0xFF),
130 (byte) ((rawValue >> 32) & 0xFF),
131 (byte) ((rawValue >> 24) & 0xFF),
132 (byte) ((rawValue >> 16) & 0xFF),
133 (byte) ((rawValue >> 8) & 0xFF),
134 (byte) ((rawValue >> 0) & 0xFF) };
135 }
136 }
137 }
Andreas Wundsam5f71b412014-02-18 12:56:35 -0800138 return Arrays.copyOf(bytesCache, bytesCache.length);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700139 }
140
Rob Vaterlaus83f40ab2013-10-14 15:44:46 -0700141 /**
142 * Returns {@code true} if the MAC address is the broadcast address.
143 * @return {@code true} if the MAC address is the broadcast address.
144 */
145 public boolean isBroadcast() {
146 return this == BROADCAST;
147 }
148
149 /**
150 * Returns {@code true} if the MAC address is a multicast address.
151 * @return {@code true} if the MAC address is a multicast address.
152 */
153 public boolean isMulticast() {
154 if (isBroadcast()) {
155 return false;
156 }
157 return (rawValue & (0x01L << 40)) != 0;
158 }
159
Shudong Zhoue5a36802014-02-09 14:54:14 -0800160 /**
Shudong Zhoua4fb4c62014-02-10 16:57:45 -0800161 * Returns {@code true} if the MAC address is an LLDP mac address.
162 * @return {@code true} if the MAC address is an LLDP mac address.
Shudong Zhoue5a36802014-02-09 14:54:14 -0800163 */
Shudong Zhoua4fb4c62014-02-10 16:57:45 -0800164 public boolean isLLDPAddress() {
165 return (rawValue & LLDP_MAC_ADDRESS_MASK) == LLDP_MAC_ADDRESS_VALUE;
Shudong Zhoue5a36802014-02-09 14:54:14 -0800166 }
167
Yotam Harcholf3f11152013-09-05 16:47:16 -0700168 @Override
169 public int getLength() {
170 return MacAddrLen;
171 }
172
173 @Override
174 public String toString() {
175 return HexString.toHexString(rawValue, 6);
176 }
177
178 @Override
179 public int hashCode() {
180 final int prime = 31;
181 int result = 1;
182 result = prime * result + (int) (rawValue ^ (rawValue >>> 32));
183 return result;
184 }
185
186 @Override
187 public boolean equals(final Object obj) {
188 if (this == obj)
189 return true;
190 if (obj == null)
191 return false;
192 if (getClass() != obj.getClass())
193 return false;
194 MacAddress other = (MacAddress) obj;
195 if (rawValue != other.rawValue)
196 return false;
197 return true;
198 }
199
200 public long getLong() {
201 return rawValue;
202 }
203
204 public void write6Bytes(ChannelBuffer c) {
205 c.writeInt((int) (this.rawValue >> 16));
206 c.writeShort((int) this.rawValue & 0xFFFF);
207 }
208
209 public static MacAddress read6Bytes(ChannelBuffer c) throws OFParseError {
210 long raw = c.readUnsignedInt() << 16 | c.readUnsignedShort();
211 return MacAddress.of(raw);
212 }
213
214 @Override
215 public MacAddress applyMask(MacAddress mask) {
216 return MacAddress.of(this.rawValue & mask.rawValue);
217 }
218
Andreas Wundsam85c961f2013-09-29 21:22:12 -0700219 @Override
220 public int compareTo(MacAddress o) {
221 return Longs.compare(rawValue, o.rawValue);
222 }
223
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700224 @Override
225 public void putTo(PrimitiveSink sink) {
226 sink.putInt((int) (this.rawValue >> 16));
227 sink.putShort((short) (this.rawValue & 0xFFFF));
228 }
229
Yotam Harcholf3f11152013-09-05 16:47:16 -0700230
231
232}