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