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