blob: 35193387176d8c62fd1ce05294753de00d851b7c [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;
40 public static final short DATALAYER_ADDRESS_LENGTH = 6; // bytes
41 public static Map<Short, Class<? extends IPacket>> etherTypeClassMap;
42
43 static {
44 Ethernet.etherTypeClassMap = new HashMap<Short, Class<? extends IPacket>>();
45 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_ARP, ARP.class);
46 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_RARP, ARP.class);
47 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_IPV4, IPv4.class);
48 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_LLDP, LLDP.class);
alshabib7911a052014-10-16 17:49:37 -070049 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_BSN, LLDP.class);
alshabibc4901cd2014-09-05 16:50:40 -070050 }
51
Ayaka Koshibea9c199f2014-09-16 16:21:40 -070052 protected MacAddress destinationMACAddress;
53 protected MacAddress sourceMACAddress;
alshabibc4901cd2014-09-05 16:50:40 -070054 protected byte priorityCode;
55 protected short vlanID;
56 protected short etherType;
57 protected boolean pad = false;
58
59 /**
60 * By default, set Ethernet to untagged.
61 */
62 public Ethernet() {
63 super();
64 this.vlanID = Ethernet.VLAN_UNTAGGED;
65 }
66
67 /**
68 * Gets the destination MAC address.
69 *
70 * @return the destination MAC as a byte array
71 */
72 public byte[] getDestinationMACAddress() {
73 return this.destinationMACAddress.toBytes();
74 }
75
76 /**
77 * Gets the destination MAC address.
78 *
79 * @return the destination MAC
80 */
Ayaka Koshibea9c199f2014-09-16 16:21:40 -070081 public MacAddress getDestinationMAC() {
alshabibc4901cd2014-09-05 16:50:40 -070082 return this.destinationMACAddress;
83 }
84
85 /**
86 * Sets the destination MAC address.
87 *
tom5f18cf32014-09-13 14:10:57 -070088 * @param destMac the destination MAC to set
alshabibc4901cd2014-09-05 16:50:40 -070089 * @return the Ethernet frame
90 */
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -080091 public Ethernet setDestinationMACAddress(final MacAddress destMac) {
92 this.destinationMACAddress = checkNotNull(destMac);
93 return this;
94 }
95
96 /**
97 * Sets the destination MAC address.
98 *
99 * @param destMac the destination MAC to set
100 * @return the Ethernet frame
101 */
alshabibc4901cd2014-09-05 16:50:40 -0700102 public Ethernet setDestinationMACAddress(final byte[] destMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700103 this.destinationMACAddress = MacAddress.valueOf(destMac);
alshabibc4901cd2014-09-05 16:50:40 -0700104 return this;
105 }
106
107 /**
108 * Sets the destination MAC address.
109 *
tom5f18cf32014-09-13 14:10:57 -0700110 * @param destMac the destination MAC to set
alshabibc4901cd2014-09-05 16:50:40 -0700111 * @return the Ethernet frame
112 */
113 public Ethernet setDestinationMACAddress(final String destMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700114 this.destinationMACAddress = MacAddress.valueOf(destMac);
alshabibc4901cd2014-09-05 16:50:40 -0700115 return this;
116 }
117
118 /**
119 * Gets the source MAC address.
120 *
121 * @return the source MACAddress as a byte array
122 */
123 public byte[] getSourceMACAddress() {
124 return this.sourceMACAddress.toBytes();
125 }
126
127 /**
128 * Gets the source MAC address.
129 *
130 * @return the source MACAddress
131 */
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700132 public MacAddress getSourceMAC() {
alshabibc4901cd2014-09-05 16:50:40 -0700133 return this.sourceMACAddress;
134 }
135
136 /**
137 * Sets the source MAC address.
138 *
tom5f18cf32014-09-13 14:10:57 -0700139 * @param sourceMac the source MAC to set
alshabibc4901cd2014-09-05 16:50:40 -0700140 * @return the Ethernet frame
141 */
Yuta HIGUCHI3e848a82014-11-02 20:19:42 -0800142 public Ethernet setSourceMACAddress(final MacAddress sourceMac) {
143 this.sourceMACAddress = checkNotNull(sourceMac);
144 return this;
145 }
146
147 /**
148 * Sets the source MAC address.
149 *
150 * @param sourceMac the source MAC to set
151 * @return the Ethernet frame
152 */
alshabibc4901cd2014-09-05 16:50:40 -0700153 public Ethernet setSourceMACAddress(final byte[] sourceMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700154 this.sourceMACAddress = MacAddress.valueOf(sourceMac);
alshabibc4901cd2014-09-05 16:50:40 -0700155 return this;
156 }
157
158 /**
159 * Sets the source MAC address.
160 *
tom5f18cf32014-09-13 14:10:57 -0700161 * @param sourceMac the source MAC to set
alshabibc4901cd2014-09-05 16:50:40 -0700162 * @return the Ethernet frame
163 */
164 public Ethernet setSourceMACAddress(final String sourceMac) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700165 this.sourceMACAddress = MacAddress.valueOf(sourceMac);
alshabibc4901cd2014-09-05 16:50:40 -0700166 return this;
167 }
168
169 /**
170 * Gets the priority code.
171 *
172 * @return the priorityCode
173 */
174 public byte getPriorityCode() {
175 return this.priorityCode;
176 }
177
178 /**
179 * Sets the priority code.
180 *
tom5f18cf32014-09-13 14:10:57 -0700181 * @param priority the priorityCode to set
alshabibc4901cd2014-09-05 16:50:40 -0700182 * @return the Ethernet frame
183 */
184 public Ethernet setPriorityCode(final byte priority) {
185 this.priorityCode = priority;
186 return this;
187 }
188
189 /**
190 * Gets the VLAN ID.
191 *
192 * @return the vlanID
193 */
194 public short getVlanID() {
195 return this.vlanID;
196 }
197
198 /**
199 * Sets the VLAN ID.
200 *
tom5f18cf32014-09-13 14:10:57 -0700201 * @param vlan the vlanID to set
alshabibc4901cd2014-09-05 16:50:40 -0700202 * @return the Ethernet frame
203 */
204 public Ethernet setVlanID(final short vlan) {
205 this.vlanID = vlan;
206 return this;
207 }
208
209 /**
210 * Gets the Ethernet type.
211 *
212 * @return the etherType
213 */
214 public short getEtherType() {
215 return this.etherType;
216 }
217
218 /**
219 * Sets the Ethernet type.
220 *
tom5f18cf32014-09-13 14:10:57 -0700221 * @param ethType the etherType to set
alshabibc4901cd2014-09-05 16:50:40 -0700222 * @return the Ethernet frame
223 */
224 public Ethernet setEtherType(final short ethType) {
225 this.etherType = ethType;
226 return this;
227 }
228
229 /**
230 * @return True if the Ethernet frame is broadcast, false otherwise
231 */
232 public boolean isBroadcast() {
233 assert this.destinationMACAddress.length() == 6;
234 return this.destinationMACAddress.isBroadcast();
235 }
236
237 /**
238 * @return True is the Ethernet frame is multicast, False otherwise
239 */
240 public boolean isMulticast() {
241 return this.destinationMACAddress.isMulticast();
242 }
243
244 /**
245 * Pad this packet to 60 bytes minimum, filling with zeros?
246 *
247 * @return the pad
248 */
249 public boolean isPad() {
250 return this.pad;
251 }
252
253 /**
254 * Pad this packet to 60 bytes minimum, filling with zeros?
255 *
tom5f18cf32014-09-13 14:10:57 -0700256 * @param pd
alshabibc4901cd2014-09-05 16:50:40 -0700257 * the pad to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800258 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700259 */
260 public Ethernet setPad(final boolean pd) {
261 this.pad = pd;
262 return this;
263 }
264
265 @Override
266 public byte[] serialize() {
267 byte[] payloadData = null;
268 if (this.payload != null) {
269 this.payload.setParent(this);
270 payloadData = this.payload.serialize();
271 }
272 int length = 14 + (this.vlanID == Ethernet.VLAN_UNTAGGED ? 0 : 4)
273 + (payloadData == null ? 0 : payloadData.length);
274 if (this.pad && length < 60) {
275 length = 60;
276 }
277 final byte[] data = new byte[length];
278 final ByteBuffer bb = ByteBuffer.wrap(data);
279 bb.put(this.destinationMACAddress.toBytes());
280 bb.put(this.sourceMACAddress.toBytes());
281 if (this.vlanID != Ethernet.VLAN_UNTAGGED) {
282 bb.putShort((short) 0x8100);
283 bb.putShort((short) (this.priorityCode << 13 | this.vlanID & 0x0fff));
284 }
285 bb.putShort(this.etherType);
286 if (payloadData != null) {
287 bb.put(payloadData);
288 }
289 if (this.pad) {
290 Arrays.fill(data, bb.position(), data.length, (byte) 0x0);
291 }
292 return data;
293 }
294
295 @Override
296 public IPacket deserialize(final byte[] data, final int offset,
297 final int length) {
298 if (length <= 0) {
299 return null;
300 }
301 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
302 if (this.destinationMACAddress == null) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700303 this.destinationMACAddress = MacAddress.valueOf(new byte[6]);
alshabibc4901cd2014-09-05 16:50:40 -0700304 }
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700305 final byte[] dstAddr = new byte[MacAddress.MAC_ADDRESS_LENGTH];
alshabibc4901cd2014-09-05 16:50:40 -0700306 bb.get(dstAddr);
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700307 this.destinationMACAddress = MacAddress.valueOf(dstAddr);
alshabibc4901cd2014-09-05 16:50:40 -0700308
309 if (this.sourceMACAddress == null) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700310 this.sourceMACAddress = MacAddress.valueOf(new byte[6]);
alshabibc4901cd2014-09-05 16:50:40 -0700311 }
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700312 final byte[] srcAddr = new byte[MacAddress.MAC_ADDRESS_LENGTH];
alshabibc4901cd2014-09-05 16:50:40 -0700313 bb.get(srcAddr);
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700314 this.sourceMACAddress = MacAddress.valueOf(srcAddr);
alshabibc4901cd2014-09-05 16:50:40 -0700315
316 short ethType = bb.getShort();
317 if (ethType == (short) 0x8100) {
318 final short tci = bb.getShort();
319 this.priorityCode = (byte) (tci >> 13 & 0x07);
320 this.vlanID = (short) (tci & 0x0fff);
321 ethType = bb.getShort();
322 } else {
323 this.vlanID = Ethernet.VLAN_UNTAGGED;
324 }
325 this.etherType = ethType;
326
327 IPacket payload;
328 if (Ethernet.etherTypeClassMap.containsKey(this.etherType)) {
329 final Class<? extends IPacket> clazz = Ethernet.etherTypeClassMap
330 .get(this.etherType);
331 try {
332 payload = clazz.newInstance();
333 } catch (final Exception e) {
334 throw new RuntimeException(
335 "Error parsing payload for Ethernet packet", e);
336 }
337 } else {
338 payload = new Data();
339 }
340 this.payload = payload.deserialize(data, bb.position(),
341 bb.limit() - bb.position());
342 this.payload.setParent(this);
343 return this;
344 }
345
346 /**
347 * Checks to see if a string is a valid MAC address.
348 *
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800349 * @param macAddress string to test if it is a valid MAC
alshabibc4901cd2014-09-05 16:50:40 -0700350 * @return True if macAddress is a valid MAC, False otherwise
351 */
352 public static boolean isMACAddress(final String macAddress) {
353 final String[] macBytes = macAddress.split(":");
354 if (macBytes.length != 6) {
355 return false;
356 }
357 for (int i = 0; i < 6; ++i) {
358 if (Ethernet.HEXES.indexOf(macBytes[i].toUpperCase().charAt(0)) == -1
359 || Ethernet.HEXES.indexOf(macBytes[i].toUpperCase().charAt(
360 1)) == -1) {
361 return false;
362 }
363 }
364 return true;
365 }
366
367 /**
368 * Accepts a MAC address of the form 00:aa:11:bb:22:cc, case does not
369 * matter, and returns a corresponding byte[].
370 *
371 * @param macAddress
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800372 * The MAC address to convert into a byte array
alshabibc4901cd2014-09-05 16:50:40 -0700373 * @return The macAddress as a byte array
374 */
375 public static byte[] toMACAddress(final String macAddress) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700376 return MacAddress.valueOf(macAddress).toBytes();
alshabibc4901cd2014-09-05 16:50:40 -0700377 }
378
379 /**
380 * Accepts a MAC address and returns the corresponding long, where the MAC
381 * bytes are set on the lower order bytes of the long.
382 *
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800383 * @param macAddress MAC address as a byte array
alshabibc4901cd2014-09-05 16:50:40 -0700384 * @return a long containing the mac address bytes
385 */
386 public static long toLong(final byte[] macAddress) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700387 return MacAddress.valueOf(macAddress).toLong();
alshabibc4901cd2014-09-05 16:50:40 -0700388 }
389
390 /**
391 * Converts a long MAC address to a byte array.
392 *
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800393 * @param macAddress MAC address set on the lower order bytes of the long
alshabibc4901cd2014-09-05 16:50:40 -0700394 * @return the bytes of the mac address
395 */
396 public static byte[] toByteArray(final long macAddress) {
Ayaka Koshibea9c199f2014-09-16 16:21:40 -0700397 return MacAddress.valueOf(macAddress).toBytes();
alshabibc4901cd2014-09-05 16:50:40 -0700398 }
399
400 /*
401 * (non-Javadoc)
402 *
403 * @see java.lang.Object#hashCode()
404 */
405 @Override
406 public int hashCode() {
407 final int prime = 7867;
408 int result = super.hashCode();
409 result = prime * result + this.destinationMACAddress.hashCode();
410 result = prime * result + this.etherType;
411 result = prime * result + this.vlanID;
412 result = prime * result + this.priorityCode;
413 result = prime * result + (this.pad ? 1231 : 1237);
414 result = prime * result + this.sourceMACAddress.hashCode();
415 return result;
416 }
417
418 /*
419 * (non-Javadoc)
420 *
421 * @see java.lang.Object#equals(java.lang.Object)
422 */
423 @Override
424 public boolean equals(final Object obj) {
425 if (this == obj) {
426 return true;
427 }
428 if (!super.equals(obj)) {
429 return false;
430 }
431 if (!(obj instanceof Ethernet)) {
432 return false;
433 }
434 final Ethernet other = (Ethernet) obj;
435 if (!this.destinationMACAddress.equals(other.destinationMACAddress)) {
436 return false;
437 }
438 if (this.priorityCode != other.priorityCode) {
439 return false;
440 }
441 if (this.vlanID != other.vlanID) {
442 return false;
443 }
444 if (this.etherType != other.etherType) {
445 return false;
446 }
447 if (this.pad != other.pad) {
448 return false;
449 }
450 if (!this.sourceMACAddress.equals(other.sourceMACAddress)) {
451 return false;
452 }
453 return true;
454 }
455
456 /*
457 * (non-Javadoc)
458 *
459 * @see java.lang.Object#toString(java.lang.Object)
460 */
461 @Override
462 public String toString() {
463
464 final StringBuffer sb = new StringBuffer("\n");
465
466 final IPacket pkt = this.getPayload();
467
468 if (pkt instanceof ARP) {
469 sb.append("arp");
470 } else if (pkt instanceof LLDP) {
471 sb.append("lldp");
472 } else if (pkt instanceof ICMP) {
473 sb.append("icmp");
474 } else if (pkt instanceof IPv4) {
475 sb.append("ip");
476 } else if (pkt instanceof DHCP) {
477 sb.append("dhcp");
478 } else {
479 sb.append(this.getEtherType());
480 }
481
482 sb.append("\ndl_vlan: ");
483 if (this.getVlanID() == Ethernet.VLAN_UNTAGGED) {
484 sb.append("untagged");
485 } else {
486 sb.append(this.getVlanID());
487 }
488 sb.append("\ndl_vlan_pcp: ");
489 sb.append(this.getPriorityCode());
490 sb.append("\ndl_src: ");
491 sb.append(bytesToHex(this.getSourceMACAddress()));
492 sb.append("\ndl_dst: ");
493 sb.append(bytesToHex(this.getDestinationMACAddress()));
494
495 if (pkt instanceof ARP) {
496 final ARP p = (ARP) pkt;
497 sb.append("\nnw_src: ");
498 sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p
499 .getSenderProtocolAddress())));
500 sb.append("\nnw_dst: ");
501 sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p
502 .getTargetProtocolAddress())));
503 } else if (pkt instanceof LLDP) {
504 sb.append("lldp packet");
505 } else if (pkt instanceof ICMP) {
506 final ICMP icmp = (ICMP) pkt;
507 sb.append("\nicmp_type: ");
508 sb.append(icmp.getIcmpType());
509 sb.append("\nicmp_code: ");
510 sb.append(icmp.getIcmpCode());
511 } else if (pkt instanceof IPv4) {
512 final IPv4 p = (IPv4) pkt;
513 sb.append("\nnw_src: ");
514 sb.append(IPv4.fromIPv4Address(p.getSourceAddress()));
515 sb.append("\nnw_dst: ");
516 sb.append(IPv4.fromIPv4Address(p.getDestinationAddress()));
517 sb.append("\nnw_tos: ");
518 sb.append(p.getDiffServ());
519 sb.append("\nnw_proto: ");
520 sb.append(p.getProtocol());
521
522 if (pkt instanceof TCP) {
523 sb.append("\ntp_src: ");
524 sb.append(((TCP) pkt).getSourcePort());
525 sb.append("\ntp_dst: ");
526 sb.append(((TCP) pkt).getDestinationPort());
527
528 } else if (pkt instanceof UDP) {
529 sb.append("\ntp_src: ");
530 sb.append(((UDP) pkt).getSourcePort());
531 sb.append("\ntp_dst: ");
532 sb.append(((UDP) pkt).getDestinationPort());
533 }
534
535 if (pkt instanceof ICMP) {
536 final ICMP icmp = (ICMP) pkt;
537 sb.append("\nicmp_type: ");
538 sb.append(icmp.getIcmpType());
539 sb.append("\nicmp_code: ");
540 sb.append(icmp.getIcmpCode());
541 }
542
543 } else if (pkt instanceof DHCP) {
544 sb.append("\ndhcp packet");
545 } else if (pkt instanceof Data) {
546 sb.append("\ndata packet");
547 } else if (pkt instanceof LLC) {
548 sb.append("\nllc packet");
549 } else {
tom5f18cf32014-09-13 14:10:57 -0700550 sb.append("\nunknown packet");
alshabibc4901cd2014-09-05 16:50:40 -0700551 }
552
553 return sb.toString();
554 }
555
556 public static String bytesToHex(byte[] in) {
557 final StringBuilder builder = new StringBuilder();
558 for (byte b : in) {
559 builder.append(String.format("%02x", b));
560 }
561 return builder.toString();
562 }
563
564}