blob: 4b9fd665a40b8bd9f872315509be562ea0fc9e0f [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
33/**
34 *
35 */
36package org.onlab.packet;
37
38import java.nio.ByteBuffer;
39import java.util.Arrays;
40import java.util.Collection;
41import java.util.HashMap;
42import java.util.Map;
43
44/**
alshabibc4901cd2014-09-05 16:50:40 -070045 *
46 */
47public class IPv4 extends BasePacket {
48 public static final byte PROTOCOL_ICMP = 0x1;
49 public static final byte PROTOCOL_TCP = 0x6;
50 public static final byte PROTOCOL_UDP = 0x11;
51 public static Map<Byte, Class<? extends IPacket>> protocolClassMap;
52
53 static {
54 IPv4.protocolClassMap = new HashMap<Byte, Class<? extends IPacket>>();
55 IPv4.protocolClassMap.put(IPv4.PROTOCOL_ICMP, ICMP.class);
56 IPv4.protocolClassMap.put(IPv4.PROTOCOL_TCP, TCP.class);
57 IPv4.protocolClassMap.put(IPv4.PROTOCOL_UDP, UDP.class);
58 }
59
60 protected byte version;
61 protected byte headerLength;
62 protected byte diffServ;
63 protected short totalLength;
64 protected short identification;
65 protected byte flags;
66 protected short fragmentOffset;
67 protected byte ttl;
68 protected byte protocol;
69 protected short checksum;
70 protected int sourceAddress;
71 protected int destinationAddress;
72 protected byte[] options;
73
74 protected boolean isTruncated;
75
76 /**
77 * Default constructor that sets the version to 4.
78 */
79 public IPv4() {
80 super();
81 this.version = 4;
82 this.isTruncated = false;
83 }
84
85 /**
86 * @return the version
87 */
88 public byte getVersion() {
89 return this.version;
90 }
91
92 /**
93 * @param version
94 * the version to set
95 */
96 public IPv4 setVersion(final byte version) {
97 this.version = version;
98 return this;
99 }
100
101 /**
102 * @return the headerLength
103 */
104 public byte getHeaderLength() {
105 return this.headerLength;
106 }
107
108 /**
109 * @return the diffServ
110 */
111 public byte getDiffServ() {
112 return this.diffServ;
113 }
114
115 /**
116 * @param diffServ
117 * the diffServ to set
118 */
119 public IPv4 setDiffServ(final byte diffServ) {
120 this.diffServ = diffServ;
121 return this;
122 }
123
124 /**
125 * @return the totalLength
126 */
127 public short getTotalLength() {
128 return this.totalLength;
129 }
130
131 /**
132 * @return the identification
133 */
134 public short getIdentification() {
135 return this.identification;
136 }
137
138 public boolean isTruncated() {
139 return this.isTruncated;
140 }
141
142 public void setTruncated(final boolean isTruncated) {
143 this.isTruncated = isTruncated;
144 }
145
146 /**
147 * @param identification
148 * the identification to set
149 */
150 public IPv4 setIdentification(final short identification) {
151 this.identification = identification;
152 return this;
153 }
154
155 /**
156 * @return the flags
157 */
158 public byte getFlags() {
159 return this.flags;
160 }
161
162 /**
163 * @param flags
164 * the flags to set
165 */
166 public IPv4 setFlags(final byte flags) {
167 this.flags = flags;
168 return this;
169 }
170
171 /**
172 * @return the fragmentOffset
173 */
174 public short getFragmentOffset() {
175 return this.fragmentOffset;
176 }
177
178 /**
179 * @param fragmentOffset
180 * the fragmentOffset to set
181 */
182 public IPv4 setFragmentOffset(final short fragmentOffset) {
183 this.fragmentOffset = fragmentOffset;
184 return this;
185 }
186
187 /**
188 * @return the ttl
189 */
190 public byte getTtl() {
191 return this.ttl;
192 }
193
194 /**
195 * @param ttl
196 * the ttl to set
197 */
198 public IPv4 setTtl(final byte ttl) {
199 this.ttl = ttl;
200 return this;
201 }
202
203 /**
204 * @return the protocol
205 */
206 public byte getProtocol() {
207 return this.protocol;
208 }
209
210 /**
211 * @param protocol
212 * the protocol to set
213 */
214 public IPv4 setProtocol(final byte protocol) {
215 this.protocol = protocol;
216 return this;
217 }
218
219 /**
220 * @return the checksum
221 */
222 public short getChecksum() {
223 return this.checksum;
224 }
225
226 /**
227 * @param checksum
228 * the checksum to set
229 */
230 public IPv4 setChecksum(final short checksum) {
231 this.checksum = checksum;
232 return this;
233 }
234
235 @Override
236 public void resetChecksum() {
237 this.checksum = 0;
238 super.resetChecksum();
239 }
240
241 /**
242 * @return the sourceAddress
243 */
244 public int getSourceAddress() {
245 return this.sourceAddress;
246 }
247
248 /**
249 * @param sourceAddress
250 * the sourceAddress to set
251 */
252 public IPv4 setSourceAddress(final int sourceAddress) {
253 this.sourceAddress = sourceAddress;
254 return this;
255 }
256
257 /**
258 * @param sourceAddress
259 * the sourceAddress to set
260 */
261 public IPv4 setSourceAddress(final String sourceAddress) {
262 this.sourceAddress = IPv4.toIPv4Address(sourceAddress);
263 return this;
264 }
265
266 /**
267 * @return the destinationAddress
268 */
269 public int getDestinationAddress() {
270 return this.destinationAddress;
271 }
272
273 /**
274 * @param destinationAddress
275 * the destinationAddress to set
276 */
277 public IPv4 setDestinationAddress(final int destinationAddress) {
278 this.destinationAddress = destinationAddress;
279 return this;
280 }
281
282 /**
283 * @param destinationAddress
284 * the destinationAddress to set
285 */
286 public IPv4 setDestinationAddress(final String destinationAddress) {
287 this.destinationAddress = IPv4.toIPv4Address(destinationAddress);
288 return this;
289 }
290
291 /**
292 * @return the options
293 */
294 public byte[] getOptions() {
295 return this.options;
296 }
297
298 /**
299 * @param options
300 * the options to set
301 */
302 public IPv4 setOptions(final byte[] options) {
303 if (options != null && options.length % 4 > 0) {
304 throw new IllegalArgumentException(
305 "Options length must be a multiple of 4");
306 }
307 this.options = options;
308 return this;
309 }
310
311 /**
312 * Serializes the packet. Will compute and set the following fields if they
313 * are set to specific values at the time serialize is called: -checksum : 0
314 * -headerLength : 0 -totalLength : 0
315 */
316 @Override
317 public byte[] serialize() {
318 byte[] payloadData = null;
319 if (this.payload != null) {
320 this.payload.setParent(this);
321 payloadData = this.payload.serialize();
322 }
323
324 int optionsLength = 0;
325 if (this.options != null) {
326 optionsLength = this.options.length / 4;
327 }
328 this.headerLength = (byte) (5 + optionsLength);
329
330 this.totalLength = (short) (this.headerLength * 4 + (payloadData == null ? 0
331 : payloadData.length));
332
333 final byte[] data = new byte[this.totalLength];
334 final ByteBuffer bb = ByteBuffer.wrap(data);
335
336 bb.put((byte) ((this.version & 0xf) << 4 | this.headerLength & 0xf));
337 bb.put(this.diffServ);
338 bb.putShort(this.totalLength);
339 bb.putShort(this.identification);
340 bb.putShort((short) ((this.flags & 0x7) << 13 | this.fragmentOffset & 0x1fff));
341 bb.put(this.ttl);
342 bb.put(this.protocol);
343 bb.putShort(this.checksum);
344 bb.putInt(this.sourceAddress);
345 bb.putInt(this.destinationAddress);
346 if (this.options != null) {
347 bb.put(this.options);
348 }
349 if (payloadData != null) {
350 bb.put(payloadData);
351 }
352
353 // compute checksum if needed
354 if (this.checksum == 0) {
355 bb.rewind();
356 int accumulation = 0;
357 for (int i = 0; i < this.headerLength * 2; ++i) {
358 accumulation += 0xffff & bb.getShort();
359 }
360 accumulation = (accumulation >> 16 & 0xffff)
361 + (accumulation & 0xffff);
362 this.checksum = (short) (~accumulation & 0xffff);
363 bb.putShort(10, this.checksum);
364 }
365 return data;
366 }
367
368 @Override
369 public IPacket deserialize(final byte[] data, final int offset,
370 final int length) {
371 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
372 short sscratch;
373
374 this.version = bb.get();
375 this.headerLength = (byte) (this.version & 0xf);
376 this.version = (byte) (this.version >> 4 & 0xf);
377 this.diffServ = bb.get();
378 this.totalLength = bb.getShort();
379 this.identification = bb.getShort();
380 sscratch = bb.getShort();
381 this.flags = (byte) (sscratch >> 13 & 0x7);
382 this.fragmentOffset = (short) (sscratch & 0x1fff);
383 this.ttl = bb.get();
384 this.protocol = bb.get();
385 this.checksum = bb.getShort();
386 this.sourceAddress = bb.getInt();
387 this.destinationAddress = bb.getInt();
388
389 if (this.headerLength > 5) {
390 final int optionsLength = (this.headerLength - 5) * 4;
391 this.options = new byte[optionsLength];
392 bb.get(this.options);
393 }
394
395 IPacket payload;
396 if (IPv4.protocolClassMap.containsKey(this.protocol)) {
397 final Class<? extends IPacket> clazz = IPv4.protocolClassMap
398 .get(this.protocol);
399 try {
400 payload = clazz.newInstance();
401 } catch (final Exception e) {
402 throw new RuntimeException(
403 "Error parsing payload for IPv4 packet", e);
404 }
405 } else {
406 payload = new Data();
407 }
408 this.payload = payload.deserialize(data, bb.position(),
409 bb.limit() - bb.position());
410 this.payload.setParent(this);
411
412 if (this.totalLength != length) {
413 this.isTruncated = true;
414 } else {
415 this.isTruncated = false;
416 }
417
418 return this;
419 }
420
421 /**
422 * Accepts an IPv4 address of the form xxx.xxx.xxx.xxx, ie 192.168.0.1 and
423 * returns the corresponding 32 bit integer.
424 *
tom5f18cf32014-09-13 14:10:57 -0700425 * @param ipAddress ip address in string form
426 * @return int ip address value
alshabibc4901cd2014-09-05 16:50:40 -0700427 */
428 public static int toIPv4Address(final String ipAddress) {
429 if (ipAddress == null) {
430 throw new IllegalArgumentException("Specified IPv4 address must"
431 + "contain 4 sets of numerical digits separated by periods");
432 }
433 final String[] octets = ipAddress.split("\\.");
434 if (octets.length != 4) {
435 throw new IllegalArgumentException("Specified IPv4 address must"
436 + "contain 4 sets of numerical digits separated by periods");
437 }
438
439 int result = 0;
440 for (int i = 0; i < 4; ++i) {
441 result |= Integer.valueOf(octets[i]) << (3 - i) * 8;
442 }
443 return result;
444 }
445
446 /**
447 * Accepts an IPv4 address in a byte array and returns the corresponding
448 * 32-bit integer value.
449 *
tom5f18cf32014-09-13 14:10:57 -0700450 * @param ipAddress ip address in byte form
451 * @return int ip address value
alshabibc4901cd2014-09-05 16:50:40 -0700452 */
453 public static int toIPv4Address(final byte[] ipAddress) {
454 int ip = 0;
455 for (int i = 0; i < 4; i++) {
456 final int t = (ipAddress[i] & 0xff) << (3 - i) * 8;
457 ip |= t;
458 }
459 return ip;
460 }
461
462 /**
463 * Accepts an IPv4 address and returns of string of the form xxx.xxx.xxx.xxx,
464 * e.g., 192.168.0.1.
465 *
tom5f18cf32014-09-13 14:10:57 -0700466 * @param ipAddress ip address in form
467 * @return string form of ip address
alshabibc4901cd2014-09-05 16:50:40 -0700468 */
469 public static String fromIPv4Address(final int ipAddress) {
470 final StringBuffer sb = new StringBuffer();
471 int result = 0;
472 for (int i = 0; i < 4; ++i) {
473 result = ipAddress >> (3 - i) * 8 & 0xff;
alshabib638dc712014-09-05 18:03:45 -0700474 sb.append(Integer.valueOf(result).toString());
475 if (i != 3) {
476 sb.append(".");
477 }
alshabibc4901cd2014-09-05 16:50:40 -0700478 }
479 return sb.toString();
480 }
481
482 /**
483 * Accepts a collection of IPv4 addresses as integers and returns a single
484 * String useful in toString method's containing collections of IP
485 * addresses.
486 *
487 * @param ipAddresses
488 * collection
tom5f18cf32014-09-13 14:10:57 -0700489 * @return ip addresses in comma-separated string form
alshabibc4901cd2014-09-05 16:50:40 -0700490 */
491 public static String fromIPv4AddressCollection(
492 final Collection<Integer> ipAddresses) {
493 if (ipAddresses == null) {
494 return "null";
495 }
496 final StringBuffer sb = new StringBuffer();
497 sb.append("[");
498 for (final Integer ip : ipAddresses) {
499 sb.append(IPv4.fromIPv4Address(ip));
500 sb.append(",");
501 }
502 sb.replace(sb.length() - 1, sb.length(), "]");
503 return sb.toString();
504 }
505
506 /**
507 * Accepts an IPv4 address of the form xxx.xxx.xxx.xxx, ie 192.168.0.1 and
508 * returns the corresponding byte array.
509 *
510 * @param ipAddress
511 * The IP address in the form xx.xxx.xxx.xxx.
512 * @return The IP address separated into bytes
513 */
514 public static byte[] toIPv4AddressBytes(final String ipAddress) {
515 final String[] octets = ipAddress.split("\\.");
516 if (octets.length != 4) {
517 throw new IllegalArgumentException("Specified IPv4 address must"
518 + "contain 4 sets of numerical digits separated by periods");
519 }
520
521 final byte[] result = new byte[4];
522 for (int i = 0; i < 4; ++i) {
523 result[i] = Integer.valueOf(octets[i]).byteValue();
524 }
525 return result;
526 }
527
528 /**
529 * Accepts an IPv4 address in the form of an integer and returns the
530 * corresponding byte array.
531 *
532 * @param ipAddress
533 * The IP address as an integer.
534 * @return The IP address separated into bytes.
535 */
536 public static byte[] toIPv4AddressBytes(final int ipAddress) {
537 return new byte[] {(byte) (ipAddress >>> 24),
538 (byte) (ipAddress >>> 16), (byte) (ipAddress >>> 8),
539 (byte) ipAddress};
540 }
541
542 /*
543 * (non-Javadoc)
544 *
545 * @see java.lang.Object#hashCode()
546 */
547 @Override
548 public int hashCode() {
549 final int prime = 2521;
550 int result = super.hashCode();
551 result = prime * result + this.checksum;
552 result = prime * result + this.destinationAddress;
553 result = prime * result + this.diffServ;
554 result = prime * result + this.flags;
555 result = prime * result + this.fragmentOffset;
556 result = prime * result + this.headerLength;
557 result = prime * result + this.identification;
558 result = prime * result + Arrays.hashCode(this.options);
559 result = prime * result + this.protocol;
560 result = prime * result + this.sourceAddress;
561 result = prime * result + this.totalLength;
562 result = prime * result + this.ttl;
563 result = prime * result + this.version;
564 return result;
565 }
566
567 /*
568 * (non-Javadoc)
569 *
570 * @see java.lang.Object#equals(java.lang.Object)
571 */
572 @Override
573 public boolean equals(final Object obj) {
574 if (this == obj) {
575 return true;
576 }
577 if (!super.equals(obj)) {
578 return false;
579 }
580 if (!(obj instanceof IPv4)) {
581 return false;
582 }
583 final IPv4 other = (IPv4) obj;
584 if (this.checksum != other.checksum) {
585 return false;
586 }
587 if (this.destinationAddress != other.destinationAddress) {
588 return false;
589 }
590 if (this.diffServ != other.diffServ) {
591 return false;
592 }
593 if (this.flags != other.flags) {
594 return false;
595 }
596 if (this.fragmentOffset != other.fragmentOffset) {
597 return false;
598 }
599 if (this.headerLength != other.headerLength) {
600 return false;
601 }
602 if (this.identification != other.identification) {
603 return false;
604 }
605 if (!Arrays.equals(this.options, other.options)) {
606 return false;
607 }
608 if (this.protocol != other.protocol) {
609 return false;
610 }
611 if (this.sourceAddress != other.sourceAddress) {
612 return false;
613 }
614 if (this.totalLength != other.totalLength) {
615 return false;
616 }
617 if (this.ttl != other.ttl) {
618 return false;
619 }
620 if (this.version != other.version) {
621 return false;
622 }
623 return true;
624 }
625}