blob: eecdb53e46e7cb492c6a7a8657fa038bb9ed5a6a [file] [log] [blame]
alshabibc4901cd2014-09-05 16:50:40 -07001/*******************************************************************************
2 * Copyright 2014 Open Networking Laboratory
3 *
4 * 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
7 *
8 * 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.
15 ******************************************************************************/
16/**
17 * Copyright 2011, Big Switch Networks, Inc.
18 * Originally created by David Erickson, Stanford University
19 *
20 * Licensed under the Apache License, Version 2.0 (the "License"); you may
21 * not use this file except in compliance with the License. You may obtain
22 * a copy of the License at
23 *
24 * http://www.apache.org/licenses/LICENSE-2.0
25 *
26 * Unless required by applicable law or agreed to in writing, software
27 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
28 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
29 * License for the specific language governing permissions and limitations
30 * under the License.
31 **/
32
33package org.onlab.packet;
34
35import java.nio.ByteBuffer;
36import java.util.Arrays;
37import java.util.HashMap;
38import java.util.Map;
39
40
41/**
42 *
alshabibc4901cd2014-09-05 16:50:40 -070043 */
44public class Ethernet extends BasePacket {
45 private static final String HEXES = "0123456789ABCDEF";
46 public static final short TYPE_ARP = 0x0806;
47 public static final short TYPE_RARP = (short) 0x8035;
48 public static final short TYPE_IPV4 = 0x0800;
49 public static final short TYPE_LLDP = (short) 0x88cc;
50 public static final short TYPE_BSN = (short) 0x8942;
51 public static final short VLAN_UNTAGGED = (short) 0xffff;
52 public static final short DATALAYER_ADDRESS_LENGTH = 6; // bytes
53 public static Map<Short, Class<? extends IPacket>> etherTypeClassMap;
54
55 static {
56 Ethernet.etherTypeClassMap = new HashMap<Short, Class<? extends IPacket>>();
57 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_ARP, ARP.class);
58 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_RARP, ARP.class);
59 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_IPV4, IPv4.class);
60 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_LLDP, LLDP.class);
alshabib7911a052014-10-16 17:49:37 -070061 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_BSN, LLDP.class);
alshabibc4901cd2014-09-05 16:50:40 -070062 }
63
Ayaka Koshibea9c199f2014-09-16 16:21:40 -070064 protected MacAddress destinationMACAddress;
65 protected MacAddress sourceMACAddress;
alshabibc4901cd2014-09-05 16:50:40 -070066 protected byte priorityCode;
67 protected short vlanID;
68 protected short etherType;
69 protected boolean pad = false;
70
71 /**
72 * By default, set Ethernet to untagged.
73 */
74 public Ethernet() {
75 super();
76 this.vlanID = Ethernet.VLAN_UNTAGGED;
77 }
78
79 /**
80 * Gets the destination MAC address.
81 *
82 * @return the destination MAC as a byte array
83 */
84 public byte[] getDestinationMACAddress() {
85 return this.destinationMACAddress.toBytes();
86 }
87
88 /**
89 * Gets the destination MAC address.
90 *
91 * @return the destination MAC
92 */
Ayaka Koshibea9c199f2014-09-16 16:21:40 -070093 public MacAddress getDestinationMAC() {
alshabibc4901cd2014-09-05 16:50:40 -070094 return this.destinationMACAddress;
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 byte[] 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 * Sets the destination MAC address.
110 *
tom5f18cf32014-09-13 14:10:57 -0700111 * @param destMac the destination MAC to set
alshabibc4901cd2014-09-05 16:50:40 -0700112 * @return the Ethernet frame
113 */
114 public Ethernet setDestinationMACAddress(final String destMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700115 this.destinationMACAddress = MacAddress.valueOf(destMac);
alshabibc4901cd2014-09-05 16:50:40 -0700116 return this;
117 }
118
119 /**
120 * Gets the source MAC address.
121 *
122 * @return the source MACAddress as a byte array
123 */
124 public byte[] getSourceMACAddress() {
125 return this.sourceMACAddress.toBytes();
126 }
127
128 /**
129 * Gets the source MAC address.
130 *
131 * @return the source MACAddress
132 */
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700133 public MacAddress getSourceMAC() {
alshabibc4901cd2014-09-05 16:50:40 -0700134 return this.sourceMACAddress;
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 byte[] 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 * Sets the source MAC address.
150 *
tom5f18cf32014-09-13 14:10:57 -0700151 * @param sourceMac the source MAC to set
alshabibc4901cd2014-09-05 16:50:40 -0700152 * @return the Ethernet frame
153 */
154 public Ethernet setSourceMACAddress(final String sourceMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700155 this.sourceMACAddress = MacAddress.valueOf(sourceMac);
alshabibc4901cd2014-09-05 16:50:40 -0700156 return this;
157 }
158
159 /**
160 * Gets the priority code.
161 *
162 * @return the priorityCode
163 */
164 public byte getPriorityCode() {
165 return this.priorityCode;
166 }
167
168 /**
169 * Sets the priority code.
170 *
tom5f18cf32014-09-13 14:10:57 -0700171 * @param priority the priorityCode to set
alshabibc4901cd2014-09-05 16:50:40 -0700172 * @return the Ethernet frame
173 */
174 public Ethernet setPriorityCode(final byte priority) {
175 this.priorityCode = priority;
176 return this;
177 }
178
179 /**
180 * Gets the VLAN ID.
181 *
182 * @return the vlanID
183 */
184 public short getVlanID() {
185 return this.vlanID;
186 }
187
188 /**
189 * Sets the VLAN ID.
190 *
tom5f18cf32014-09-13 14:10:57 -0700191 * @param vlan the vlanID to set
alshabibc4901cd2014-09-05 16:50:40 -0700192 * @return the Ethernet frame
193 */
194 public Ethernet setVlanID(final short vlan) {
195 this.vlanID = vlan;
196 return this;
197 }
198
199 /**
200 * Gets the Ethernet type.
201 *
202 * @return the etherType
203 */
204 public short getEtherType() {
205 return this.etherType;
206 }
207
208 /**
209 * Sets the Ethernet type.
210 *
tom5f18cf32014-09-13 14:10:57 -0700211 * @param ethType the etherType to set
alshabibc4901cd2014-09-05 16:50:40 -0700212 * @return the Ethernet frame
213 */
214 public Ethernet setEtherType(final short ethType) {
215 this.etherType = ethType;
216 return this;
217 }
218
219 /**
220 * @return True if the Ethernet frame is broadcast, false otherwise
221 */
222 public boolean isBroadcast() {
223 assert this.destinationMACAddress.length() == 6;
224 return this.destinationMACAddress.isBroadcast();
225 }
226
227 /**
228 * @return True is the Ethernet frame is multicast, False otherwise
229 */
230 public boolean isMulticast() {
231 return this.destinationMACAddress.isMulticast();
232 }
233
234 /**
235 * Pad this packet to 60 bytes minimum, filling with zeros?
236 *
237 * @return the pad
238 */
239 public boolean isPad() {
240 return this.pad;
241 }
242
243 /**
244 * Pad this packet to 60 bytes minimum, filling with zeros?
245 *
tom5f18cf32014-09-13 14:10:57 -0700246 * @param pd
alshabibc4901cd2014-09-05 16:50:40 -0700247 * the pad to set
248 */
249 public Ethernet setPad(final boolean pd) {
250 this.pad = pd;
251 return this;
252 }
253
254 @Override
255 public byte[] serialize() {
256 byte[] payloadData = null;
257 if (this.payload != null) {
258 this.payload.setParent(this);
259 payloadData = this.payload.serialize();
260 }
261 int length = 14 + (this.vlanID == Ethernet.VLAN_UNTAGGED ? 0 : 4)
262 + (payloadData == null ? 0 : payloadData.length);
263 if (this.pad && length < 60) {
264 length = 60;
265 }
266 final byte[] data = new byte[length];
267 final ByteBuffer bb = ByteBuffer.wrap(data);
268 bb.put(this.destinationMACAddress.toBytes());
269 bb.put(this.sourceMACAddress.toBytes());
270 if (this.vlanID != Ethernet.VLAN_UNTAGGED) {
271 bb.putShort((short) 0x8100);
272 bb.putShort((short) (this.priorityCode << 13 | this.vlanID & 0x0fff));
273 }
274 bb.putShort(this.etherType);
275 if (payloadData != null) {
276 bb.put(payloadData);
277 }
278 if (this.pad) {
279 Arrays.fill(data, bb.position(), data.length, (byte) 0x0);
280 }
281 return data;
282 }
283
284 @Override
285 public IPacket deserialize(final byte[] data, final int offset,
286 final int length) {
287 if (length <= 0) {
288 return null;
289 }
290 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
291 if (this.destinationMACAddress == null) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700292 this.destinationMACAddress = MacAddress.valueOf(new byte[6]);
alshabibc4901cd2014-09-05 16:50:40 -0700293 }
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700294 final byte[] dstAddr = new byte[MacAddress.MAC_ADDRESS_LENGTH];
alshabibc4901cd2014-09-05 16:50:40 -0700295 bb.get(dstAddr);
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700296 this.destinationMACAddress = MacAddress.valueOf(dstAddr);
alshabibc4901cd2014-09-05 16:50:40 -0700297
298 if (this.sourceMACAddress == null) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700299 this.sourceMACAddress = MacAddress.valueOf(new byte[6]);
alshabibc4901cd2014-09-05 16:50:40 -0700300 }
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700301 final byte[] srcAddr = new byte[MacAddress.MAC_ADDRESS_LENGTH];
alshabibc4901cd2014-09-05 16:50:40 -0700302 bb.get(srcAddr);
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700303 this.sourceMACAddress = MacAddress.valueOf(srcAddr);
alshabibc4901cd2014-09-05 16:50:40 -0700304
305 short ethType = bb.getShort();
306 if (ethType == (short) 0x8100) {
307 final short tci = bb.getShort();
308 this.priorityCode = (byte) (tci >> 13 & 0x07);
309 this.vlanID = (short) (tci & 0x0fff);
310 ethType = bb.getShort();
311 } else {
312 this.vlanID = Ethernet.VLAN_UNTAGGED;
313 }
314 this.etherType = ethType;
315
316 IPacket payload;
317 if (Ethernet.etherTypeClassMap.containsKey(this.etherType)) {
318 final Class<? extends IPacket> clazz = Ethernet.etherTypeClassMap
319 .get(this.etherType);
320 try {
321 payload = clazz.newInstance();
322 } catch (final Exception e) {
323 throw new RuntimeException(
324 "Error parsing payload for Ethernet packet", e);
325 }
326 } else {
327 payload = new Data();
328 }
329 this.payload = payload.deserialize(data, bb.position(),
330 bb.limit() - bb.position());
331 this.payload.setParent(this);
332 return this;
333 }
334
335 /**
336 * Checks to see if a string is a valid MAC address.
337 *
338 * @param macAddress
339 * @return True if macAddress is a valid MAC, False otherwise
340 */
341 public static boolean isMACAddress(final String macAddress) {
342 final String[] macBytes = macAddress.split(":");
343 if (macBytes.length != 6) {
344 return false;
345 }
346 for (int i = 0; i < 6; ++i) {
347 if (Ethernet.HEXES.indexOf(macBytes[i].toUpperCase().charAt(0)) == -1
348 || Ethernet.HEXES.indexOf(macBytes[i].toUpperCase().charAt(
349 1)) == -1) {
350 return false;
351 }
352 }
353 return true;
354 }
355
356 /**
357 * Accepts a MAC address of the form 00:aa:11:bb:22:cc, case does not
358 * matter, and returns a corresponding byte[].
359 *
360 * @param macAddress
361 * The MAC address to convert into a bye array
362 * @return The macAddress as a byte array
363 */
364 public static byte[] toMACAddress(final String macAddress) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700365 return MacAddress.valueOf(macAddress).toBytes();
alshabibc4901cd2014-09-05 16:50:40 -0700366 }
367
368 /**
369 * Accepts a MAC address and returns the corresponding long, where the MAC
370 * bytes are set on the lower order bytes of the long.
371 *
372 * @param macAddress
373 * @return a long containing the mac address bytes
374 */
375 public static long toLong(final byte[] macAddress) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700376 return MacAddress.valueOf(macAddress).toLong();
alshabibc4901cd2014-09-05 16:50:40 -0700377 }
378
379 /**
380 * Converts a long MAC address to a byte array.
381 *
382 * @param macAddress
383 * @return the bytes of the mac address
384 */
385 public static byte[] toByteArray(final long macAddress) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700386 return MacAddress.valueOf(macAddress).toBytes();
alshabibc4901cd2014-09-05 16:50:40 -0700387 }
388
389 /*
390 * (non-Javadoc)
391 *
392 * @see java.lang.Object#hashCode()
393 */
394 @Override
395 public int hashCode() {
396 final int prime = 7867;
397 int result = super.hashCode();
398 result = prime * result + this.destinationMACAddress.hashCode();
399 result = prime * result + this.etherType;
400 result = prime * result + this.vlanID;
401 result = prime * result + this.priorityCode;
402 result = prime * result + (this.pad ? 1231 : 1237);
403 result = prime * result + this.sourceMACAddress.hashCode();
404 return result;
405 }
406
407 /*
408 * (non-Javadoc)
409 *
410 * @see java.lang.Object#equals(java.lang.Object)
411 */
412 @Override
413 public boolean equals(final Object obj) {
414 if (this == obj) {
415 return true;
416 }
417 if (!super.equals(obj)) {
418 return false;
419 }
420 if (!(obj instanceof Ethernet)) {
421 return false;
422 }
423 final Ethernet other = (Ethernet) obj;
424 if (!this.destinationMACAddress.equals(other.destinationMACAddress)) {
425 return false;
426 }
427 if (this.priorityCode != other.priorityCode) {
428 return false;
429 }
430 if (this.vlanID != other.vlanID) {
431 return false;
432 }
433 if (this.etherType != other.etherType) {
434 return false;
435 }
436 if (this.pad != other.pad) {
437 return false;
438 }
439 if (!this.sourceMACAddress.equals(other.sourceMACAddress)) {
440 return false;
441 }
442 return true;
443 }
444
445 /*
446 * (non-Javadoc)
447 *
448 * @see java.lang.Object#toString(java.lang.Object)
449 */
450 @Override
451 public String toString() {
452
453 final StringBuffer sb = new StringBuffer("\n");
454
455 final IPacket pkt = this.getPayload();
456
457 if (pkt instanceof ARP) {
458 sb.append("arp");
459 } else if (pkt instanceof LLDP) {
460 sb.append("lldp");
461 } else if (pkt instanceof ICMP) {
462 sb.append("icmp");
463 } else if (pkt instanceof IPv4) {
464 sb.append("ip");
465 } else if (pkt instanceof DHCP) {
466 sb.append("dhcp");
467 } else {
468 sb.append(this.getEtherType());
469 }
470
471 sb.append("\ndl_vlan: ");
472 if (this.getVlanID() == Ethernet.VLAN_UNTAGGED) {
473 sb.append("untagged");
474 } else {
475 sb.append(this.getVlanID());
476 }
477 sb.append("\ndl_vlan_pcp: ");
478 sb.append(this.getPriorityCode());
479 sb.append("\ndl_src: ");
480 sb.append(bytesToHex(this.getSourceMACAddress()));
481 sb.append("\ndl_dst: ");
482 sb.append(bytesToHex(this.getDestinationMACAddress()));
483
484 if (pkt instanceof ARP) {
485 final ARP p = (ARP) pkt;
486 sb.append("\nnw_src: ");
487 sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p
488 .getSenderProtocolAddress())));
489 sb.append("\nnw_dst: ");
490 sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p
491 .getTargetProtocolAddress())));
492 } else if (pkt instanceof LLDP) {
493 sb.append("lldp packet");
494 } else if (pkt instanceof ICMP) {
495 final ICMP icmp = (ICMP) pkt;
496 sb.append("\nicmp_type: ");
497 sb.append(icmp.getIcmpType());
498 sb.append("\nicmp_code: ");
499 sb.append(icmp.getIcmpCode());
500 } else if (pkt instanceof IPv4) {
501 final IPv4 p = (IPv4) pkt;
502 sb.append("\nnw_src: ");
503 sb.append(IPv4.fromIPv4Address(p.getSourceAddress()));
504 sb.append("\nnw_dst: ");
505 sb.append(IPv4.fromIPv4Address(p.getDestinationAddress()));
506 sb.append("\nnw_tos: ");
507 sb.append(p.getDiffServ());
508 sb.append("\nnw_proto: ");
509 sb.append(p.getProtocol());
510
511 if (pkt instanceof TCP) {
512 sb.append("\ntp_src: ");
513 sb.append(((TCP) pkt).getSourcePort());
514 sb.append("\ntp_dst: ");
515 sb.append(((TCP) pkt).getDestinationPort());
516
517 } else if (pkt instanceof UDP) {
518 sb.append("\ntp_src: ");
519 sb.append(((UDP) pkt).getSourcePort());
520 sb.append("\ntp_dst: ");
521 sb.append(((UDP) pkt).getDestinationPort());
522 }
523
524 if (pkt instanceof ICMP) {
525 final ICMP icmp = (ICMP) pkt;
526 sb.append("\nicmp_type: ");
527 sb.append(icmp.getIcmpType());
528 sb.append("\nicmp_code: ");
529 sb.append(icmp.getIcmpCode());
530 }
531
532 } else if (pkt instanceof DHCP) {
533 sb.append("\ndhcp packet");
534 } else if (pkt instanceof Data) {
535 sb.append("\ndata packet");
536 } else if (pkt instanceof LLC) {
537 sb.append("\nllc packet");
538 } else {
tom5f18cf32014-09-13 14:10:57 -0700539 sb.append("\nunknown packet");
alshabibc4901cd2014-09-05 16:50:40 -0700540 }
541
542 return sb.toString();
543 }
544
545 public static String bytesToHex(byte[] in) {
546 final StringBuilder builder = new StringBuilder();
547 for (byte b : in) {
548 builder.append(String.format("%02x", b));
549 }
550 return builder.toString();
551 }
552
553}