blob: b026b6c256d39f51d8ffb771cdb91ae58d12570b [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;
12import com.google.common.base.Strings;
Andreas Wundsam22ba3af2013-10-04 16:00:30 -070013import com.google.common.hash.PrimitiveSink;
Andreas Wundsam85c961f2013-09-29 21:22:12 -070014import com.google.common.primitives.Longs;
15
Yotam Harcholf3f11152013-09-05 16:47:16 -070016/**
17 * Wrapper around a 6 byte mac address.
18 *
kjwon157bc85402015-02-12 15:07:42 +090019 * @author Andreas Wundsam {@literal <}andreas.wundsam@bigswitch.com{@literal >}
Yotam Harcholf3f11152013-09-05 16:47:16 -070020 */
21
22public class MacAddress implements OFValueType<MacAddress> {
23 static final int MacAddrLen = 6;
24 private final long rawValue;
25
Andreas Wundsamb75c4ad2013-09-23 14:45:35 -070026 private final static long NONE_VAL = 0x0L;
27 public static final MacAddress NONE = new MacAddress(NONE_VAL);
28
Rob Vaterlaus83f40ab2013-10-14 15:44:46 -070029 private final static long BROADCAST_VAL = 0x0000FFFFFFFFFFFFL;
30 public static final MacAddress BROADCAST = new MacAddress(BROADCAST_VAL);
31
Yotam Harcholf3f11152013-09-05 16:47:16 -070032 public static final MacAddress NO_MASK = MacAddress.of(0xFFFFFFFFFFFFFFFFl);
33 public static final MacAddress FULL_MASK = MacAddress.of(0x0);
34
Shudong Zhoua4fb4c62014-02-10 16:57:45 -080035 private static final long LLDP_MAC_ADDRESS_MASK = 0xfffffffffff0L;
36 private static final long LLDP_MAC_ADDRESS_VALUE = 0x0180c2000000L;
Shudong Zhoue5a36802014-02-09 14:54:14 -080037
Sovietaced5b75b7c2015-07-21 18:39:53 -040038 private static final String FORMAT_ERROR = "Mac address is not well-formed. " +
39 "It must consist of 6 hex digit pairs separated by colons: ";
40 private static final int STRING_LENGTH = 6 * 2 + 5;
41
Yotam Harcholf3f11152013-09-05 16:47:16 -070042 private MacAddress(final long rawValue) {
43 this.rawValue = rawValue;
44 }
45
46 public static MacAddress of(final byte[] address) {
Rob Vaterlaus83f40ab2013-10-14 15:44:46 -070047 if (address.length != MacAddrLen)
48 throw new IllegalArgumentException(
49 "Mac address byte array must be exactly 6 bytes long; length = " + address.length);
Yotam Harcholf3f11152013-09-05 16:47:16 -070050 long raw =
51 (address[0] & 0xFFL) << 40 | (address[1] & 0xFFL) << 32
52 | (address[2] & 0xFFL) << 24 | (address[3] & 0xFFL) << 16
53 | (address[4] & 0xFFL) << 8 | (address[5] & 0xFFL);
54 return MacAddress.of(raw);
55 }
56
Rob Vaterlaus83f40ab2013-10-14 15:44:46 -070057 public static MacAddress of(long raw) {
58 raw &= BROADCAST_VAL;
Andreas Wundsamb75c4ad2013-09-23 14:45:35 -070059 if(raw == NONE_VAL)
60 return NONE;
Rob Vaterlaus83f40ab2013-10-14 15:44:46 -070061 if (raw == BROADCAST_VAL)
62 return BROADCAST;
Yotam Harcholf3f11152013-09-05 16:47:16 -070063 return new MacAddress(raw);
64 }
65
Andreas Wundsam3700d162014-03-11 04:43:38 -070066 /** Parse a mac adress from the canonical string representation as
67 * 6 hex bytes separated by colons (01:02:03:04:05:06).
68 *
69 * @param macString - a mac address in canonical string representation
70 * @return the parsed MacAddress
71 * @throws IllegalArgumentException if macString is not a valid mac adddress
72 */
73 @Nonnull
74 public static MacAddress of(@Nonnull final String macString) throws IllegalArgumentException {
Sovietaced5b75b7c2015-07-21 18:39:53 -040075 Preconditions.checkArgument(!Strings.isNullOrEmpty(macString),
76 "macString must not be null or empty");
77 Preconditions.checkArgument(macString.length() == (STRING_LENGTH),
78 FORMAT_ERROR + macString);
79
Yotam Harcholf3f11152013-09-05 16:47:16 -070080 int index = 0;
81 int shift = 40;
Yotam Harcholf3f11152013-09-05 16:47:16 -070082 long raw = 0;
Yotam Harcholf3f11152013-09-05 16:47:16 -070083
84 while (shift >= 0) {
Andreas Wundsam3700d162014-03-11 04:43:38 -070085 int digit1 = Character.digit(macString.charAt(index++), 16);
86 int digit2 = Character.digit(macString.charAt(index++), 16);
Rob Vaterlaus83f40ab2013-10-14 15:44:46 -070087 if ((digit1 < 0) || (digit2 < 0))
Andreas Wundsam3700d162014-03-11 04:43:38 -070088 throw new IllegalArgumentException(FORMAT_ERROR + macString);
Rob Vaterlaus83f40ab2013-10-14 15:44:46 -070089 raw |= ((long) (digit1 << 4 | digit2)) << shift;
Yotam Harcholf3f11152013-09-05 16:47:16 -070090
91 if (shift == 0)
92 break;
Andreas Wundsam3700d162014-03-11 04:43:38 -070093 if (macString.charAt(index++) != ':')
94 throw new IllegalArgumentException(FORMAT_ERROR + macString);
Yotam Harcholf3f11152013-09-05 16:47:16 -070095 shift -= 8;
96 }
97 return MacAddress.of(raw);
98 }
99
Sovietaced5b75b7c2015-07-21 18:39:53 -0400100 /**
101 * Creates a {@link MacAddress} from a {@link DatapathId}. This factory
102 * method assumes that the {@link MacAddress} is composed of the last six
103 * bytes of the supplied {@link DatapathId}.
104 * @param dpid the {@link DatapathId} to create the {@link MacAddress} from
105 * @return a {@link MacAddress} derived from the supplied {@link DatapathId}
106 */
107 public static MacAddress of(@Nonnull DatapathId dpid) {
108 Preconditions.checkNotNull(dpid, "dpid must not be null");
109
110 byte[] dpidBytes = dpid.getBytes();
111 byte[] macBytes = Arrays.copyOfRange(dpidBytes, 2, 8);
112 return MacAddress.of(macBytes);
113 }
114
Andreas Wundsam4e2469e2014-02-17 15:32:43 -0800115 private volatile byte[] bytesCache = null;
Yotam Harcholf3f11152013-09-05 16:47:16 -0700116
117 public byte[] getBytes() {
118 if (bytesCache == null) {
119 synchronized (this) {
120 if (bytesCache == null) {
121 bytesCache =
122 new byte[] { (byte) ((rawValue >> 40) & 0xFF),
123 (byte) ((rawValue >> 32) & 0xFF),
124 (byte) ((rawValue >> 24) & 0xFF),
125 (byte) ((rawValue >> 16) & 0xFF),
126 (byte) ((rawValue >> 8) & 0xFF),
127 (byte) ((rawValue >> 0) & 0xFF) };
128 }
129 }
130 }
Andreas Wundsam5f71b412014-02-18 12:56:35 -0800131 return Arrays.copyOf(bytesCache, bytesCache.length);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700132 }
133
Rob Vaterlaus83f40ab2013-10-14 15:44:46 -0700134 /**
135 * Returns {@code true} if the MAC address is the broadcast address.
136 * @return {@code true} if the MAC address is the broadcast address.
137 */
138 public boolean isBroadcast() {
139 return this == BROADCAST;
140 }
141
142 /**
143 * Returns {@code true} if the MAC address is a multicast address.
144 * @return {@code true} if the MAC address is a multicast address.
145 */
146 public boolean isMulticast() {
147 if (isBroadcast()) {
148 return false;
149 }
150 return (rawValue & (0x01L << 40)) != 0;
151 }
152
Shudong Zhoue5a36802014-02-09 14:54:14 -0800153 /**
Shudong Zhoua4fb4c62014-02-10 16:57:45 -0800154 * Returns {@code true} if the MAC address is an LLDP mac address.
155 * @return {@code true} if the MAC address is an LLDP mac address.
Shudong Zhoue5a36802014-02-09 14:54:14 -0800156 */
Shudong Zhoua4fb4c62014-02-10 16:57:45 -0800157 public boolean isLLDPAddress() {
158 return (rawValue & LLDP_MAC_ADDRESS_MASK) == LLDP_MAC_ADDRESS_VALUE;
Shudong Zhoue5a36802014-02-09 14:54:14 -0800159 }
160
Yotam Harcholf3f11152013-09-05 16:47:16 -0700161 @Override
162 public int getLength() {
163 return MacAddrLen;
164 }
165
166 @Override
167 public String toString() {
168 return HexString.toHexString(rawValue, 6);
169 }
170
171 @Override
172 public int hashCode() {
173 final int prime = 31;
174 int result = 1;
175 result = prime * result + (int) (rawValue ^ (rawValue >>> 32));
176 return result;
177 }
178
179 @Override
180 public boolean equals(final Object obj) {
181 if (this == obj)
182 return true;
183 if (obj == null)
184 return false;
185 if (getClass() != obj.getClass())
186 return false;
187 MacAddress other = (MacAddress) obj;
188 if (rawValue != other.rawValue)
189 return false;
190 return true;
191 }
192
193 public long getLong() {
194 return rawValue;
195 }
196
197 public void write6Bytes(ChannelBuffer c) {
198 c.writeInt((int) (this.rawValue >> 16));
199 c.writeShort((int) this.rawValue & 0xFFFF);
200 }
201
202 public static MacAddress read6Bytes(ChannelBuffer c) throws OFParseError {
203 long raw = c.readUnsignedInt() << 16 | c.readUnsignedShort();
204 return MacAddress.of(raw);
205 }
206
207 @Override
208 public MacAddress applyMask(MacAddress mask) {
209 return MacAddress.of(this.rawValue & mask.rawValue);
210 }
211
Andreas Wundsam85c961f2013-09-29 21:22:12 -0700212 @Override
213 public int compareTo(MacAddress o) {
214 return Longs.compare(rawValue, o.rawValue);
215 }
216
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700217 @Override
218 public void putTo(PrimitiveSink sink) {
219 sink.putInt((int) (this.rawValue >> 16));
220 sink.putShort((short) (this.rawValue & 0xFFFF));
221 }
222
Yotam Harcholf3f11152013-09-05 16:47:16 -0700223
224
225}