blob: 9e55c7016df50ab7e58c79ffbe78dc1bc66299a6 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
alshabibc4901cd2014-09-05 16:50:40 -07009 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
Thomas Vachuska24c849c2014-10-27 09:53:05 -070012 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
alshabibc4901cd2014-09-05 16:50:40 -070017 * under the License.
Thomas Vachuska24c849c2014-10-27 09:53:05 -070018 */
19
20
alshabibc4901cd2014-09-05 16:50:40 -070021
22package org.onlab.packet;
23
24import java.nio.ByteBuffer;
25import java.util.Arrays;
26import java.util.HashMap;
27import java.util.Map;
28
29
30/**
31 *
alshabibc4901cd2014-09-05 16:50:40 -070032 */
33public class Ethernet extends BasePacket {
34 private static final String HEXES = "0123456789ABCDEF";
35 public static final short TYPE_ARP = 0x0806;
36 public static final short TYPE_RARP = (short) 0x8035;
37 public static final short TYPE_IPV4 = 0x0800;
38 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;
41 public static final short DATALAYER_ADDRESS_LENGTH = 6; // bytes
42 public static Map<Short, Class<? extends IPacket>> etherTypeClassMap;
43
44 static {
45 Ethernet.etherTypeClassMap = new HashMap<Short, Class<? extends IPacket>>();
46 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_ARP, ARP.class);
47 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_RARP, ARP.class);
48 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_IPV4, IPv4.class);
49 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_LLDP, LLDP.class);
alshabib7911a052014-10-16 17:49:37 -070050 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_BSN, LLDP.class);
alshabibc4901cd2014-09-05 16:50:40 -070051 }
52
Ayaka Koshibea9c199f2014-09-16 16:21:40 -070053 protected MacAddress destinationMACAddress;
54 protected MacAddress sourceMACAddress;
alshabibc4901cd2014-09-05 16:50:40 -070055 protected byte priorityCode;
56 protected short vlanID;
57 protected short etherType;
58 protected boolean pad = false;
59
60 /**
61 * By default, set Ethernet to untagged.
62 */
63 public Ethernet() {
64 super();
65 this.vlanID = Ethernet.VLAN_UNTAGGED;
66 }
67
68 /**
69 * Gets the destination MAC address.
70 *
71 * @return the destination MAC as a byte array
72 */
73 public byte[] getDestinationMACAddress() {
74 return this.destinationMACAddress.toBytes();
75 }
76
77 /**
78 * Gets the destination MAC address.
79 *
80 * @return the destination MAC
81 */
Ayaka Koshibea9c199f2014-09-16 16:21:40 -070082 public MacAddress getDestinationMAC() {
alshabibc4901cd2014-09-05 16:50:40 -070083 return this.destinationMACAddress;
84 }
85
86 /**
87 * Sets the destination MAC address.
88 *
tom5f18cf32014-09-13 14:10:57 -070089 * @param destMac the destination MAC to set
alshabibc4901cd2014-09-05 16:50:40 -070090 * @return the Ethernet frame
91 */
92 public Ethernet setDestinationMACAddress(final byte[] destMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -070093 this.destinationMACAddress = MacAddress.valueOf(destMac);
alshabibc4901cd2014-09-05 16:50:40 -070094 return this;
95 }
96
97 /**
98 * Sets the destination MAC address.
99 *
tom5f18cf32014-09-13 14:10:57 -0700100 * @param destMac the destination MAC to set
alshabibc4901cd2014-09-05 16:50:40 -0700101 * @return the Ethernet frame
102 */
103 public Ethernet setDestinationMACAddress(final String destMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700104 this.destinationMACAddress = MacAddress.valueOf(destMac);
alshabibc4901cd2014-09-05 16:50:40 -0700105 return this;
106 }
107
108 /**
109 * Gets the source MAC address.
110 *
111 * @return the source MACAddress as a byte array
112 */
113 public byte[] getSourceMACAddress() {
114 return this.sourceMACAddress.toBytes();
115 }
116
117 /**
118 * Gets the source MAC address.
119 *
120 * @return the source MACAddress
121 */
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700122 public MacAddress getSourceMAC() {
alshabibc4901cd2014-09-05 16:50:40 -0700123 return this.sourceMACAddress;
124 }
125
126 /**
127 * Sets the source MAC address.
128 *
tom5f18cf32014-09-13 14:10:57 -0700129 * @param sourceMac the source MAC to set
alshabibc4901cd2014-09-05 16:50:40 -0700130 * @return the Ethernet frame
131 */
132 public Ethernet setSourceMACAddress(final byte[] sourceMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700133 this.sourceMACAddress = MacAddress.valueOf(sourceMac);
alshabibc4901cd2014-09-05 16:50:40 -0700134 return this;
135 }
136
137 /**
138 * Sets the source MAC address.
139 *
tom5f18cf32014-09-13 14:10:57 -0700140 * @param sourceMac the source MAC to set
alshabibc4901cd2014-09-05 16:50:40 -0700141 * @return the Ethernet frame
142 */
143 public Ethernet setSourceMACAddress(final String sourceMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700144 this.sourceMACAddress = MacAddress.valueOf(sourceMac);
alshabibc4901cd2014-09-05 16:50:40 -0700145 return this;
146 }
147
148 /**
149 * Gets the priority code.
150 *
151 * @return the priorityCode
152 */
153 public byte getPriorityCode() {
154 return this.priorityCode;
155 }
156
157 /**
158 * Sets the priority code.
159 *
tom5f18cf32014-09-13 14:10:57 -0700160 * @param priority the priorityCode to set
alshabibc4901cd2014-09-05 16:50:40 -0700161 * @return the Ethernet frame
162 */
163 public Ethernet setPriorityCode(final byte priority) {
164 this.priorityCode = priority;
165 return this;
166 }
167
168 /**
169 * Gets the VLAN ID.
170 *
171 * @return the vlanID
172 */
173 public short getVlanID() {
174 return this.vlanID;
175 }
176
177 /**
178 * Sets the VLAN ID.
179 *
tom5f18cf32014-09-13 14:10:57 -0700180 * @param vlan the vlanID to set
alshabibc4901cd2014-09-05 16:50:40 -0700181 * @return the Ethernet frame
182 */
183 public Ethernet setVlanID(final short vlan) {
184 this.vlanID = vlan;
185 return this;
186 }
187
188 /**
189 * Gets the Ethernet type.
190 *
191 * @return the etherType
192 */
193 public short getEtherType() {
194 return this.etherType;
195 }
196
197 /**
198 * Sets the Ethernet type.
199 *
tom5f18cf32014-09-13 14:10:57 -0700200 * @param ethType the etherType to set
alshabibc4901cd2014-09-05 16:50:40 -0700201 * @return the Ethernet frame
202 */
203 public Ethernet setEtherType(final short ethType) {
204 this.etherType = ethType;
205 return this;
206 }
207
208 /**
209 * @return True if the Ethernet frame is broadcast, false otherwise
210 */
211 public boolean isBroadcast() {
212 assert this.destinationMACAddress.length() == 6;
213 return this.destinationMACAddress.isBroadcast();
214 }
215
216 /**
217 * @return True is the Ethernet frame is multicast, False otherwise
218 */
219 public boolean isMulticast() {
220 return this.destinationMACAddress.isMulticast();
221 }
222
223 /**
224 * Pad this packet to 60 bytes minimum, filling with zeros?
225 *
226 * @return the pad
227 */
228 public boolean isPad() {
229 return this.pad;
230 }
231
232 /**
233 * Pad this packet to 60 bytes minimum, filling with zeros?
234 *
tom5f18cf32014-09-13 14:10:57 -0700235 * @param pd
alshabibc4901cd2014-09-05 16:50:40 -0700236 * the pad to set
237 */
238 public Ethernet setPad(final boolean pd) {
239 this.pad = pd;
240 return this;
241 }
242
243 @Override
244 public byte[] serialize() {
245 byte[] payloadData = null;
246 if (this.payload != null) {
247 this.payload.setParent(this);
248 payloadData = this.payload.serialize();
249 }
250 int length = 14 + (this.vlanID == Ethernet.VLAN_UNTAGGED ? 0 : 4)
251 + (payloadData == null ? 0 : payloadData.length);
252 if (this.pad && length < 60) {
253 length = 60;
254 }
255 final byte[] data = new byte[length];
256 final ByteBuffer bb = ByteBuffer.wrap(data);
257 bb.put(this.destinationMACAddress.toBytes());
258 bb.put(this.sourceMACAddress.toBytes());
259 if (this.vlanID != Ethernet.VLAN_UNTAGGED) {
260 bb.putShort((short) 0x8100);
261 bb.putShort((short) (this.priorityCode << 13 | this.vlanID & 0x0fff));
262 }
263 bb.putShort(this.etherType);
264 if (payloadData != null) {
265 bb.put(payloadData);
266 }
267 if (this.pad) {
268 Arrays.fill(data, bb.position(), data.length, (byte) 0x0);
269 }
270 return data;
271 }
272
273 @Override
274 public IPacket deserialize(final byte[] data, final int offset,
275 final int length) {
276 if (length <= 0) {
277 return null;
278 }
279 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
280 if (this.destinationMACAddress == null) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700281 this.destinationMACAddress = MacAddress.valueOf(new byte[6]);
alshabibc4901cd2014-09-05 16:50:40 -0700282 }
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700283 final byte[] dstAddr = new byte[MacAddress.MAC_ADDRESS_LENGTH];
alshabibc4901cd2014-09-05 16:50:40 -0700284 bb.get(dstAddr);
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700285 this.destinationMACAddress = MacAddress.valueOf(dstAddr);
alshabibc4901cd2014-09-05 16:50:40 -0700286
287 if (this.sourceMACAddress == null) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700288 this.sourceMACAddress = MacAddress.valueOf(new byte[6]);
alshabibc4901cd2014-09-05 16:50:40 -0700289 }
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700290 final byte[] srcAddr = new byte[MacAddress.MAC_ADDRESS_LENGTH];
alshabibc4901cd2014-09-05 16:50:40 -0700291 bb.get(srcAddr);
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700292 this.sourceMACAddress = MacAddress.valueOf(srcAddr);
alshabibc4901cd2014-09-05 16:50:40 -0700293
294 short ethType = bb.getShort();
295 if (ethType == (short) 0x8100) {
296 final short tci = bb.getShort();
297 this.priorityCode = (byte) (tci >> 13 & 0x07);
298 this.vlanID = (short) (tci & 0x0fff);
299 ethType = bb.getShort();
300 } else {
301 this.vlanID = Ethernet.VLAN_UNTAGGED;
302 }
303 this.etherType = ethType;
304
305 IPacket payload;
306 if (Ethernet.etherTypeClassMap.containsKey(this.etherType)) {
307 final Class<? extends IPacket> clazz = Ethernet.etherTypeClassMap
308 .get(this.etherType);
309 try {
310 payload = clazz.newInstance();
311 } catch (final Exception e) {
312 throw new RuntimeException(
313 "Error parsing payload for Ethernet packet", e);
314 }
315 } else {
316 payload = new Data();
317 }
318 this.payload = payload.deserialize(data, bb.position(),
319 bb.limit() - bb.position());
320 this.payload.setParent(this);
321 return this;
322 }
323
324 /**
325 * Checks to see if a string is a valid MAC address.
326 *
327 * @param macAddress
328 * @return True if macAddress is a valid MAC, False otherwise
329 */
330 public static boolean isMACAddress(final String macAddress) {
331 final String[] macBytes = macAddress.split(":");
332 if (macBytes.length != 6) {
333 return false;
334 }
335 for (int i = 0; i < 6; ++i) {
336 if (Ethernet.HEXES.indexOf(macBytes[i].toUpperCase().charAt(0)) == -1
337 || Ethernet.HEXES.indexOf(macBytes[i].toUpperCase().charAt(
338 1)) == -1) {
339 return false;
340 }
341 }
342 return true;
343 }
344
345 /**
346 * Accepts a MAC address of the form 00:aa:11:bb:22:cc, case does not
347 * matter, and returns a corresponding byte[].
348 *
349 * @param macAddress
350 * The MAC address to convert into a bye array
351 * @return The macAddress as a byte array
352 */
353 public static byte[] toMACAddress(final String macAddress) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700354 return MacAddress.valueOf(macAddress).toBytes();
alshabibc4901cd2014-09-05 16:50:40 -0700355 }
356
357 /**
358 * Accepts a MAC address and returns the corresponding long, where the MAC
359 * bytes are set on the lower order bytes of the long.
360 *
361 * @param macAddress
362 * @return a long containing the mac address bytes
363 */
364 public static long toLong(final byte[] macAddress) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700365 return MacAddress.valueOf(macAddress).toLong();
alshabibc4901cd2014-09-05 16:50:40 -0700366 }
367
368 /**
369 * Converts a long MAC address to a byte array.
370 *
371 * @param macAddress
372 * @return the bytes of the mac address
373 */
374 public static byte[] toByteArray(final long macAddress) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700375 return MacAddress.valueOf(macAddress).toBytes();
alshabibc4901cd2014-09-05 16:50:40 -0700376 }
377
378 /*
379 * (non-Javadoc)
380 *
381 * @see java.lang.Object#hashCode()
382 */
383 @Override
384 public int hashCode() {
385 final int prime = 7867;
386 int result = super.hashCode();
387 result = prime * result + this.destinationMACAddress.hashCode();
388 result = prime * result + this.etherType;
389 result = prime * result + this.vlanID;
390 result = prime * result + this.priorityCode;
391 result = prime * result + (this.pad ? 1231 : 1237);
392 result = prime * result + this.sourceMACAddress.hashCode();
393 return result;
394 }
395
396 /*
397 * (non-Javadoc)
398 *
399 * @see java.lang.Object#equals(java.lang.Object)
400 */
401 @Override
402 public boolean equals(final Object obj) {
403 if (this == obj) {
404 return true;
405 }
406 if (!super.equals(obj)) {
407 return false;
408 }
409 if (!(obj instanceof Ethernet)) {
410 return false;
411 }
412 final Ethernet other = (Ethernet) obj;
413 if (!this.destinationMACAddress.equals(other.destinationMACAddress)) {
414 return false;
415 }
416 if (this.priorityCode != other.priorityCode) {
417 return false;
418 }
419 if (this.vlanID != other.vlanID) {
420 return false;
421 }
422 if (this.etherType != other.etherType) {
423 return false;
424 }
425 if (this.pad != other.pad) {
426 return false;
427 }
428 if (!this.sourceMACAddress.equals(other.sourceMACAddress)) {
429 return false;
430 }
431 return true;
432 }
433
434 /*
435 * (non-Javadoc)
436 *
437 * @see java.lang.Object#toString(java.lang.Object)
438 */
439 @Override
440 public String toString() {
441
442 final StringBuffer sb = new StringBuffer("\n");
443
444 final IPacket pkt = this.getPayload();
445
446 if (pkt instanceof ARP) {
447 sb.append("arp");
448 } else if (pkt instanceof LLDP) {
449 sb.append("lldp");
450 } else if (pkt instanceof ICMP) {
451 sb.append("icmp");
452 } else if (pkt instanceof IPv4) {
453 sb.append("ip");
454 } else if (pkt instanceof DHCP) {
455 sb.append("dhcp");
456 } else {
457 sb.append(this.getEtherType());
458 }
459
460 sb.append("\ndl_vlan: ");
461 if (this.getVlanID() == Ethernet.VLAN_UNTAGGED) {
462 sb.append("untagged");
463 } else {
464 sb.append(this.getVlanID());
465 }
466 sb.append("\ndl_vlan_pcp: ");
467 sb.append(this.getPriorityCode());
468 sb.append("\ndl_src: ");
469 sb.append(bytesToHex(this.getSourceMACAddress()));
470 sb.append("\ndl_dst: ");
471 sb.append(bytesToHex(this.getDestinationMACAddress()));
472
473 if (pkt instanceof ARP) {
474 final ARP p = (ARP) pkt;
475 sb.append("\nnw_src: ");
476 sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p
477 .getSenderProtocolAddress())));
478 sb.append("\nnw_dst: ");
479 sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p
480 .getTargetProtocolAddress())));
481 } else if (pkt instanceof LLDP) {
482 sb.append("lldp packet");
483 } else if (pkt instanceof ICMP) {
484 final ICMP icmp = (ICMP) pkt;
485 sb.append("\nicmp_type: ");
486 sb.append(icmp.getIcmpType());
487 sb.append("\nicmp_code: ");
488 sb.append(icmp.getIcmpCode());
489 } else if (pkt instanceof IPv4) {
490 final IPv4 p = (IPv4) pkt;
491 sb.append("\nnw_src: ");
492 sb.append(IPv4.fromIPv4Address(p.getSourceAddress()));
493 sb.append("\nnw_dst: ");
494 sb.append(IPv4.fromIPv4Address(p.getDestinationAddress()));
495 sb.append("\nnw_tos: ");
496 sb.append(p.getDiffServ());
497 sb.append("\nnw_proto: ");
498 sb.append(p.getProtocol());
499
500 if (pkt instanceof TCP) {
501 sb.append("\ntp_src: ");
502 sb.append(((TCP) pkt).getSourcePort());
503 sb.append("\ntp_dst: ");
504 sb.append(((TCP) pkt).getDestinationPort());
505
506 } else if (pkt instanceof UDP) {
507 sb.append("\ntp_src: ");
508 sb.append(((UDP) pkt).getSourcePort());
509 sb.append("\ntp_dst: ");
510 sb.append(((UDP) pkt).getDestinationPort());
511 }
512
513 if (pkt instanceof ICMP) {
514 final ICMP icmp = (ICMP) pkt;
515 sb.append("\nicmp_type: ");
516 sb.append(icmp.getIcmpType());
517 sb.append("\nicmp_code: ");
518 sb.append(icmp.getIcmpCode());
519 }
520
521 } else if (pkt instanceof DHCP) {
522 sb.append("\ndhcp packet");
523 } else if (pkt instanceof Data) {
524 sb.append("\ndata packet");
525 } else if (pkt instanceof LLC) {
526 sb.append("\nllc packet");
527 } else {
tom5f18cf32014-09-13 14:10:57 -0700528 sb.append("\nunknown packet");
alshabibc4901cd2014-09-05 16:50:40 -0700529 }
530
531 return sb.toString();
532 }
533
534 public static String bytesToHex(byte[] in) {
535 final StringBuilder builder = new StringBuilder();
536 for (byte b : in) {
537 builder.append(String.format("%02x", b));
538 }
539 return builder.toString();
540 }
541
542}