blob: 1e00ca52838d1c20a5c4d6761d69900184020a9d [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 */
alshabibc4901cd2014-09-05 16:50:40 -070016
17/**
18 *
19 */
20package org.onlab.packet;
21
22import java.nio.ByteBuffer;
23import java.util.Arrays;
24import java.util.Collection;
25import java.util.HashMap;
26import java.util.Map;
27
28/**
alshabibc4901cd2014-09-05 16:50:40 -070029 *
30 */
31public class IPv4 extends BasePacket {
32 public static final byte PROTOCOL_ICMP = 0x1;
33 public static final byte PROTOCOL_TCP = 0x6;
34 public static final byte PROTOCOL_UDP = 0x11;
Ray Milkey705d9bc2014-11-18 08:19:00 -080035 public static final Map<Byte, Class<? extends IPacket>> PROTOCOL_CLASS_MAP =
Ray Milkey241b96a2014-11-17 13:08:20 -080036 new HashMap<>();
alshabibc4901cd2014-09-05 16:50:40 -070037
38 static {
Ray Milkey705d9bc2014-11-18 08:19:00 -080039 IPv4.PROTOCOL_CLASS_MAP.put(IPv4.PROTOCOL_ICMP, ICMP.class);
40 IPv4.PROTOCOL_CLASS_MAP.put(IPv4.PROTOCOL_TCP, TCP.class);
41 IPv4.PROTOCOL_CLASS_MAP.put(IPv4.PROTOCOL_UDP, UDP.class);
alshabibc4901cd2014-09-05 16:50:40 -070042 }
43
Pavlin Radoslavovbf23c552015-02-20 14:20:30 -080044 private static final byte DSCP_MASK = 0x3f;
45 private static final byte DSCP_OFFSET = 2;
46 private static final byte ECN_MASK = 0x3;
47
alshabibc4901cd2014-09-05 16:50:40 -070048 protected byte version;
49 protected byte headerLength;
50 protected byte diffServ;
51 protected short totalLength;
52 protected short identification;
53 protected byte flags;
54 protected short fragmentOffset;
55 protected byte ttl;
56 protected byte protocol;
57 protected short checksum;
58 protected int sourceAddress;
59 protected int destinationAddress;
60 protected byte[] options;
61
62 protected boolean isTruncated;
63
64 /**
65 * Default constructor that sets the version to 4.
66 */
67 public IPv4() {
68 super();
69 this.version = 4;
70 this.isTruncated = false;
71 }
72
73 /**
74 * @return the version
75 */
76 public byte getVersion() {
77 return this.version;
78 }
79
80 /**
81 * @param version
82 * the version to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -080083 * @return this
alshabibc4901cd2014-09-05 16:50:40 -070084 */
85 public IPv4 setVersion(final byte version) {
86 this.version = version;
87 return this;
88 }
89
90 /**
91 * @return the headerLength
92 */
93 public byte getHeaderLength() {
94 return this.headerLength;
95 }
96
97 /**
Pavlin Radoslavovbf23c552015-02-20 14:20:30 -080098 * Gets the DSCP value (6 bits).
99 *
100 * @return the DSCP value (6 bits)
101 */
102 public byte getDscp() {
103 return (byte) ((this.diffServ >>> DSCP_OFFSET) & DSCP_MASK);
104 }
105
106 /**
107 * Sets the DSCP value (6 bits).
108 *
109 * @param dscp the DSCP value (6 bits)
110 * @return this
111 */
112 public IPv4 setDscp(byte dscp) {
113 this.diffServ &= ~(DSCP_MASK << DSCP_OFFSET);
114 this.diffServ |= (dscp & DSCP_MASK) << DSCP_OFFSET;
115 return this;
116 }
117
118 /**
119 * Gets the ECN value (2 bits).
120 *
121 * @return the ECN value (2 bits)
122 */
123 public byte getEcn() {
124 return (byte) (this.diffServ & ECN_MASK);
125 }
126
127 /**
128 * Sets the ECN value (2 bits).
129 *
130 * @param ecn the ECN value (2 bits)
131 * @return this
132 */
133 public IPv4 setEcn(byte ecn) {
134 this.diffServ &= ~ECN_MASK;
135 this.diffServ |= (ecn & ECN_MASK);
136 return this;
137 }
138
139 /**
140 * Gets the DiffServ octet (including the DSCP and ECN bits).
141 *
142 * @return the diffServ octet (including the DSCP and ECN bits)
alshabibc4901cd2014-09-05 16:50:40 -0700143 */
144 public byte getDiffServ() {
145 return this.diffServ;
146 }
147
148 /**
Pavlin Radoslavovbf23c552015-02-20 14:20:30 -0800149 * Sets the DiffServ octet (including the DSCP and ECN bits).
150 *
151 * @param diffServ the diffServ octet to set (including the DSCP and ECN
152 * bits)
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800153 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700154 */
155 public IPv4 setDiffServ(final byte diffServ) {
156 this.diffServ = diffServ;
157 return this;
158 }
159
160 /**
161 * @return the totalLength
162 */
163 public short getTotalLength() {
164 return this.totalLength;
165 }
166
167 /**
168 * @return the identification
169 */
170 public short getIdentification() {
171 return this.identification;
172 }
173
174 public boolean isTruncated() {
175 return this.isTruncated;
176 }
177
178 public void setTruncated(final boolean isTruncated) {
179 this.isTruncated = isTruncated;
180 }
181
182 /**
183 * @param identification
184 * the identification to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800185 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700186 */
187 public IPv4 setIdentification(final short identification) {
188 this.identification = identification;
189 return this;
190 }
191
192 /**
193 * @return the flags
194 */
195 public byte getFlags() {
196 return this.flags;
197 }
198
199 /**
200 * @param flags
201 * the flags to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800202 * @return this
203s */
alshabibc4901cd2014-09-05 16:50:40 -0700204 public IPv4 setFlags(final byte flags) {
205 this.flags = flags;
206 return this;
207 }
208
209 /**
210 * @return the fragmentOffset
211 */
212 public short getFragmentOffset() {
213 return this.fragmentOffset;
214 }
215
216 /**
217 * @param fragmentOffset
218 * the fragmentOffset to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800219 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700220 */
221 public IPv4 setFragmentOffset(final short fragmentOffset) {
222 this.fragmentOffset = fragmentOffset;
223 return this;
224 }
225
226 /**
227 * @return the ttl
228 */
229 public byte getTtl() {
230 return this.ttl;
231 }
232
233 /**
234 * @param ttl
235 * the ttl to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800236 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700237 */
238 public IPv4 setTtl(final byte ttl) {
239 this.ttl = ttl;
240 return this;
241 }
242
243 /**
244 * @return the protocol
245 */
246 public byte getProtocol() {
247 return this.protocol;
248 }
249
250 /**
251 * @param protocol
252 * the protocol to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800253 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700254 */
255 public IPv4 setProtocol(final byte protocol) {
256 this.protocol = protocol;
257 return this;
258 }
259
260 /**
261 * @return the checksum
262 */
263 public short getChecksum() {
264 return this.checksum;
265 }
266
267 /**
268 * @param checksum
269 * the checksum to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800270 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700271 */
272 public IPv4 setChecksum(final short checksum) {
273 this.checksum = checksum;
274 return this;
275 }
276
277 @Override
278 public void resetChecksum() {
279 this.checksum = 0;
280 super.resetChecksum();
281 }
282
283 /**
284 * @return the sourceAddress
285 */
286 public int getSourceAddress() {
287 return this.sourceAddress;
288 }
289
290 /**
291 * @param sourceAddress
292 * the sourceAddress to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800293 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700294 */
295 public IPv4 setSourceAddress(final int sourceAddress) {
296 this.sourceAddress = sourceAddress;
297 return this;
298 }
299
300 /**
301 * @param sourceAddress
302 * the sourceAddress to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800303 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700304 */
305 public IPv4 setSourceAddress(final String sourceAddress) {
306 this.sourceAddress = IPv4.toIPv4Address(sourceAddress);
307 return this;
308 }
309
310 /**
311 * @return the destinationAddress
312 */
313 public int getDestinationAddress() {
314 return this.destinationAddress;
315 }
316
317 /**
318 * @param destinationAddress
319 * the destinationAddress to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800320 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700321 */
322 public IPv4 setDestinationAddress(final int destinationAddress) {
323 this.destinationAddress = destinationAddress;
324 return this;
325 }
326
327 /**
328 * @param destinationAddress
329 * the destinationAddress to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800330 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700331 */
332 public IPv4 setDestinationAddress(final String destinationAddress) {
333 this.destinationAddress = IPv4.toIPv4Address(destinationAddress);
334 return this;
335 }
336
337 /**
338 * @return the options
339 */
340 public byte[] getOptions() {
341 return this.options;
342 }
343
344 /**
345 * @param options
346 * the options to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800347 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700348 */
349 public IPv4 setOptions(final byte[] options) {
350 if (options != null && options.length % 4 > 0) {
351 throw new IllegalArgumentException(
352 "Options length must be a multiple of 4");
353 }
354 this.options = options;
355 return this;
356 }
357
358 /**
359 * Serializes the packet. Will compute and set the following fields if they
360 * are set to specific values at the time serialize is called: -checksum : 0
361 * -headerLength : 0 -totalLength : 0
362 */
363 @Override
364 public byte[] serialize() {
365 byte[] payloadData = null;
366 if (this.payload != null) {
367 this.payload.setParent(this);
368 payloadData = this.payload.serialize();
369 }
370
371 int optionsLength = 0;
372 if (this.options != null) {
373 optionsLength = this.options.length / 4;
374 }
375 this.headerLength = (byte) (5 + optionsLength);
376
377 this.totalLength = (short) (this.headerLength * 4 + (payloadData == null ? 0
378 : payloadData.length));
379
380 final byte[] data = new byte[this.totalLength];
381 final ByteBuffer bb = ByteBuffer.wrap(data);
382
383 bb.put((byte) ((this.version & 0xf) << 4 | this.headerLength & 0xf));
384 bb.put(this.diffServ);
385 bb.putShort(this.totalLength);
386 bb.putShort(this.identification);
387 bb.putShort((short) ((this.flags & 0x7) << 13 | this.fragmentOffset & 0x1fff));
388 bb.put(this.ttl);
389 bb.put(this.protocol);
390 bb.putShort(this.checksum);
391 bb.putInt(this.sourceAddress);
392 bb.putInt(this.destinationAddress);
393 if (this.options != null) {
394 bb.put(this.options);
395 }
396 if (payloadData != null) {
397 bb.put(payloadData);
398 }
399
400 // compute checksum if needed
401 if (this.checksum == 0) {
402 bb.rewind();
403 int accumulation = 0;
404 for (int i = 0; i < this.headerLength * 2; ++i) {
405 accumulation += 0xffff & bb.getShort();
406 }
407 accumulation = (accumulation >> 16 & 0xffff)
408 + (accumulation & 0xffff);
409 this.checksum = (short) (~accumulation & 0xffff);
410 bb.putShort(10, this.checksum);
411 }
412 return data;
413 }
414
415 @Override
416 public IPacket deserialize(final byte[] data, final int offset,
417 final int length) {
418 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
419 short sscratch;
420
421 this.version = bb.get();
422 this.headerLength = (byte) (this.version & 0xf);
423 this.version = (byte) (this.version >> 4 & 0xf);
424 this.diffServ = bb.get();
425 this.totalLength = bb.getShort();
426 this.identification = bb.getShort();
427 sscratch = bb.getShort();
428 this.flags = (byte) (sscratch >> 13 & 0x7);
429 this.fragmentOffset = (short) (sscratch & 0x1fff);
430 this.ttl = bb.get();
431 this.protocol = bb.get();
432 this.checksum = bb.getShort();
433 this.sourceAddress = bb.getInt();
434 this.destinationAddress = bb.getInt();
435
436 if (this.headerLength > 5) {
437 final int optionsLength = (this.headerLength - 5) * 4;
438 this.options = new byte[optionsLength];
439 bb.get(this.options);
440 }
441
442 IPacket payload;
Ray Milkey705d9bc2014-11-18 08:19:00 -0800443 if (IPv4.PROTOCOL_CLASS_MAP.containsKey(this.protocol)) {
444 final Class<? extends IPacket> clazz = IPv4.PROTOCOL_CLASS_MAP
alshabibc4901cd2014-09-05 16:50:40 -0700445 .get(this.protocol);
446 try {
447 payload = clazz.newInstance();
448 } catch (final Exception e) {
449 throw new RuntimeException(
450 "Error parsing payload for IPv4 packet", e);
451 }
452 } else {
453 payload = new Data();
454 }
455 this.payload = payload.deserialize(data, bb.position(),
456 bb.limit() - bb.position());
457 this.payload.setParent(this);
458
459 if (this.totalLength != length) {
460 this.isTruncated = true;
461 } else {
462 this.isTruncated = false;
463 }
464
465 return this;
466 }
467
468 /**
469 * Accepts an IPv4 address of the form xxx.xxx.xxx.xxx, ie 192.168.0.1 and
470 * returns the corresponding 32 bit integer.
471 *
tom5f18cf32014-09-13 14:10:57 -0700472 * @param ipAddress ip address in string form
473 * @return int ip address value
alshabibc4901cd2014-09-05 16:50:40 -0700474 */
475 public static int toIPv4Address(final String ipAddress) {
476 if (ipAddress == null) {
477 throw new IllegalArgumentException("Specified IPv4 address must"
478 + "contain 4 sets of numerical digits separated by periods");
479 }
480 final String[] octets = ipAddress.split("\\.");
481 if (octets.length != 4) {
482 throw new IllegalArgumentException("Specified IPv4 address must"
483 + "contain 4 sets of numerical digits separated by periods");
484 }
485
486 int result = 0;
487 for (int i = 0; i < 4; ++i) {
Yuta HIGUCHIe5ca93b2014-10-23 09:49:00 -0700488 result |= Integer.parseInt(octets[i]) << (3 - i) * 8;
alshabibc4901cd2014-09-05 16:50:40 -0700489 }
490 return result;
491 }
492
493 /**
494 * Accepts an IPv4 address in a byte array and returns the corresponding
495 * 32-bit integer value.
496 *
tom5f18cf32014-09-13 14:10:57 -0700497 * @param ipAddress ip address in byte form
498 * @return int ip address value
alshabibc4901cd2014-09-05 16:50:40 -0700499 */
500 public static int toIPv4Address(final byte[] ipAddress) {
501 int ip = 0;
502 for (int i = 0; i < 4; i++) {
503 final int t = (ipAddress[i] & 0xff) << (3 - i) * 8;
504 ip |= t;
505 }
506 return ip;
507 }
508
509 /**
510 * Accepts an IPv4 address and returns of string of the form xxx.xxx.xxx.xxx,
511 * e.g., 192.168.0.1.
512 *
tom5f18cf32014-09-13 14:10:57 -0700513 * @param ipAddress ip address in form
514 * @return string form of ip address
alshabibc4901cd2014-09-05 16:50:40 -0700515 */
516 public static String fromIPv4Address(final int ipAddress) {
517 final StringBuffer sb = new StringBuffer();
518 int result = 0;
519 for (int i = 0; i < 4; ++i) {
520 result = ipAddress >> (3 - i) * 8 & 0xff;
Yuta HIGUCHIe5ca93b2014-10-23 09:49:00 -0700521 sb.append(result);
alshabib638dc712014-09-05 18:03:45 -0700522 if (i != 3) {
523 sb.append(".");
524 }
alshabibc4901cd2014-09-05 16:50:40 -0700525 }
526 return sb.toString();
527 }
528
529 /**
530 * Accepts a collection of IPv4 addresses as integers and returns a single
531 * String useful in toString method's containing collections of IP
532 * addresses.
533 *
534 * @param ipAddresses
535 * collection
tom5f18cf32014-09-13 14:10:57 -0700536 * @return ip addresses in comma-separated string form
alshabibc4901cd2014-09-05 16:50:40 -0700537 */
538 public static String fromIPv4AddressCollection(
539 final Collection<Integer> ipAddresses) {
540 if (ipAddresses == null) {
541 return "null";
542 }
543 final StringBuffer sb = new StringBuffer();
544 sb.append("[");
545 for (final Integer ip : ipAddresses) {
546 sb.append(IPv4.fromIPv4Address(ip));
547 sb.append(",");
548 }
549 sb.replace(sb.length() - 1, sb.length(), "]");
550 return sb.toString();
551 }
552
553 /**
554 * Accepts an IPv4 address of the form xxx.xxx.xxx.xxx, ie 192.168.0.1 and
555 * returns the corresponding byte array.
556 *
557 * @param ipAddress
558 * The IP address in the form xx.xxx.xxx.xxx.
559 * @return The IP address separated into bytes
560 */
561 public static byte[] toIPv4AddressBytes(final String ipAddress) {
562 final String[] octets = ipAddress.split("\\.");
563 if (octets.length != 4) {
564 throw new IllegalArgumentException("Specified IPv4 address must"
565 + "contain 4 sets of numerical digits separated by periods");
566 }
567
568 final byte[] result = new byte[4];
569 for (int i = 0; i < 4; ++i) {
570 result[i] = Integer.valueOf(octets[i]).byteValue();
571 }
572 return result;
573 }
574
575 /**
576 * Accepts an IPv4 address in the form of an integer and returns the
577 * corresponding byte array.
578 *
579 * @param ipAddress
580 * The IP address as an integer.
581 * @return The IP address separated into bytes.
582 */
583 public static byte[] toIPv4AddressBytes(final int ipAddress) {
584 return new byte[] {(byte) (ipAddress >>> 24),
585 (byte) (ipAddress >>> 16), (byte) (ipAddress >>> 8),
586 (byte) ipAddress};
587 }
588
589 /*
590 * (non-Javadoc)
591 *
592 * @see java.lang.Object#hashCode()
593 */
594 @Override
595 public int hashCode() {
596 final int prime = 2521;
597 int result = super.hashCode();
598 result = prime * result + this.checksum;
599 result = prime * result + this.destinationAddress;
600 result = prime * result + this.diffServ;
601 result = prime * result + this.flags;
602 result = prime * result + this.fragmentOffset;
603 result = prime * result + this.headerLength;
604 result = prime * result + this.identification;
605 result = prime * result + Arrays.hashCode(this.options);
606 result = prime * result + this.protocol;
607 result = prime * result + this.sourceAddress;
608 result = prime * result + this.totalLength;
609 result = prime * result + this.ttl;
610 result = prime * result + this.version;
611 return result;
612 }
613
614 /*
615 * (non-Javadoc)
616 *
617 * @see java.lang.Object#equals(java.lang.Object)
618 */
619 @Override
620 public boolean equals(final Object obj) {
621 if (this == obj) {
622 return true;
623 }
624 if (!super.equals(obj)) {
625 return false;
626 }
627 if (!(obj instanceof IPv4)) {
628 return false;
629 }
630 final IPv4 other = (IPv4) obj;
631 if (this.checksum != other.checksum) {
632 return false;
633 }
634 if (this.destinationAddress != other.destinationAddress) {
635 return false;
636 }
637 if (this.diffServ != other.diffServ) {
638 return false;
639 }
640 if (this.flags != other.flags) {
641 return false;
642 }
643 if (this.fragmentOffset != other.fragmentOffset) {
644 return false;
645 }
646 if (this.headerLength != other.headerLength) {
647 return false;
648 }
649 if (this.identification != other.identification) {
650 return false;
651 }
652 if (!Arrays.equals(this.options, other.options)) {
653 return false;
654 }
655 if (this.protocol != other.protocol) {
656 return false;
657 }
658 if (this.sourceAddress != other.sourceAddress) {
659 return false;
660 }
661 if (this.totalLength != other.totalLength) {
662 return false;
663 }
664 if (this.ttl != other.ttl) {
665 return false;
666 }
667 if (this.version != other.version) {
668 return false;
669 }
670 return true;
671 }
672}