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