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