blob: b2a01b9d58942c608e2cfdbddf12aacc26acab4c [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 Open Networking Laboratory
alshabibc4901cd2014-09-05 16:50:40 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
alshabibc4901cd2014-09-05 16:50:40 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
Thomas Vachuska24c849c2014-10-27 09:53:05 -070015 */
16
17
alshabibc4901cd2014-09-05 16:50:40 -070018
19package org.onlab.packet;
20
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -080021import static com.google.common.base.Preconditions.checkNotNull;
22
alshabibc4901cd2014-09-05 16:50:40 -070023import java.nio.ByteBuffer;
24import java.util.Arrays;
25import java.util.HashMap;
26import java.util.Map;
27
28
29/**
30 *
alshabibc4901cd2014-09-05 16:50:40 -070031 */
32public class Ethernet extends BasePacket {
33 private static final String HEXES = "0123456789ABCDEF";
Thomas Vachuska5dd52f72014-11-28 19:27:45 -080034 public static final short TYPE_ARP = (short) 0x0806;
alshabibc4901cd2014-09-05 16:50:40 -070035 public static final short TYPE_RARP = (short) 0x8035;
Thomas Vachuska5dd52f72014-11-28 19:27:45 -080036 public static final short TYPE_IPV4 = (short) 0x0800;
37 public static final short TYPE_IPV6 = (short) 0x86dd;
alshabibc4901cd2014-09-05 16:50:40 -070038 public static final short TYPE_LLDP = (short) 0x88cc;
alshabib10580802015-02-18 18:30:33 -080039 public static final short TYPE_VLAN = (short) 0x8100;
alshabibc4901cd2014-09-05 16:50:40 -070040 public static final short TYPE_BSN = (short) 0x8942;
41 public static final short VLAN_UNTAGGED = (short) 0xffff;
Praseed Balakrishnan8c67d172014-11-10 10:15:41 -080042 public static final short MPLS_UNICAST = (short) 0x8847;
43 public static final short MPLS_MULTICAST = (short) 0x8848;
Thomas Vachuska5dd52f72014-11-28 19:27:45 -080044
alshabibc4901cd2014-09-05 16:50:40 -070045 public static final short DATALAYER_ADDRESS_LENGTH = 6; // bytes
Ray Milkey241b96a2014-11-17 13:08:20 -080046 public static final Map<Short, Class<? extends IPacket>> ETHER_TYPE_CLASS_MAP =
47 new HashMap<>();
alshabibc4901cd2014-09-05 16:50:40 -070048
49 static {
Ray Milkey241b96a2014-11-17 13:08:20 -080050 Ethernet.ETHER_TYPE_CLASS_MAP.put(Ethernet.TYPE_ARP, ARP.class);
51 Ethernet.ETHER_TYPE_CLASS_MAP.put(Ethernet.TYPE_RARP, ARP.class);
52 Ethernet.ETHER_TYPE_CLASS_MAP.put(Ethernet.TYPE_IPV4, IPv4.class);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080053 Ethernet.ETHER_TYPE_CLASS_MAP.put(Ethernet.TYPE_IPV6, IPv6.class);
Ray Milkey241b96a2014-11-17 13:08:20 -080054 Ethernet.ETHER_TYPE_CLASS_MAP.put(Ethernet.TYPE_LLDP, LLDP.class);
55 Ethernet.ETHER_TYPE_CLASS_MAP.put(Ethernet.TYPE_BSN, LLDP.class);
alshabibc4901cd2014-09-05 16:50:40 -070056 }
57
Ayaka Koshibea9c199f2014-09-16 16:21:40 -070058 protected MacAddress destinationMACAddress;
59 protected MacAddress sourceMACAddress;
alshabibc4901cd2014-09-05 16:50:40 -070060 protected byte priorityCode;
61 protected short vlanID;
62 protected short etherType;
63 protected boolean pad = false;
64
65 /**
66 * By default, set Ethernet to untagged.
67 */
68 public Ethernet() {
69 super();
70 this.vlanID = Ethernet.VLAN_UNTAGGED;
71 }
72
73 /**
74 * Gets the destination MAC address.
75 *
76 * @return the destination MAC as a byte array
77 */
78 public byte[] getDestinationMACAddress() {
79 return this.destinationMACAddress.toBytes();
80 }
81
82 /**
83 * Gets the destination MAC address.
84 *
85 * @return the destination MAC
86 */
Ayaka Koshibea9c199f2014-09-16 16:21:40 -070087 public MacAddress getDestinationMAC() {
alshabibc4901cd2014-09-05 16:50:40 -070088 return this.destinationMACAddress;
89 }
90
91 /**
92 * Sets the destination MAC address.
93 *
tom5f18cf32014-09-13 14:10:57 -070094 * @param destMac the destination MAC to set
alshabibc4901cd2014-09-05 16:50:40 -070095 * @return the Ethernet frame
96 */
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -080097 public Ethernet setDestinationMACAddress(final MacAddress destMac) {
98 this.destinationMACAddress = checkNotNull(destMac);
99 return this;
100 }
101
102 /**
103 * Sets the destination MAC address.
104 *
105 * @param destMac the destination MAC to set
106 * @return the Ethernet frame
107 */
alshabibc4901cd2014-09-05 16:50:40 -0700108 public Ethernet setDestinationMACAddress(final byte[] destMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700109 this.destinationMACAddress = MacAddress.valueOf(destMac);
alshabibc4901cd2014-09-05 16:50:40 -0700110 return this;
111 }
112
113 /**
114 * Sets the destination MAC address.
115 *
tom5f18cf32014-09-13 14:10:57 -0700116 * @param destMac the destination MAC to set
alshabibc4901cd2014-09-05 16:50:40 -0700117 * @return the Ethernet frame
118 */
119 public Ethernet setDestinationMACAddress(final String destMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700120 this.destinationMACAddress = MacAddress.valueOf(destMac);
alshabibc4901cd2014-09-05 16:50:40 -0700121 return this;
122 }
123
124 /**
125 * Gets the source MAC address.
126 *
127 * @return the source MACAddress as a byte array
128 */
129 public byte[] getSourceMACAddress() {
130 return this.sourceMACAddress.toBytes();
131 }
132
133 /**
134 * Gets the source MAC address.
135 *
136 * @return the source MACAddress
137 */
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700138 public MacAddress getSourceMAC() {
alshabibc4901cd2014-09-05 16:50:40 -0700139 return this.sourceMACAddress;
140 }
141
142 /**
143 * Sets the source MAC address.
144 *
tom5f18cf32014-09-13 14:10:57 -0700145 * @param sourceMac the source MAC to set
alshabibc4901cd2014-09-05 16:50:40 -0700146 * @return the Ethernet frame
147 */
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800148 public Ethernet setSourceMACAddress(final MacAddress sourceMac) {
149 this.sourceMACAddress = checkNotNull(sourceMac);
150 return this;
151 }
152
153 /**
154 * Sets the source MAC address.
155 *
156 * @param sourceMac the source MAC to set
157 * @return the Ethernet frame
158 */
alshabibc4901cd2014-09-05 16:50:40 -0700159 public Ethernet setSourceMACAddress(final byte[] sourceMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700160 this.sourceMACAddress = MacAddress.valueOf(sourceMac);
alshabibc4901cd2014-09-05 16:50:40 -0700161 return this;
162 }
163
164 /**
165 * Sets the source MAC address.
166 *
tom5f18cf32014-09-13 14:10:57 -0700167 * @param sourceMac the source MAC to set
alshabibc4901cd2014-09-05 16:50:40 -0700168 * @return the Ethernet frame
169 */
170 public Ethernet setSourceMACAddress(final String sourceMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700171 this.sourceMACAddress = MacAddress.valueOf(sourceMac);
alshabibc4901cd2014-09-05 16:50:40 -0700172 return this;
173 }
174
175 /**
176 * Gets the priority code.
177 *
178 * @return the priorityCode
179 */
180 public byte getPriorityCode() {
181 return this.priorityCode;
182 }
183
184 /**
185 * Sets the priority code.
186 *
tom5f18cf32014-09-13 14:10:57 -0700187 * @param priority the priorityCode to set
alshabibc4901cd2014-09-05 16:50:40 -0700188 * @return the Ethernet frame
189 */
190 public Ethernet setPriorityCode(final byte priority) {
191 this.priorityCode = priority;
192 return this;
193 }
194
195 /**
196 * Gets the VLAN ID.
197 *
198 * @return the vlanID
199 */
200 public short getVlanID() {
201 return this.vlanID;
202 }
203
204 /**
205 * Sets the VLAN ID.
206 *
tom5f18cf32014-09-13 14:10:57 -0700207 * @param vlan the vlanID to set
alshabibc4901cd2014-09-05 16:50:40 -0700208 * @return the Ethernet frame
209 */
210 public Ethernet setVlanID(final short vlan) {
211 this.vlanID = vlan;
212 return this;
213 }
214
215 /**
216 * Gets the Ethernet type.
217 *
218 * @return the etherType
219 */
220 public short getEtherType() {
221 return this.etherType;
222 }
223
224 /**
225 * Sets the Ethernet type.
226 *
tom5f18cf32014-09-13 14:10:57 -0700227 * @param ethType the etherType to set
alshabibc4901cd2014-09-05 16:50:40 -0700228 * @return the Ethernet frame
229 */
230 public Ethernet setEtherType(final short ethType) {
231 this.etherType = ethType;
232 return this;
233 }
234
235 /**
236 * @return True if the Ethernet frame is broadcast, false otherwise
237 */
238 public boolean isBroadcast() {
239 assert this.destinationMACAddress.length() == 6;
240 return this.destinationMACAddress.isBroadcast();
241 }
242
243 /**
244 * @return True is the Ethernet frame is multicast, False otherwise
245 */
246 public boolean isMulticast() {
247 return this.destinationMACAddress.isMulticast();
248 }
249
250 /**
251 * Pad this packet to 60 bytes minimum, filling with zeros?
252 *
253 * @return the pad
254 */
255 public boolean isPad() {
256 return this.pad;
257 }
258
259 /**
260 * Pad this packet to 60 bytes minimum, filling with zeros?
261 *
tom5f18cf32014-09-13 14:10:57 -0700262 * @param pd
alshabibc4901cd2014-09-05 16:50:40 -0700263 * the pad to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800264 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700265 */
266 public Ethernet setPad(final boolean pd) {
267 this.pad = pd;
268 return this;
269 }
270
271 @Override
272 public byte[] serialize() {
273 byte[] payloadData = null;
274 if (this.payload != null) {
275 this.payload.setParent(this);
276 payloadData = this.payload.serialize();
277 }
278 int length = 14 + (this.vlanID == Ethernet.VLAN_UNTAGGED ? 0 : 4)
279 + (payloadData == null ? 0 : payloadData.length);
280 if (this.pad && length < 60) {
281 length = 60;
282 }
283 final byte[] data = new byte[length];
284 final ByteBuffer bb = ByteBuffer.wrap(data);
285 bb.put(this.destinationMACAddress.toBytes());
286 bb.put(this.sourceMACAddress.toBytes());
287 if (this.vlanID != Ethernet.VLAN_UNTAGGED) {
alshabib10580802015-02-18 18:30:33 -0800288 bb.putShort(TYPE_VLAN);
alshabibc4901cd2014-09-05 16:50:40 -0700289 bb.putShort((short) (this.priorityCode << 13 | this.vlanID & 0x0fff));
290 }
291 bb.putShort(this.etherType);
292 if (payloadData != null) {
293 bb.put(payloadData);
294 }
295 if (this.pad) {
296 Arrays.fill(data, bb.position(), data.length, (byte) 0x0);
297 }
298 return data;
299 }
300
301 @Override
302 public IPacket deserialize(final byte[] data, final int offset,
303 final int length) {
304 if (length <= 0) {
305 return null;
306 }
307 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
308 if (this.destinationMACAddress == null) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700309 this.destinationMACAddress = MacAddress.valueOf(new byte[6]);
alshabibc4901cd2014-09-05 16:50:40 -0700310 }
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700311 final byte[] dstAddr = new byte[MacAddress.MAC_ADDRESS_LENGTH];
alshabibc4901cd2014-09-05 16:50:40 -0700312 bb.get(dstAddr);
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700313 this.destinationMACAddress = MacAddress.valueOf(dstAddr);
alshabibc4901cd2014-09-05 16:50:40 -0700314
315 if (this.sourceMACAddress == null) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700316 this.sourceMACAddress = MacAddress.valueOf(new byte[6]);
alshabibc4901cd2014-09-05 16:50:40 -0700317 }
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700318 final byte[] srcAddr = new byte[MacAddress.MAC_ADDRESS_LENGTH];
alshabibc4901cd2014-09-05 16:50:40 -0700319 bb.get(srcAddr);
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700320 this.sourceMACAddress = MacAddress.valueOf(srcAddr);
alshabibc4901cd2014-09-05 16:50:40 -0700321
322 short ethType = bb.getShort();
alshabib10580802015-02-18 18:30:33 -0800323 if (ethType == TYPE_VLAN) {
alshabibc4901cd2014-09-05 16:50:40 -0700324 final short tci = bb.getShort();
325 this.priorityCode = (byte) (tci >> 13 & 0x07);
326 this.vlanID = (short) (tci & 0x0fff);
327 ethType = bb.getShort();
328 } else {
329 this.vlanID = Ethernet.VLAN_UNTAGGED;
330 }
331 this.etherType = ethType;
332
333 IPacket payload;
Ray Milkey241b96a2014-11-17 13:08:20 -0800334 if (Ethernet.ETHER_TYPE_CLASS_MAP.containsKey(this.etherType)) {
335 final Class<? extends IPacket> clazz = Ethernet.ETHER_TYPE_CLASS_MAP
alshabibc4901cd2014-09-05 16:50:40 -0700336 .get(this.etherType);
337 try {
338 payload = clazz.newInstance();
339 } catch (final Exception e) {
340 throw new RuntimeException(
341 "Error parsing payload for Ethernet packet", e);
342 }
343 } else {
344 payload = new Data();
345 }
346 this.payload = payload.deserialize(data, bb.position(),
347 bb.limit() - bb.position());
348 this.payload.setParent(this);
349 return this;
350 }
351
352 /**
353 * Checks to see if a string is a valid MAC address.
354 *
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800355 * @param macAddress string to test if it is a valid MAC
alshabibc4901cd2014-09-05 16:50:40 -0700356 * @return True if macAddress is a valid MAC, False otherwise
357 */
358 public static boolean isMACAddress(final String macAddress) {
359 final String[] macBytes = macAddress.split(":");
360 if (macBytes.length != 6) {
361 return false;
362 }
363 for (int i = 0; i < 6; ++i) {
364 if (Ethernet.HEXES.indexOf(macBytes[i].toUpperCase().charAt(0)) == -1
365 || Ethernet.HEXES.indexOf(macBytes[i].toUpperCase().charAt(
366 1)) == -1) {
367 return false;
368 }
369 }
370 return true;
371 }
372
373 /**
374 * Accepts a MAC address of the form 00:aa:11:bb:22:cc, case does not
375 * matter, and returns a corresponding byte[].
376 *
377 * @param macAddress
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800378 * The MAC address to convert into a byte array
alshabibc4901cd2014-09-05 16:50:40 -0700379 * @return The macAddress as a byte array
380 */
381 public static byte[] toMACAddress(final String macAddress) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700382 return MacAddress.valueOf(macAddress).toBytes();
alshabibc4901cd2014-09-05 16:50:40 -0700383 }
384
385 /**
386 * Accepts a MAC address and returns the corresponding long, where the MAC
387 * bytes are set on the lower order bytes of the long.
388 *
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800389 * @param macAddress MAC address as a byte array
alshabibc4901cd2014-09-05 16:50:40 -0700390 * @return a long containing the mac address bytes
391 */
392 public static long toLong(final byte[] macAddress) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700393 return MacAddress.valueOf(macAddress).toLong();
alshabibc4901cd2014-09-05 16:50:40 -0700394 }
395
396 /**
397 * Converts a long MAC address to a byte array.
398 *
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800399 * @param macAddress MAC address set on the lower order bytes of the long
alshabibc4901cd2014-09-05 16:50:40 -0700400 * @return the bytes of the mac address
401 */
402 public static byte[] toByteArray(final long macAddress) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700403 return MacAddress.valueOf(macAddress).toBytes();
alshabibc4901cd2014-09-05 16:50:40 -0700404 }
405
406 /*
407 * (non-Javadoc)
408 *
409 * @see java.lang.Object#hashCode()
410 */
411 @Override
412 public int hashCode() {
413 final int prime = 7867;
414 int result = super.hashCode();
415 result = prime * result + this.destinationMACAddress.hashCode();
416 result = prime * result + this.etherType;
417 result = prime * result + this.vlanID;
418 result = prime * result + this.priorityCode;
419 result = prime * result + (this.pad ? 1231 : 1237);
420 result = prime * result + this.sourceMACAddress.hashCode();
421 return result;
422 }
423
424 /*
425 * (non-Javadoc)
426 *
427 * @see java.lang.Object#equals(java.lang.Object)
428 */
429 @Override
430 public boolean equals(final Object obj) {
431 if (this == obj) {
432 return true;
433 }
434 if (!super.equals(obj)) {
435 return false;
436 }
437 if (!(obj instanceof Ethernet)) {
438 return false;
439 }
440 final Ethernet other = (Ethernet) obj;
441 if (!this.destinationMACAddress.equals(other.destinationMACAddress)) {
442 return false;
443 }
444 if (this.priorityCode != other.priorityCode) {
445 return false;
446 }
447 if (this.vlanID != other.vlanID) {
448 return false;
449 }
450 if (this.etherType != other.etherType) {
451 return false;
452 }
453 if (this.pad != other.pad) {
454 return false;
455 }
456 if (!this.sourceMACAddress.equals(other.sourceMACAddress)) {
457 return false;
458 }
459 return true;
460 }
461
462 /*
463 * (non-Javadoc)
464 *
465 * @see java.lang.Object#toString(java.lang.Object)
466 */
467 @Override
468 public String toString() {
469
470 final StringBuffer sb = new StringBuffer("\n");
471
472 final IPacket pkt = this.getPayload();
473
474 if (pkt instanceof ARP) {
475 sb.append("arp");
476 } else if (pkt instanceof LLDP) {
477 sb.append("lldp");
478 } else if (pkt instanceof ICMP) {
479 sb.append("icmp");
480 } else if (pkt instanceof IPv4) {
481 sb.append("ip");
482 } else if (pkt instanceof DHCP) {
483 sb.append("dhcp");
484 } else {
485 sb.append(this.getEtherType());
486 }
487
488 sb.append("\ndl_vlan: ");
489 if (this.getVlanID() == Ethernet.VLAN_UNTAGGED) {
490 sb.append("untagged");
491 } else {
492 sb.append(this.getVlanID());
493 }
494 sb.append("\ndl_vlan_pcp: ");
495 sb.append(this.getPriorityCode());
496 sb.append("\ndl_src: ");
497 sb.append(bytesToHex(this.getSourceMACAddress()));
498 sb.append("\ndl_dst: ");
499 sb.append(bytesToHex(this.getDestinationMACAddress()));
500
501 if (pkt instanceof ARP) {
502 final ARP p = (ARP) pkt;
503 sb.append("\nnw_src: ");
504 sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p
505 .getSenderProtocolAddress())));
506 sb.append("\nnw_dst: ");
507 sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p
508 .getTargetProtocolAddress())));
509 } else if (pkt instanceof LLDP) {
510 sb.append("lldp packet");
511 } else if (pkt instanceof ICMP) {
512 final ICMP icmp = (ICMP) pkt;
513 sb.append("\nicmp_type: ");
514 sb.append(icmp.getIcmpType());
515 sb.append("\nicmp_code: ");
516 sb.append(icmp.getIcmpCode());
517 } else if (pkt instanceof IPv4) {
518 final IPv4 p = (IPv4) pkt;
519 sb.append("\nnw_src: ");
520 sb.append(IPv4.fromIPv4Address(p.getSourceAddress()));
521 sb.append("\nnw_dst: ");
522 sb.append(IPv4.fromIPv4Address(p.getDestinationAddress()));
523 sb.append("\nnw_tos: ");
524 sb.append(p.getDiffServ());
525 sb.append("\nnw_proto: ");
526 sb.append(p.getProtocol());
527
528 if (pkt instanceof TCP) {
529 sb.append("\ntp_src: ");
530 sb.append(((TCP) pkt).getSourcePort());
531 sb.append("\ntp_dst: ");
532 sb.append(((TCP) pkt).getDestinationPort());
533
534 } else if (pkt instanceof UDP) {
535 sb.append("\ntp_src: ");
536 sb.append(((UDP) pkt).getSourcePort());
537 sb.append("\ntp_dst: ");
538 sb.append(((UDP) pkt).getDestinationPort());
539 }
540
541 if (pkt instanceof ICMP) {
542 final ICMP icmp = (ICMP) pkt;
543 sb.append("\nicmp_type: ");
544 sb.append(icmp.getIcmpType());
545 sb.append("\nicmp_code: ");
546 sb.append(icmp.getIcmpCode());
547 }
548
549 } else if (pkt instanceof DHCP) {
550 sb.append("\ndhcp packet");
551 } else if (pkt instanceof Data) {
552 sb.append("\ndata packet");
553 } else if (pkt instanceof LLC) {
554 sb.append("\nllc packet");
555 } else {
tom5f18cf32014-09-13 14:10:57 -0700556 sb.append("\nunknown packet");
alshabibc4901cd2014-09-05 16:50:40 -0700557 }
558
559 return sb.toString();
560 }
561
562 public static String bytesToHex(byte[] in) {
563 final StringBuilder builder = new StringBuilder();
564 for (byte b : in) {
565 builder.append(String.format("%02x", b));
566 }
567 return builder.toString();
568 }
569
570}