blob: 70a52e2d5007fcadb7ef8cd08e6c952a06613778 [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
21import java.nio.ByteBuffer;
22import java.util.Arrays;
23import java.util.HashMap;
24import java.util.Map;
25
26
27/**
28 *
alshabibc4901cd2014-09-05 16:50:40 -070029 */
30public class Ethernet extends BasePacket {
31 private static final String HEXES = "0123456789ABCDEF";
32 public static final short TYPE_ARP = 0x0806;
33 public static final short TYPE_RARP = (short) 0x8035;
34 public static final short TYPE_IPV4 = 0x0800;
35 public static final short TYPE_LLDP = (short) 0x88cc;
36 public static final short TYPE_BSN = (short) 0x8942;
37 public static final short VLAN_UNTAGGED = (short) 0xffff;
38 public static final short DATALAYER_ADDRESS_LENGTH = 6; // bytes
39 public static Map<Short, Class<? extends IPacket>> etherTypeClassMap;
40
41 static {
42 Ethernet.etherTypeClassMap = new HashMap<Short, Class<? extends IPacket>>();
43 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_ARP, ARP.class);
44 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_RARP, ARP.class);
45 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_IPV4, IPv4.class);
46 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_LLDP, LLDP.class);
alshabib7911a052014-10-16 17:49:37 -070047 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_BSN, LLDP.class);
alshabibc4901cd2014-09-05 16:50:40 -070048 }
49
Ayaka Koshibea9c199f2014-09-16 16:21:40 -070050 protected MacAddress destinationMACAddress;
51 protected MacAddress sourceMACAddress;
alshabibc4901cd2014-09-05 16:50:40 -070052 protected byte priorityCode;
53 protected short vlanID;
54 protected short etherType;
55 protected boolean pad = false;
56
57 /**
58 * By default, set Ethernet to untagged.
59 */
60 public Ethernet() {
61 super();
62 this.vlanID = Ethernet.VLAN_UNTAGGED;
63 }
64
65 /**
66 * Gets the destination MAC address.
67 *
68 * @return the destination MAC as a byte array
69 */
70 public byte[] getDestinationMACAddress() {
71 return this.destinationMACAddress.toBytes();
72 }
73
74 /**
75 * Gets the destination MAC address.
76 *
77 * @return the destination MAC
78 */
Ayaka Koshibea9c199f2014-09-16 16:21:40 -070079 public MacAddress getDestinationMAC() {
alshabibc4901cd2014-09-05 16:50:40 -070080 return this.destinationMACAddress;
81 }
82
83 /**
84 * Sets the destination MAC address.
85 *
tom5f18cf32014-09-13 14:10:57 -070086 * @param destMac the destination MAC to set
alshabibc4901cd2014-09-05 16:50:40 -070087 * @return the Ethernet frame
88 */
89 public Ethernet setDestinationMACAddress(final byte[] destMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -070090 this.destinationMACAddress = MacAddress.valueOf(destMac);
alshabibc4901cd2014-09-05 16:50:40 -070091 return this;
92 }
93
94 /**
95 * Sets the destination MAC address.
96 *
tom5f18cf32014-09-13 14:10:57 -070097 * @param destMac the destination MAC to set
alshabibc4901cd2014-09-05 16:50:40 -070098 * @return the Ethernet frame
99 */
100 public Ethernet setDestinationMACAddress(final String destMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700101 this.destinationMACAddress = MacAddress.valueOf(destMac);
alshabibc4901cd2014-09-05 16:50:40 -0700102 return this;
103 }
104
105 /**
106 * Gets the source MAC address.
107 *
108 * @return the source MACAddress as a byte array
109 */
110 public byte[] getSourceMACAddress() {
111 return this.sourceMACAddress.toBytes();
112 }
113
114 /**
115 * Gets the source MAC address.
116 *
117 * @return the source MACAddress
118 */
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700119 public MacAddress getSourceMAC() {
alshabibc4901cd2014-09-05 16:50:40 -0700120 return this.sourceMACAddress;
121 }
122
123 /**
124 * Sets the source MAC address.
125 *
tom5f18cf32014-09-13 14:10:57 -0700126 * @param sourceMac the source MAC to set
alshabibc4901cd2014-09-05 16:50:40 -0700127 * @return the Ethernet frame
128 */
129 public Ethernet setSourceMACAddress(final byte[] sourceMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700130 this.sourceMACAddress = MacAddress.valueOf(sourceMac);
alshabibc4901cd2014-09-05 16:50:40 -0700131 return this;
132 }
133
134 /**
135 * Sets the source MAC address.
136 *
tom5f18cf32014-09-13 14:10:57 -0700137 * @param sourceMac the source MAC to set
alshabibc4901cd2014-09-05 16:50:40 -0700138 * @return the Ethernet frame
139 */
140 public Ethernet setSourceMACAddress(final String sourceMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700141 this.sourceMACAddress = MacAddress.valueOf(sourceMac);
alshabibc4901cd2014-09-05 16:50:40 -0700142 return this;
143 }
144
145 /**
146 * Gets the priority code.
147 *
148 * @return the priorityCode
149 */
150 public byte getPriorityCode() {
151 return this.priorityCode;
152 }
153
154 /**
155 * Sets the priority code.
156 *
tom5f18cf32014-09-13 14:10:57 -0700157 * @param priority the priorityCode to set
alshabibc4901cd2014-09-05 16:50:40 -0700158 * @return the Ethernet frame
159 */
160 public Ethernet setPriorityCode(final byte priority) {
161 this.priorityCode = priority;
162 return this;
163 }
164
165 /**
166 * Gets the VLAN ID.
167 *
168 * @return the vlanID
169 */
170 public short getVlanID() {
171 return this.vlanID;
172 }
173
174 /**
175 * Sets the VLAN ID.
176 *
tom5f18cf32014-09-13 14:10:57 -0700177 * @param vlan the vlanID to set
alshabibc4901cd2014-09-05 16:50:40 -0700178 * @return the Ethernet frame
179 */
180 public Ethernet setVlanID(final short vlan) {
181 this.vlanID = vlan;
182 return this;
183 }
184
185 /**
186 * Gets the Ethernet type.
187 *
188 * @return the etherType
189 */
190 public short getEtherType() {
191 return this.etherType;
192 }
193
194 /**
195 * Sets the Ethernet type.
196 *
tom5f18cf32014-09-13 14:10:57 -0700197 * @param ethType the etherType to set
alshabibc4901cd2014-09-05 16:50:40 -0700198 * @return the Ethernet frame
199 */
200 public Ethernet setEtherType(final short ethType) {
201 this.etherType = ethType;
202 return this;
203 }
204
205 /**
206 * @return True if the Ethernet frame is broadcast, false otherwise
207 */
208 public boolean isBroadcast() {
209 assert this.destinationMACAddress.length() == 6;
210 return this.destinationMACAddress.isBroadcast();
211 }
212
213 /**
214 * @return True is the Ethernet frame is multicast, False otherwise
215 */
216 public boolean isMulticast() {
217 return this.destinationMACAddress.isMulticast();
218 }
219
220 /**
221 * Pad this packet to 60 bytes minimum, filling with zeros?
222 *
223 * @return the pad
224 */
225 public boolean isPad() {
226 return this.pad;
227 }
228
229 /**
230 * Pad this packet to 60 bytes minimum, filling with zeros?
231 *
tom5f18cf32014-09-13 14:10:57 -0700232 * @param pd
alshabibc4901cd2014-09-05 16:50:40 -0700233 * the pad to set
234 */
235 public Ethernet setPad(final boolean pd) {
236 this.pad = pd;
237 return this;
238 }
239
240 @Override
241 public byte[] serialize() {
242 byte[] payloadData = null;
243 if (this.payload != null) {
244 this.payload.setParent(this);
245 payloadData = this.payload.serialize();
246 }
247 int length = 14 + (this.vlanID == Ethernet.VLAN_UNTAGGED ? 0 : 4)
248 + (payloadData == null ? 0 : payloadData.length);
249 if (this.pad && length < 60) {
250 length = 60;
251 }
252 final byte[] data = new byte[length];
253 final ByteBuffer bb = ByteBuffer.wrap(data);
254 bb.put(this.destinationMACAddress.toBytes());
255 bb.put(this.sourceMACAddress.toBytes());
256 if (this.vlanID != Ethernet.VLAN_UNTAGGED) {
257 bb.putShort((short) 0x8100);
258 bb.putShort((short) (this.priorityCode << 13 | this.vlanID & 0x0fff));
259 }
260 bb.putShort(this.etherType);
261 if (payloadData != null) {
262 bb.put(payloadData);
263 }
264 if (this.pad) {
265 Arrays.fill(data, bb.position(), data.length, (byte) 0x0);
266 }
267 return data;
268 }
269
270 @Override
271 public IPacket deserialize(final byte[] data, final int offset,
272 final int length) {
273 if (length <= 0) {
274 return null;
275 }
276 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
277 if (this.destinationMACAddress == null) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700278 this.destinationMACAddress = MacAddress.valueOf(new byte[6]);
alshabibc4901cd2014-09-05 16:50:40 -0700279 }
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700280 final byte[] dstAddr = new byte[MacAddress.MAC_ADDRESS_LENGTH];
alshabibc4901cd2014-09-05 16:50:40 -0700281 bb.get(dstAddr);
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700282 this.destinationMACAddress = MacAddress.valueOf(dstAddr);
alshabibc4901cd2014-09-05 16:50:40 -0700283
284 if (this.sourceMACAddress == null) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700285 this.sourceMACAddress = MacAddress.valueOf(new byte[6]);
alshabibc4901cd2014-09-05 16:50:40 -0700286 }
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700287 final byte[] srcAddr = new byte[MacAddress.MAC_ADDRESS_LENGTH];
alshabibc4901cd2014-09-05 16:50:40 -0700288 bb.get(srcAddr);
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700289 this.sourceMACAddress = MacAddress.valueOf(srcAddr);
alshabibc4901cd2014-09-05 16:50:40 -0700290
291 short ethType = bb.getShort();
292 if (ethType == (short) 0x8100) {
293 final short tci = bb.getShort();
294 this.priorityCode = (byte) (tci >> 13 & 0x07);
295 this.vlanID = (short) (tci & 0x0fff);
296 ethType = bb.getShort();
297 } else {
298 this.vlanID = Ethernet.VLAN_UNTAGGED;
299 }
300 this.etherType = ethType;
301
302 IPacket payload;
303 if (Ethernet.etherTypeClassMap.containsKey(this.etherType)) {
304 final Class<? extends IPacket> clazz = Ethernet.etherTypeClassMap
305 .get(this.etherType);
306 try {
307 payload = clazz.newInstance();
308 } catch (final Exception e) {
309 throw new RuntimeException(
310 "Error parsing payload for Ethernet packet", e);
311 }
312 } else {
313 payload = new Data();
314 }
315 this.payload = payload.deserialize(data, bb.position(),
316 bb.limit() - bb.position());
317 this.payload.setParent(this);
318 return this;
319 }
320
321 /**
322 * Checks to see if a string is a valid MAC address.
323 *
324 * @param macAddress
325 * @return True if macAddress is a valid MAC, False otherwise
326 */
327 public static boolean isMACAddress(final String macAddress) {
328 final String[] macBytes = macAddress.split(":");
329 if (macBytes.length != 6) {
330 return false;
331 }
332 for (int i = 0; i < 6; ++i) {
333 if (Ethernet.HEXES.indexOf(macBytes[i].toUpperCase().charAt(0)) == -1
334 || Ethernet.HEXES.indexOf(macBytes[i].toUpperCase().charAt(
335 1)) == -1) {
336 return false;
337 }
338 }
339 return true;
340 }
341
342 /**
343 * Accepts a MAC address of the form 00:aa:11:bb:22:cc, case does not
344 * matter, and returns a corresponding byte[].
345 *
346 * @param macAddress
347 * The MAC address to convert into a bye array
348 * @return The macAddress as a byte array
349 */
350 public static byte[] toMACAddress(final String macAddress) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700351 return MacAddress.valueOf(macAddress).toBytes();
alshabibc4901cd2014-09-05 16:50:40 -0700352 }
353
354 /**
355 * Accepts a MAC address and returns the corresponding long, where the MAC
356 * bytes are set on the lower order bytes of the long.
357 *
358 * @param macAddress
359 * @return a long containing the mac address bytes
360 */
361 public static long toLong(final byte[] macAddress) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700362 return MacAddress.valueOf(macAddress).toLong();
alshabibc4901cd2014-09-05 16:50:40 -0700363 }
364
365 /**
366 * Converts a long MAC address to a byte array.
367 *
368 * @param macAddress
369 * @return the bytes of the mac address
370 */
371 public static byte[] toByteArray(final long macAddress) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700372 return MacAddress.valueOf(macAddress).toBytes();
alshabibc4901cd2014-09-05 16:50:40 -0700373 }
374
375 /*
376 * (non-Javadoc)
377 *
378 * @see java.lang.Object#hashCode()
379 */
380 @Override
381 public int hashCode() {
382 final int prime = 7867;
383 int result = super.hashCode();
384 result = prime * result + this.destinationMACAddress.hashCode();
385 result = prime * result + this.etherType;
386 result = prime * result + this.vlanID;
387 result = prime * result + this.priorityCode;
388 result = prime * result + (this.pad ? 1231 : 1237);
389 result = prime * result + this.sourceMACAddress.hashCode();
390 return result;
391 }
392
393 /*
394 * (non-Javadoc)
395 *
396 * @see java.lang.Object#equals(java.lang.Object)
397 */
398 @Override
399 public boolean equals(final Object obj) {
400 if (this == obj) {
401 return true;
402 }
403 if (!super.equals(obj)) {
404 return false;
405 }
406 if (!(obj instanceof Ethernet)) {
407 return false;
408 }
409 final Ethernet other = (Ethernet) obj;
410 if (!this.destinationMACAddress.equals(other.destinationMACAddress)) {
411 return false;
412 }
413 if (this.priorityCode != other.priorityCode) {
414 return false;
415 }
416 if (this.vlanID != other.vlanID) {
417 return false;
418 }
419 if (this.etherType != other.etherType) {
420 return false;
421 }
422 if (this.pad != other.pad) {
423 return false;
424 }
425 if (!this.sourceMACAddress.equals(other.sourceMACAddress)) {
426 return false;
427 }
428 return true;
429 }
430
431 /*
432 * (non-Javadoc)
433 *
434 * @see java.lang.Object#toString(java.lang.Object)
435 */
436 @Override
437 public String toString() {
438
439 final StringBuffer sb = new StringBuffer("\n");
440
441 final IPacket pkt = this.getPayload();
442
443 if (pkt instanceof ARP) {
444 sb.append("arp");
445 } else if (pkt instanceof LLDP) {
446 sb.append("lldp");
447 } else if (pkt instanceof ICMP) {
448 sb.append("icmp");
449 } else if (pkt instanceof IPv4) {
450 sb.append("ip");
451 } else if (pkt instanceof DHCP) {
452 sb.append("dhcp");
453 } else {
454 sb.append(this.getEtherType());
455 }
456
457 sb.append("\ndl_vlan: ");
458 if (this.getVlanID() == Ethernet.VLAN_UNTAGGED) {
459 sb.append("untagged");
460 } else {
461 sb.append(this.getVlanID());
462 }
463 sb.append("\ndl_vlan_pcp: ");
464 sb.append(this.getPriorityCode());
465 sb.append("\ndl_src: ");
466 sb.append(bytesToHex(this.getSourceMACAddress()));
467 sb.append("\ndl_dst: ");
468 sb.append(bytesToHex(this.getDestinationMACAddress()));
469
470 if (pkt instanceof ARP) {
471 final ARP p = (ARP) pkt;
472 sb.append("\nnw_src: ");
473 sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p
474 .getSenderProtocolAddress())));
475 sb.append("\nnw_dst: ");
476 sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p
477 .getTargetProtocolAddress())));
478 } else if (pkt instanceof LLDP) {
479 sb.append("lldp packet");
480 } else if (pkt instanceof ICMP) {
481 final ICMP icmp = (ICMP) pkt;
482 sb.append("\nicmp_type: ");
483 sb.append(icmp.getIcmpType());
484 sb.append("\nicmp_code: ");
485 sb.append(icmp.getIcmpCode());
486 } else if (pkt instanceof IPv4) {
487 final IPv4 p = (IPv4) pkt;
488 sb.append("\nnw_src: ");
489 sb.append(IPv4.fromIPv4Address(p.getSourceAddress()));
490 sb.append("\nnw_dst: ");
491 sb.append(IPv4.fromIPv4Address(p.getDestinationAddress()));
492 sb.append("\nnw_tos: ");
493 sb.append(p.getDiffServ());
494 sb.append("\nnw_proto: ");
495 sb.append(p.getProtocol());
496
497 if (pkt instanceof TCP) {
498 sb.append("\ntp_src: ");
499 sb.append(((TCP) pkt).getSourcePort());
500 sb.append("\ntp_dst: ");
501 sb.append(((TCP) pkt).getDestinationPort());
502
503 } else if (pkt instanceof UDP) {
504 sb.append("\ntp_src: ");
505 sb.append(((UDP) pkt).getSourcePort());
506 sb.append("\ntp_dst: ");
507 sb.append(((UDP) pkt).getDestinationPort());
508 }
509
510 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 }
517
518 } else if (pkt instanceof DHCP) {
519 sb.append("\ndhcp packet");
520 } else if (pkt instanceof Data) {
521 sb.append("\ndata packet");
522 } else if (pkt instanceof LLC) {
523 sb.append("\nllc packet");
524 } else {
tom5f18cf32014-09-13 14:10:57 -0700525 sb.append("\nunknown packet");
alshabibc4901cd2014-09-05 16:50:40 -0700526 }
527
528 return sb.toString();
529 }
530
531 public static String bytesToHex(byte[] in) {
532 final StringBuilder builder = new StringBuilder();
533 for (byte b : in) {
534 builder.append(String.format("%02x", b));
535 }
536 return builder.toString();
537 }
538
539}