blob: 0058e168cd12cb7d08742e7c91a794eb3a712548 [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";
34 public static final short TYPE_ARP = 0x0806;
35 public static final short TYPE_RARP = (short) 0x8035;
36 public static final short TYPE_IPV4 = 0x0800;
37 public static final short TYPE_LLDP = (short) 0x88cc;
38 public static final short TYPE_BSN = (short) 0x8942;
39 public static final short VLAN_UNTAGGED = (short) 0xffff;
Praseed Balakrishnan8c67d172014-11-10 10:15:41 -080040 public static final short MPLS_UNICAST = (short) 0x8847;
41 public static final short MPLS_MULTICAST = (short) 0x8848;
alshabibc4901cd2014-09-05 16:50:40 -070042 public static final short DATALAYER_ADDRESS_LENGTH = 6; // bytes
43 public static Map<Short, Class<? extends IPacket>> etherTypeClassMap;
44
45 static {
46 Ethernet.etherTypeClassMap = new HashMap<Short, Class<? extends IPacket>>();
47 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_ARP, ARP.class);
48 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_RARP, ARP.class);
49 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_IPV4, IPv4.class);
50 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_LLDP, LLDP.class);
alshabib7911a052014-10-16 17:49:37 -070051 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_BSN, LLDP.class);
alshabibc4901cd2014-09-05 16:50:40 -070052 }
53
Ayaka Koshibea9c199f2014-09-16 16:21:40 -070054 protected MacAddress destinationMACAddress;
55 protected MacAddress sourceMACAddress;
alshabibc4901cd2014-09-05 16:50:40 -070056 protected byte priorityCode;
57 protected short vlanID;
58 protected short etherType;
59 protected boolean pad = false;
60
61 /**
62 * By default, set Ethernet to untagged.
63 */
64 public Ethernet() {
65 super();
66 this.vlanID = Ethernet.VLAN_UNTAGGED;
67 }
68
69 /**
70 * Gets the destination MAC address.
71 *
72 * @return the destination MAC as a byte array
73 */
74 public byte[] getDestinationMACAddress() {
75 return this.destinationMACAddress.toBytes();
76 }
77
78 /**
79 * Gets the destination MAC address.
80 *
81 * @return the destination MAC
82 */
Ayaka Koshibea9c199f2014-09-16 16:21:40 -070083 public MacAddress getDestinationMAC() {
alshabibc4901cd2014-09-05 16:50:40 -070084 return this.destinationMACAddress;
85 }
86
87 /**
88 * Sets the destination MAC address.
89 *
tom5f18cf32014-09-13 14:10:57 -070090 * @param destMac the destination MAC to set
alshabibc4901cd2014-09-05 16:50:40 -070091 * @return the Ethernet frame
92 */
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -080093 public Ethernet setDestinationMACAddress(final MacAddress destMac) {
94 this.destinationMACAddress = checkNotNull(destMac);
95 return this;
96 }
97
98 /**
99 * Sets the destination MAC address.
100 *
101 * @param destMac the destination MAC to set
102 * @return the Ethernet frame
103 */
alshabibc4901cd2014-09-05 16:50:40 -0700104 public Ethernet setDestinationMACAddress(final byte[] destMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700105 this.destinationMACAddress = MacAddress.valueOf(destMac);
alshabibc4901cd2014-09-05 16:50:40 -0700106 return this;
107 }
108
109 /**
110 * Sets the destination MAC address.
111 *
tom5f18cf32014-09-13 14:10:57 -0700112 * @param destMac the destination MAC to set
alshabibc4901cd2014-09-05 16:50:40 -0700113 * @return the Ethernet frame
114 */
115 public Ethernet setDestinationMACAddress(final String destMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700116 this.destinationMACAddress = MacAddress.valueOf(destMac);
alshabibc4901cd2014-09-05 16:50:40 -0700117 return this;
118 }
119
120 /**
121 * Gets the source MAC address.
122 *
123 * @return the source MACAddress as a byte array
124 */
125 public byte[] getSourceMACAddress() {
126 return this.sourceMACAddress.toBytes();
127 }
128
129 /**
130 * Gets the source MAC address.
131 *
132 * @return the source MACAddress
133 */
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700134 public MacAddress getSourceMAC() {
alshabibc4901cd2014-09-05 16:50:40 -0700135 return this.sourceMACAddress;
136 }
137
138 /**
139 * Sets the source MAC address.
140 *
tom5f18cf32014-09-13 14:10:57 -0700141 * @param sourceMac the source MAC to set
alshabibc4901cd2014-09-05 16:50:40 -0700142 * @return the Ethernet frame
143 */
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800144 public Ethernet setSourceMACAddress(final MacAddress sourceMac) {
145 this.sourceMACAddress = checkNotNull(sourceMac);
146 return this;
147 }
148
149 /**
150 * Sets the source MAC address.
151 *
152 * @param sourceMac the source MAC to set
153 * @return the Ethernet frame
154 */
alshabibc4901cd2014-09-05 16:50:40 -0700155 public Ethernet setSourceMACAddress(final byte[] sourceMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700156 this.sourceMACAddress = MacAddress.valueOf(sourceMac);
alshabibc4901cd2014-09-05 16:50:40 -0700157 return this;
158 }
159
160 /**
161 * Sets the source MAC address.
162 *
tom5f18cf32014-09-13 14:10:57 -0700163 * @param sourceMac the source MAC to set
alshabibc4901cd2014-09-05 16:50:40 -0700164 * @return the Ethernet frame
165 */
166 public Ethernet setSourceMACAddress(final String sourceMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700167 this.sourceMACAddress = MacAddress.valueOf(sourceMac);
alshabibc4901cd2014-09-05 16:50:40 -0700168 return this;
169 }
170
171 /**
172 * Gets the priority code.
173 *
174 * @return the priorityCode
175 */
176 public byte getPriorityCode() {
177 return this.priorityCode;
178 }
179
180 /**
181 * Sets the priority code.
182 *
tom5f18cf32014-09-13 14:10:57 -0700183 * @param priority the priorityCode to set
alshabibc4901cd2014-09-05 16:50:40 -0700184 * @return the Ethernet frame
185 */
186 public Ethernet setPriorityCode(final byte priority) {
187 this.priorityCode = priority;
188 return this;
189 }
190
191 /**
192 * Gets the VLAN ID.
193 *
194 * @return the vlanID
195 */
196 public short getVlanID() {
197 return this.vlanID;
198 }
199
200 /**
201 * Sets the VLAN ID.
202 *
tom5f18cf32014-09-13 14:10:57 -0700203 * @param vlan the vlanID to set
alshabibc4901cd2014-09-05 16:50:40 -0700204 * @return the Ethernet frame
205 */
206 public Ethernet setVlanID(final short vlan) {
207 this.vlanID = vlan;
208 return this;
209 }
210
211 /**
212 * Gets the Ethernet type.
213 *
214 * @return the etherType
215 */
216 public short getEtherType() {
217 return this.etherType;
218 }
219
220 /**
221 * Sets the Ethernet type.
222 *
tom5f18cf32014-09-13 14:10:57 -0700223 * @param ethType the etherType to set
alshabibc4901cd2014-09-05 16:50:40 -0700224 * @return the Ethernet frame
225 */
226 public Ethernet setEtherType(final short ethType) {
227 this.etherType = ethType;
228 return this;
229 }
230
231 /**
232 * @return True if the Ethernet frame is broadcast, false otherwise
233 */
234 public boolean isBroadcast() {
235 assert this.destinationMACAddress.length() == 6;
236 return this.destinationMACAddress.isBroadcast();
237 }
238
239 /**
240 * @return True is the Ethernet frame is multicast, False otherwise
241 */
242 public boolean isMulticast() {
243 return this.destinationMACAddress.isMulticast();
244 }
245
246 /**
247 * Pad this packet to 60 bytes minimum, filling with zeros?
248 *
249 * @return the pad
250 */
251 public boolean isPad() {
252 return this.pad;
253 }
254
255 /**
256 * Pad this packet to 60 bytes minimum, filling with zeros?
257 *
tom5f18cf32014-09-13 14:10:57 -0700258 * @param pd
alshabibc4901cd2014-09-05 16:50:40 -0700259 * the pad to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800260 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700261 */
262 public Ethernet setPad(final boolean pd) {
263 this.pad = pd;
264 return this;
265 }
266
267 @Override
268 public byte[] serialize() {
269 byte[] payloadData = null;
270 if (this.payload != null) {
271 this.payload.setParent(this);
272 payloadData = this.payload.serialize();
273 }
274 int length = 14 + (this.vlanID == Ethernet.VLAN_UNTAGGED ? 0 : 4)
275 + (payloadData == null ? 0 : payloadData.length);
276 if (this.pad && length < 60) {
277 length = 60;
278 }
279 final byte[] data = new byte[length];
280 final ByteBuffer bb = ByteBuffer.wrap(data);
281 bb.put(this.destinationMACAddress.toBytes());
282 bb.put(this.sourceMACAddress.toBytes());
283 if (this.vlanID != Ethernet.VLAN_UNTAGGED) {
284 bb.putShort((short) 0x8100);
285 bb.putShort((short) (this.priorityCode << 13 | this.vlanID & 0x0fff));
286 }
287 bb.putShort(this.etherType);
288 if (payloadData != null) {
289 bb.put(payloadData);
290 }
291 if (this.pad) {
292 Arrays.fill(data, bb.position(), data.length, (byte) 0x0);
293 }
294 return data;
295 }
296
297 @Override
298 public IPacket deserialize(final byte[] data, final int offset,
299 final int length) {
300 if (length <= 0) {
301 return null;
302 }
303 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
304 if (this.destinationMACAddress == null) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700305 this.destinationMACAddress = MacAddress.valueOf(new byte[6]);
alshabibc4901cd2014-09-05 16:50:40 -0700306 }
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700307 final byte[] dstAddr = new byte[MacAddress.MAC_ADDRESS_LENGTH];
alshabibc4901cd2014-09-05 16:50:40 -0700308 bb.get(dstAddr);
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700309 this.destinationMACAddress = MacAddress.valueOf(dstAddr);
alshabibc4901cd2014-09-05 16:50:40 -0700310
311 if (this.sourceMACAddress == null) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700312 this.sourceMACAddress = MacAddress.valueOf(new byte[6]);
alshabibc4901cd2014-09-05 16:50:40 -0700313 }
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700314 final byte[] srcAddr = new byte[MacAddress.MAC_ADDRESS_LENGTH];
alshabibc4901cd2014-09-05 16:50:40 -0700315 bb.get(srcAddr);
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700316 this.sourceMACAddress = MacAddress.valueOf(srcAddr);
alshabibc4901cd2014-09-05 16:50:40 -0700317
318 short ethType = bb.getShort();
319 if (ethType == (short) 0x8100) {
320 final short tci = bb.getShort();
321 this.priorityCode = (byte) (tci >> 13 & 0x07);
322 this.vlanID = (short) (tci & 0x0fff);
323 ethType = bb.getShort();
324 } else {
325 this.vlanID = Ethernet.VLAN_UNTAGGED;
326 }
327 this.etherType = ethType;
328
329 IPacket payload;
330 if (Ethernet.etherTypeClassMap.containsKey(this.etherType)) {
331 final Class<? extends IPacket> clazz = Ethernet.etherTypeClassMap
332 .get(this.etherType);
333 try {
334 payload = clazz.newInstance();
335 } catch (final Exception e) {
336 throw new RuntimeException(
337 "Error parsing payload for Ethernet packet", e);
338 }
339 } else {
340 payload = new Data();
341 }
342 this.payload = payload.deserialize(data, bb.position(),
343 bb.limit() - bb.position());
344 this.payload.setParent(this);
345 return this;
346 }
347
348 /**
349 * Checks to see if a string is a valid MAC address.
350 *
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800351 * @param macAddress string to test if it is a valid MAC
alshabibc4901cd2014-09-05 16:50:40 -0700352 * @return True if macAddress is a valid MAC, False otherwise
353 */
354 public static boolean isMACAddress(final String macAddress) {
355 final String[] macBytes = macAddress.split(":");
356 if (macBytes.length != 6) {
357 return false;
358 }
359 for (int i = 0; i < 6; ++i) {
360 if (Ethernet.HEXES.indexOf(macBytes[i].toUpperCase().charAt(0)) == -1
361 || Ethernet.HEXES.indexOf(macBytes[i].toUpperCase().charAt(
362 1)) == -1) {
363 return false;
364 }
365 }
366 return true;
367 }
368
369 /**
370 * Accepts a MAC address of the form 00:aa:11:bb:22:cc, case does not
371 * matter, and returns a corresponding byte[].
372 *
373 * @param macAddress
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800374 * The MAC address to convert into a byte array
alshabibc4901cd2014-09-05 16:50:40 -0700375 * @return The macAddress as a byte array
376 */
377 public static byte[] toMACAddress(final String macAddress) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700378 return MacAddress.valueOf(macAddress).toBytes();
alshabibc4901cd2014-09-05 16:50:40 -0700379 }
380
381 /**
382 * Accepts a MAC address and returns the corresponding long, where the MAC
383 * bytes are set on the lower order bytes of the long.
384 *
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800385 * @param macAddress MAC address as a byte array
alshabibc4901cd2014-09-05 16:50:40 -0700386 * @return a long containing the mac address bytes
387 */
388 public static long toLong(final byte[] macAddress) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700389 return MacAddress.valueOf(macAddress).toLong();
alshabibc4901cd2014-09-05 16:50:40 -0700390 }
391
392 /**
393 * Converts a long MAC address to a byte array.
394 *
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800395 * @param macAddress MAC address set on the lower order bytes of the long
alshabibc4901cd2014-09-05 16:50:40 -0700396 * @return the bytes of the mac address
397 */
398 public static byte[] toByteArray(final long macAddress) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700399 return MacAddress.valueOf(macAddress).toBytes();
alshabibc4901cd2014-09-05 16:50:40 -0700400 }
401
402 /*
403 * (non-Javadoc)
404 *
405 * @see java.lang.Object#hashCode()
406 */
407 @Override
408 public int hashCode() {
409 final int prime = 7867;
410 int result = super.hashCode();
411 result = prime * result + this.destinationMACAddress.hashCode();
412 result = prime * result + this.etherType;
413 result = prime * result + this.vlanID;
414 result = prime * result + this.priorityCode;
415 result = prime * result + (this.pad ? 1231 : 1237);
416 result = prime * result + this.sourceMACAddress.hashCode();
417 return result;
418 }
419
420 /*
421 * (non-Javadoc)
422 *
423 * @see java.lang.Object#equals(java.lang.Object)
424 */
425 @Override
426 public boolean equals(final Object obj) {
427 if (this == obj) {
428 return true;
429 }
430 if (!super.equals(obj)) {
431 return false;
432 }
433 if (!(obj instanceof Ethernet)) {
434 return false;
435 }
436 final Ethernet other = (Ethernet) obj;
437 if (!this.destinationMACAddress.equals(other.destinationMACAddress)) {
438 return false;
439 }
440 if (this.priorityCode != other.priorityCode) {
441 return false;
442 }
443 if (this.vlanID != other.vlanID) {
444 return false;
445 }
446 if (this.etherType != other.etherType) {
447 return false;
448 }
449 if (this.pad != other.pad) {
450 return false;
451 }
452 if (!this.sourceMACAddress.equals(other.sourceMACAddress)) {
453 return false;
454 }
455 return true;
456 }
457
458 /*
459 * (non-Javadoc)
460 *
461 * @see java.lang.Object#toString(java.lang.Object)
462 */
463 @Override
464 public String toString() {
465
466 final StringBuffer sb = new StringBuffer("\n");
467
468 final IPacket pkt = this.getPayload();
469
470 if (pkt instanceof ARP) {
471 sb.append("arp");
472 } else if (pkt instanceof LLDP) {
473 sb.append("lldp");
474 } else if (pkt instanceof ICMP) {
475 sb.append("icmp");
476 } else if (pkt instanceof IPv4) {
477 sb.append("ip");
478 } else if (pkt instanceof DHCP) {
479 sb.append("dhcp");
480 } else {
481 sb.append(this.getEtherType());
482 }
483
484 sb.append("\ndl_vlan: ");
485 if (this.getVlanID() == Ethernet.VLAN_UNTAGGED) {
486 sb.append("untagged");
487 } else {
488 sb.append(this.getVlanID());
489 }
490 sb.append("\ndl_vlan_pcp: ");
491 sb.append(this.getPriorityCode());
492 sb.append("\ndl_src: ");
493 sb.append(bytesToHex(this.getSourceMACAddress()));
494 sb.append("\ndl_dst: ");
495 sb.append(bytesToHex(this.getDestinationMACAddress()));
496
497 if (pkt instanceof ARP) {
498 final ARP p = (ARP) pkt;
499 sb.append("\nnw_src: ");
500 sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p
501 .getSenderProtocolAddress())));
502 sb.append("\nnw_dst: ");
503 sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p
504 .getTargetProtocolAddress())));
505 } else if (pkt instanceof LLDP) {
506 sb.append("lldp packet");
507 } else if (pkt instanceof ICMP) {
508 final ICMP icmp = (ICMP) pkt;
509 sb.append("\nicmp_type: ");
510 sb.append(icmp.getIcmpType());
511 sb.append("\nicmp_code: ");
512 sb.append(icmp.getIcmpCode());
513 } else if (pkt instanceof IPv4) {
514 final IPv4 p = (IPv4) pkt;
515 sb.append("\nnw_src: ");
516 sb.append(IPv4.fromIPv4Address(p.getSourceAddress()));
517 sb.append("\nnw_dst: ");
518 sb.append(IPv4.fromIPv4Address(p.getDestinationAddress()));
519 sb.append("\nnw_tos: ");
520 sb.append(p.getDiffServ());
521 sb.append("\nnw_proto: ");
522 sb.append(p.getProtocol());
523
524 if (pkt instanceof TCP) {
525 sb.append("\ntp_src: ");
526 sb.append(((TCP) pkt).getSourcePort());
527 sb.append("\ntp_dst: ");
528 sb.append(((TCP) pkt).getDestinationPort());
529
530 } else if (pkt instanceof UDP) {
531 sb.append("\ntp_src: ");
532 sb.append(((UDP) pkt).getSourcePort());
533 sb.append("\ntp_dst: ");
534 sb.append(((UDP) pkt).getDestinationPort());
535 }
536
537 if (pkt instanceof ICMP) {
538 final ICMP icmp = (ICMP) pkt;
539 sb.append("\nicmp_type: ");
540 sb.append(icmp.getIcmpType());
541 sb.append("\nicmp_code: ");
542 sb.append(icmp.getIcmpCode());
543 }
544
545 } else if (pkt instanceof DHCP) {
546 sb.append("\ndhcp packet");
547 } else if (pkt instanceof Data) {
548 sb.append("\ndata packet");
549 } else if (pkt instanceof LLC) {
550 sb.append("\nllc packet");
551 } else {
tom5f18cf32014-09-13 14:10:57 -0700552 sb.append("\nunknown packet");
alshabibc4901cd2014-09-05 16:50:40 -0700553 }
554
555 return sb.toString();
556 }
557
558 public static String bytesToHex(byte[] in) {
559 final StringBuilder builder = new StringBuilder();
560 for (byte b : in) {
561 builder.append(String.format("%02x", b));
562 }
563 return builder.toString();
564 }
565
566}