blob: 2a69ffae09a176a369a1a5f1ee0b481af51b3928 [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;
39 public static final short TYPE_BSN = (short) 0x8942;
40 public static final short VLAN_UNTAGGED = (short) 0xffff;
Praseed Balakrishnan8c67d172014-11-10 10:15:41 -080041 public static final short MPLS_UNICAST = (short) 0x8847;
42 public static final short MPLS_MULTICAST = (short) 0x8848;
Thomas Vachuska5dd52f72014-11-28 19:27:45 -080043
alshabibc4901cd2014-09-05 16:50:40 -070044 public static final short DATALAYER_ADDRESS_LENGTH = 6; // bytes
Ray Milkey241b96a2014-11-17 13:08:20 -080045 public static final Map<Short, Class<? extends IPacket>> ETHER_TYPE_CLASS_MAP =
46 new HashMap<>();
alshabibc4901cd2014-09-05 16:50:40 -070047
48 static {
Ray Milkey241b96a2014-11-17 13:08:20 -080049 Ethernet.ETHER_TYPE_CLASS_MAP.put(Ethernet.TYPE_ARP, ARP.class);
50 Ethernet.ETHER_TYPE_CLASS_MAP.put(Ethernet.TYPE_RARP, ARP.class);
51 Ethernet.ETHER_TYPE_CLASS_MAP.put(Ethernet.TYPE_IPV4, IPv4.class);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080052 Ethernet.ETHER_TYPE_CLASS_MAP.put(Ethernet.TYPE_IPV6, IPv6.class);
Ray Milkey241b96a2014-11-17 13:08:20 -080053 Ethernet.ETHER_TYPE_CLASS_MAP.put(Ethernet.TYPE_LLDP, LLDP.class);
54 Ethernet.ETHER_TYPE_CLASS_MAP.put(Ethernet.TYPE_BSN, LLDP.class);
alshabibc4901cd2014-09-05 16:50:40 -070055 }
56
Ayaka Koshibea9c199f2014-09-16 16:21:40 -070057 protected MacAddress destinationMACAddress;
58 protected MacAddress sourceMACAddress;
alshabibc4901cd2014-09-05 16:50:40 -070059 protected byte priorityCode;
60 protected short vlanID;
61 protected short etherType;
62 protected boolean pad = false;
63
64 /**
65 * By default, set Ethernet to untagged.
66 */
67 public Ethernet() {
68 super();
69 this.vlanID = Ethernet.VLAN_UNTAGGED;
70 }
71
72 /**
73 * Gets the destination MAC address.
74 *
75 * @return the destination MAC as a byte array
76 */
77 public byte[] getDestinationMACAddress() {
78 return this.destinationMACAddress.toBytes();
79 }
80
81 /**
82 * Gets the destination MAC address.
83 *
84 * @return the destination MAC
85 */
Ayaka Koshibea9c199f2014-09-16 16:21:40 -070086 public MacAddress getDestinationMAC() {
alshabibc4901cd2014-09-05 16:50:40 -070087 return this.destinationMACAddress;
88 }
89
90 /**
91 * Sets the destination MAC address.
92 *
tom5f18cf32014-09-13 14:10:57 -070093 * @param destMac the destination MAC to set
alshabibc4901cd2014-09-05 16:50:40 -070094 * @return the Ethernet frame
95 */
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -080096 public Ethernet setDestinationMACAddress(final MacAddress destMac) {
97 this.destinationMACAddress = checkNotNull(destMac);
98 return this;
99 }
100
101 /**
102 * Sets the destination MAC address.
103 *
104 * @param destMac the destination MAC to set
105 * @return the Ethernet frame
106 */
alshabibc4901cd2014-09-05 16:50:40 -0700107 public Ethernet setDestinationMACAddress(final byte[] destMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700108 this.destinationMACAddress = MacAddress.valueOf(destMac);
alshabibc4901cd2014-09-05 16:50:40 -0700109 return this;
110 }
111
112 /**
113 * Sets the destination MAC address.
114 *
tom5f18cf32014-09-13 14:10:57 -0700115 * @param destMac the destination MAC to set
alshabibc4901cd2014-09-05 16:50:40 -0700116 * @return the Ethernet frame
117 */
118 public Ethernet setDestinationMACAddress(final String destMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700119 this.destinationMACAddress = MacAddress.valueOf(destMac);
alshabibc4901cd2014-09-05 16:50:40 -0700120 return this;
121 }
122
123 /**
124 * Gets the source MAC address.
125 *
126 * @return the source MACAddress as a byte array
127 */
128 public byte[] getSourceMACAddress() {
129 return this.sourceMACAddress.toBytes();
130 }
131
132 /**
133 * Gets the source MAC address.
134 *
135 * @return the source MACAddress
136 */
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700137 public MacAddress getSourceMAC() {
alshabibc4901cd2014-09-05 16:50:40 -0700138 return this.sourceMACAddress;
139 }
140
141 /**
142 * Sets the source MAC address.
143 *
tom5f18cf32014-09-13 14:10:57 -0700144 * @param sourceMac the source MAC to set
alshabibc4901cd2014-09-05 16:50:40 -0700145 * @return the Ethernet frame
146 */
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800147 public Ethernet setSourceMACAddress(final MacAddress sourceMac) {
148 this.sourceMACAddress = checkNotNull(sourceMac);
149 return this;
150 }
151
152 /**
153 * Sets the source MAC address.
154 *
155 * @param sourceMac the source MAC to set
156 * @return the Ethernet frame
157 */
alshabibc4901cd2014-09-05 16:50:40 -0700158 public Ethernet setSourceMACAddress(final byte[] sourceMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700159 this.sourceMACAddress = MacAddress.valueOf(sourceMac);
alshabibc4901cd2014-09-05 16:50:40 -0700160 return this;
161 }
162
163 /**
164 * Sets the source MAC address.
165 *
tom5f18cf32014-09-13 14:10:57 -0700166 * @param sourceMac the source MAC to set
alshabibc4901cd2014-09-05 16:50:40 -0700167 * @return the Ethernet frame
168 */
169 public Ethernet setSourceMACAddress(final String sourceMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700170 this.sourceMACAddress = MacAddress.valueOf(sourceMac);
alshabibc4901cd2014-09-05 16:50:40 -0700171 return this;
172 }
173
174 /**
175 * Gets the priority code.
176 *
177 * @return the priorityCode
178 */
179 public byte getPriorityCode() {
180 return this.priorityCode;
181 }
182
183 /**
184 * Sets the priority code.
185 *
tom5f18cf32014-09-13 14:10:57 -0700186 * @param priority the priorityCode to set
alshabibc4901cd2014-09-05 16:50:40 -0700187 * @return the Ethernet frame
188 */
189 public Ethernet setPriorityCode(final byte priority) {
190 this.priorityCode = priority;
191 return this;
192 }
193
194 /**
195 * Gets the VLAN ID.
196 *
197 * @return the vlanID
198 */
199 public short getVlanID() {
200 return this.vlanID;
201 }
202
203 /**
204 * Sets the VLAN ID.
205 *
tom5f18cf32014-09-13 14:10:57 -0700206 * @param vlan the vlanID to set
alshabibc4901cd2014-09-05 16:50:40 -0700207 * @return the Ethernet frame
208 */
209 public Ethernet setVlanID(final short vlan) {
210 this.vlanID = vlan;
211 return this;
212 }
213
214 /**
215 * Gets the Ethernet type.
216 *
217 * @return the etherType
218 */
219 public short getEtherType() {
220 return this.etherType;
221 }
222
223 /**
224 * Sets the Ethernet type.
225 *
tom5f18cf32014-09-13 14:10:57 -0700226 * @param ethType the etherType to set
alshabibc4901cd2014-09-05 16:50:40 -0700227 * @return the Ethernet frame
228 */
229 public Ethernet setEtherType(final short ethType) {
230 this.etherType = ethType;
231 return this;
232 }
233
234 /**
235 * @return True if the Ethernet frame is broadcast, false otherwise
236 */
237 public boolean isBroadcast() {
238 assert this.destinationMACAddress.length() == 6;
239 return this.destinationMACAddress.isBroadcast();
240 }
241
242 /**
243 * @return True is the Ethernet frame is multicast, False otherwise
244 */
245 public boolean isMulticast() {
246 return this.destinationMACAddress.isMulticast();
247 }
248
249 /**
250 * Pad this packet to 60 bytes minimum, filling with zeros?
251 *
252 * @return the pad
253 */
254 public boolean isPad() {
255 return this.pad;
256 }
257
258 /**
259 * Pad this packet to 60 bytes minimum, filling with zeros?
260 *
tom5f18cf32014-09-13 14:10:57 -0700261 * @param pd
alshabibc4901cd2014-09-05 16:50:40 -0700262 * the pad to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800263 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700264 */
265 public Ethernet setPad(final boolean pd) {
266 this.pad = pd;
267 return this;
268 }
269
270 @Override
271 public byte[] serialize() {
272 byte[] payloadData = null;
273 if (this.payload != null) {
274 this.payload.setParent(this);
275 payloadData = this.payload.serialize();
276 }
277 int length = 14 + (this.vlanID == Ethernet.VLAN_UNTAGGED ? 0 : 4)
278 + (payloadData == null ? 0 : payloadData.length);
279 if (this.pad && length < 60) {
280 length = 60;
281 }
282 final byte[] data = new byte[length];
283 final ByteBuffer bb = ByteBuffer.wrap(data);
284 bb.put(this.destinationMACAddress.toBytes());
285 bb.put(this.sourceMACAddress.toBytes());
286 if (this.vlanID != Ethernet.VLAN_UNTAGGED) {
287 bb.putShort((short) 0x8100);
288 bb.putShort((short) (this.priorityCode << 13 | this.vlanID & 0x0fff));
289 }
290 bb.putShort(this.etherType);
291 if (payloadData != null) {
292 bb.put(payloadData);
293 }
294 if (this.pad) {
295 Arrays.fill(data, bb.position(), data.length, (byte) 0x0);
296 }
297 return data;
298 }
299
300 @Override
301 public IPacket deserialize(final byte[] data, final int offset,
302 final int length) {
303 if (length <= 0) {
304 return null;
305 }
306 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
307 if (this.destinationMACAddress == null) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700308 this.destinationMACAddress = MacAddress.valueOf(new byte[6]);
alshabibc4901cd2014-09-05 16:50:40 -0700309 }
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700310 final byte[] dstAddr = new byte[MacAddress.MAC_ADDRESS_LENGTH];
alshabibc4901cd2014-09-05 16:50:40 -0700311 bb.get(dstAddr);
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700312 this.destinationMACAddress = MacAddress.valueOf(dstAddr);
alshabibc4901cd2014-09-05 16:50:40 -0700313
314 if (this.sourceMACAddress == null) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700315 this.sourceMACAddress = MacAddress.valueOf(new byte[6]);
alshabibc4901cd2014-09-05 16:50:40 -0700316 }
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700317 final byte[] srcAddr = new byte[MacAddress.MAC_ADDRESS_LENGTH];
alshabibc4901cd2014-09-05 16:50:40 -0700318 bb.get(srcAddr);
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700319 this.sourceMACAddress = MacAddress.valueOf(srcAddr);
alshabibc4901cd2014-09-05 16:50:40 -0700320
321 short ethType = bb.getShort();
322 if (ethType == (short) 0x8100) {
323 final short tci = bb.getShort();
324 this.priorityCode = (byte) (tci >> 13 & 0x07);
325 this.vlanID = (short) (tci & 0x0fff);
326 ethType = bb.getShort();
327 } else {
328 this.vlanID = Ethernet.VLAN_UNTAGGED;
329 }
330 this.etherType = ethType;
331
332 IPacket payload;
Ray Milkey241b96a2014-11-17 13:08:20 -0800333 if (Ethernet.ETHER_TYPE_CLASS_MAP.containsKey(this.etherType)) {
334 final Class<? extends IPacket> clazz = Ethernet.ETHER_TYPE_CLASS_MAP
alshabibc4901cd2014-09-05 16:50:40 -0700335 .get(this.etherType);
336 try {
337 payload = clazz.newInstance();
338 } catch (final Exception e) {
339 throw new RuntimeException(
340 "Error parsing payload for Ethernet packet", e);
341 }
342 } else {
343 payload = new Data();
344 }
345 this.payload = payload.deserialize(data, bb.position(),
346 bb.limit() - bb.position());
347 this.payload.setParent(this);
348 return this;
349 }
350
351 /**
352 * Checks to see if a string is a valid MAC address.
353 *
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800354 * @param macAddress string to test if it is a valid MAC
alshabibc4901cd2014-09-05 16:50:40 -0700355 * @return True if macAddress is a valid MAC, False otherwise
356 */
357 public static boolean isMACAddress(final String macAddress) {
358 final String[] macBytes = macAddress.split(":");
359 if (macBytes.length != 6) {
360 return false;
361 }
362 for (int i = 0; i < 6; ++i) {
363 if (Ethernet.HEXES.indexOf(macBytes[i].toUpperCase().charAt(0)) == -1
364 || Ethernet.HEXES.indexOf(macBytes[i].toUpperCase().charAt(
365 1)) == -1) {
366 return false;
367 }
368 }
369 return true;
370 }
371
372 /**
373 * Accepts a MAC address of the form 00:aa:11:bb:22:cc, case does not
374 * matter, and returns a corresponding byte[].
375 *
376 * @param macAddress
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800377 * The MAC address to convert into a byte array
alshabibc4901cd2014-09-05 16:50:40 -0700378 * @return The macAddress as a byte array
379 */
380 public static byte[] toMACAddress(final String macAddress) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700381 return MacAddress.valueOf(macAddress).toBytes();
alshabibc4901cd2014-09-05 16:50:40 -0700382 }
383
384 /**
385 * Accepts a MAC address and returns the corresponding long, where the MAC
386 * bytes are set on the lower order bytes of the long.
387 *
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800388 * @param macAddress MAC address as a byte array
alshabibc4901cd2014-09-05 16:50:40 -0700389 * @return a long containing the mac address bytes
390 */
391 public static long toLong(final byte[] macAddress) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700392 return MacAddress.valueOf(macAddress).toLong();
alshabibc4901cd2014-09-05 16:50:40 -0700393 }
394
395 /**
396 * Converts a long MAC address to a byte array.
397 *
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800398 * @param macAddress MAC address set on the lower order bytes of the long
alshabibc4901cd2014-09-05 16:50:40 -0700399 * @return the bytes of the mac address
400 */
401 public static byte[] toByteArray(final long macAddress) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700402 return MacAddress.valueOf(macAddress).toBytes();
alshabibc4901cd2014-09-05 16:50:40 -0700403 }
404
405 /*
406 * (non-Javadoc)
407 *
408 * @see java.lang.Object#hashCode()
409 */
410 @Override
411 public int hashCode() {
412 final int prime = 7867;
413 int result = super.hashCode();
414 result = prime * result + this.destinationMACAddress.hashCode();
415 result = prime * result + this.etherType;
416 result = prime * result + this.vlanID;
417 result = prime * result + this.priorityCode;
418 result = prime * result + (this.pad ? 1231 : 1237);
419 result = prime * result + this.sourceMACAddress.hashCode();
420 return result;
421 }
422
423 /*
424 * (non-Javadoc)
425 *
426 * @see java.lang.Object#equals(java.lang.Object)
427 */
428 @Override
429 public boolean equals(final Object obj) {
430 if (this == obj) {
431 return true;
432 }
433 if (!super.equals(obj)) {
434 return false;
435 }
436 if (!(obj instanceof Ethernet)) {
437 return false;
438 }
439 final Ethernet other = (Ethernet) obj;
440 if (!this.destinationMACAddress.equals(other.destinationMACAddress)) {
441 return false;
442 }
443 if (this.priorityCode != other.priorityCode) {
444 return false;
445 }
446 if (this.vlanID != other.vlanID) {
447 return false;
448 }
449 if (this.etherType != other.etherType) {
450 return false;
451 }
452 if (this.pad != other.pad) {
453 return false;
454 }
455 if (!this.sourceMACAddress.equals(other.sourceMACAddress)) {
456 return false;
457 }
458 return true;
459 }
460
461 /*
462 * (non-Javadoc)
463 *
464 * @see java.lang.Object#toString(java.lang.Object)
465 */
466 @Override
467 public String toString() {
468
469 final StringBuffer sb = new StringBuffer("\n");
470
471 final IPacket pkt = this.getPayload();
472
473 if (pkt instanceof ARP) {
474 sb.append("arp");
475 } else if (pkt instanceof LLDP) {
476 sb.append("lldp");
477 } else if (pkt instanceof ICMP) {
478 sb.append("icmp");
479 } else if (pkt instanceof IPv4) {
480 sb.append("ip");
481 } else if (pkt instanceof DHCP) {
482 sb.append("dhcp");
483 } else {
484 sb.append(this.getEtherType());
485 }
486
487 sb.append("\ndl_vlan: ");
488 if (this.getVlanID() == Ethernet.VLAN_UNTAGGED) {
489 sb.append("untagged");
490 } else {
491 sb.append(this.getVlanID());
492 }
493 sb.append("\ndl_vlan_pcp: ");
494 sb.append(this.getPriorityCode());
495 sb.append("\ndl_src: ");
496 sb.append(bytesToHex(this.getSourceMACAddress()));
497 sb.append("\ndl_dst: ");
498 sb.append(bytesToHex(this.getDestinationMACAddress()));
499
500 if (pkt instanceof ARP) {
501 final ARP p = (ARP) pkt;
502 sb.append("\nnw_src: ");
503 sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p
504 .getSenderProtocolAddress())));
505 sb.append("\nnw_dst: ");
506 sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p
507 .getTargetProtocolAddress())));
508 } else if (pkt instanceof LLDP) {
509 sb.append("lldp packet");
510 } else if (pkt instanceof ICMP) {
511 final ICMP icmp = (ICMP) pkt;
512 sb.append("\nicmp_type: ");
513 sb.append(icmp.getIcmpType());
514 sb.append("\nicmp_code: ");
515 sb.append(icmp.getIcmpCode());
516 } else if (pkt instanceof IPv4) {
517 final IPv4 p = (IPv4) pkt;
518 sb.append("\nnw_src: ");
519 sb.append(IPv4.fromIPv4Address(p.getSourceAddress()));
520 sb.append("\nnw_dst: ");
521 sb.append(IPv4.fromIPv4Address(p.getDestinationAddress()));
522 sb.append("\nnw_tos: ");
523 sb.append(p.getDiffServ());
524 sb.append("\nnw_proto: ");
525 sb.append(p.getProtocol());
526
527 if (pkt instanceof TCP) {
528 sb.append("\ntp_src: ");
529 sb.append(((TCP) pkt).getSourcePort());
530 sb.append("\ntp_dst: ");
531 sb.append(((TCP) pkt).getDestinationPort());
532
533 } else if (pkt instanceof UDP) {
534 sb.append("\ntp_src: ");
535 sb.append(((UDP) pkt).getSourcePort());
536 sb.append("\ntp_dst: ");
537 sb.append(((UDP) pkt).getDestinationPort());
538 }
539
540 if (pkt instanceof ICMP) {
541 final ICMP icmp = (ICMP) pkt;
542 sb.append("\nicmp_type: ");
543 sb.append(icmp.getIcmpType());
544 sb.append("\nicmp_code: ");
545 sb.append(icmp.getIcmpCode());
546 }
547
548 } else if (pkt instanceof DHCP) {
549 sb.append("\ndhcp packet");
550 } else if (pkt instanceof Data) {
551 sb.append("\ndata packet");
552 } else if (pkt instanceof LLC) {
553 sb.append("\nllc packet");
554 } else {
tom5f18cf32014-09-13 14:10:57 -0700555 sb.append("\nunknown packet");
alshabibc4901cd2014-09-05 16:50:40 -0700556 }
557
558 return sb.toString();
559 }
560
561 public static String bytesToHex(byte[] in) {
562 final StringBuilder builder = new StringBuilder();
563 for (byte b : in) {
564 builder.append(String.format("%02x", b));
565 }
566 return builder.toString();
567 }
568
569}