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