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