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