blob: 8d450797d10c9b6fd1d264d61431dbe272061710 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Charles M.C. Chan197a0122015-04-08 18:15:34 +08002 * Copyright 2014-2015 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 */
16
17
alshabibc4901cd2014-09-05 16:50:40 -070018
19package org.onlab.packet;
20
21import java.nio.ByteBuffer;
22
Jonathan Hart2a655752015-04-07 16:46:33 -070023import static org.onlab.packet.PacketUtils.*;
24
alshabibc4901cd2014-09-05 16:50:40 -070025/**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080026 * Implements TCP packet format.
alshabibc4901cd2014-09-05 16:50:40 -070027 */
28
29public class TCP extends BasePacket {
Jonathan Hart2a655752015-04-07 16:46:33 -070030
31 private static final short TCP_HEADER_LENGTH = 20;
32
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070033 protected int sourcePort;
34 protected int destinationPort;
alshabibc4901cd2014-09-05 16:50:40 -070035 protected int sequence;
36 protected int acknowledge;
37 protected byte dataOffset;
38 protected short flags;
39 protected short windowSize;
40 protected short checksum;
41 protected short urgentPointer;
42 protected byte[] options;
43
44 /**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080045 * Gets TCP source port.
46 *
47 * @return TCP source port
alshabibc4901cd2014-09-05 16:50:40 -070048 */
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070049 public int getSourcePort() {
alshabibc4901cd2014-09-05 16:50:40 -070050 return this.sourcePort;
51 }
52
53 /**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080054 * Sets TCP source port.
55 *
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070056 * @param sourcePort the sourcePort to set (unsigned 16 bits integer)
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -080057 * @return this
alshabibc4901cd2014-09-05 16:50:40 -070058 */
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070059 public TCP setSourcePort(final int sourcePort) {
alshabibc4901cd2014-09-05 16:50:40 -070060 this.sourcePort = sourcePort;
61 return this;
62 }
63
64 /**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080065 * Gets TCP destination port.
66 *
alshabibc4901cd2014-09-05 16:50:40 -070067 * @return the destinationPort
68 */
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070069 public int getDestinationPort() {
alshabibc4901cd2014-09-05 16:50:40 -070070 return this.destinationPort;
71 }
72
73 /**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080074 * Sets TCP destination port.
75 *
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070076 * @param destinationPort the destinationPort to set (unsigned 16 bits integer)
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -080077 * @return this
alshabibc4901cd2014-09-05 16:50:40 -070078 */
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070079 public TCP setDestinationPort(final int destinationPort) {
alshabibc4901cd2014-09-05 16:50:40 -070080 this.destinationPort = destinationPort;
81 return this;
82 }
83
84 /**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080085 * Gets checksum.
86 *
alshabibc4901cd2014-09-05 16:50:40 -070087 * @return the checksum
88 */
89 public short getChecksum() {
90 return this.checksum;
91 }
92
Charles M.C. Chan197a0122015-04-08 18:15:34 +080093 /**
94 * Sets checksum.
95 *
96 * @param checksum the checksum to set
97 * @return this
98 */
99 public TCP setChecksum(final short checksum) {
100 this.checksum = checksum;
101 return this;
102 }
103
104 /**
105 * Gets sequence number.
106 *
107 * @return the sequence number
108 */
alshabibc4901cd2014-09-05 16:50:40 -0700109 public int getSequence() {
110 return this.sequence;
111 }
112
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800113 /**
114 * Sets sequence number.
115 *
116 * @param seq the sequence number to set
117 * @return this
118 */
alshabibc4901cd2014-09-05 16:50:40 -0700119 public TCP setSequence(final int seq) {
120 this.sequence = seq;
121 return this;
122 }
123
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800124 /**
125 * Gets acknowledge number.
126 *
127 * @return the acknowledge number
128 */
alshabibc4901cd2014-09-05 16:50:40 -0700129 public int getAcknowledge() {
130 return this.acknowledge;
131 }
132
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800133 /**
134 * Sets acknowledge number.
135 *
136 * @param ack the acknowledge number to set
137 * @return this
138 */
alshabibc4901cd2014-09-05 16:50:40 -0700139 public TCP setAcknowledge(final int ack) {
140 this.acknowledge = ack;
141 return this;
142 }
143
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800144 /**
145 * Gets offset.
146 *
147 * @return the offset
148 */
alshabibc4901cd2014-09-05 16:50:40 -0700149 public byte getDataOffset() {
150 return this.dataOffset;
151 }
152
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800153 /**
154 * Sets offset.
155 *
156 * @param offset the offset to set
157 * @return this
158 */
alshabibc4901cd2014-09-05 16:50:40 -0700159 public TCP setDataOffset(final byte offset) {
160 this.dataOffset = offset;
161 return this;
162 }
163
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800164 /**
165 * Gets TCP flags.
166 *
167 * @return the TCP flags
168 */
alshabibc4901cd2014-09-05 16:50:40 -0700169 public short getFlags() {
170 return this.flags;
171 }
172
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800173 /**
174 * Sets TCP flags.
175 *
176 * @param flags the TCP flags to set
177 * @return this
178 */
alshabibc4901cd2014-09-05 16:50:40 -0700179 public TCP setFlags(final short flags) {
180 this.flags = flags;
181 return this;
182 }
183
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800184 /**
185 * Gets TCP window size.
186 *
187 * @return the TCP window size
188 */
alshabibc4901cd2014-09-05 16:50:40 -0700189 public short getWindowSize() {
190 return this.windowSize;
191 }
192
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800193 /**
194 * Sets TCP window size.
195 *
196 * @param windowSize the TCP window size to set
197 * @return this
198 */
alshabibc4901cd2014-09-05 16:50:40 -0700199 public TCP setWindowSize(final short windowSize) {
200 this.windowSize = windowSize;
201 return this;
202 }
203
alshabibc4901cd2014-09-05 16:50:40 -0700204 @Override
205 public void resetChecksum() {
206 this.checksum = 0;
207 super.resetChecksum();
208 }
209
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800210 /**
211 * Gets urgent pointer.
212 *
213 * @return the urgent pointer
214 */
215 public short getUrgentPointer() {
alshabibc4901cd2014-09-05 16:50:40 -0700216 return this.urgentPointer;
217 }
218
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800219 /**
220 * Sets urgent pointer.
221 *
222 * @param urgentPointer the urgent pointer to set
223 * @return this
224 */
alshabibc4901cd2014-09-05 16:50:40 -0700225 public TCP setUrgentPointer(final short urgentPointer) {
226 this.urgentPointer = urgentPointer;
227 return this;
228 }
229
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800230 /**
231 * Gets TCP options.
232 *
233 * @return the TCP options
234 */
alshabibc4901cd2014-09-05 16:50:40 -0700235 public byte[] getOptions() {
236 return this.options;
237 }
238
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800239 /**
240 * Sets TCP options.
241 *
242 * @param options the options to set
243 * @return this
244 */
alshabibc4901cd2014-09-05 16:50:40 -0700245 public TCP setOptions(final byte[] options) {
246 this.options = options;
247 this.dataOffset = (byte) (20 + options.length + 3 >> 2);
248 return this;
249 }
250
251 /**
alshabibc4901cd2014-09-05 16:50:40 -0700252 * Serializes the packet. Will compute and set the following fields if they
253 * are set to specific values at the time serialize is called: -checksum : 0
254 * -length : 0
255 */
256 @Override
257 public byte[] serialize() {
258 int length;
259 if (this.dataOffset == 0) {
260 this.dataOffset = 5; // default header length
261 }
262 length = this.dataOffset << 2;
263 byte[] payloadData = null;
264 if (this.payload != null) {
265 this.payload.setParent(this);
266 payloadData = this.payload.serialize();
267 length += payloadData.length;
268 }
269
270 final byte[] data = new byte[length];
271 final ByteBuffer bb = ByteBuffer.wrap(data);
272
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700273 bb.putShort((short) (this.sourcePort & 0xffff));
274 bb.putShort((short) (this.destinationPort & 0xffff));
alshabibc4901cd2014-09-05 16:50:40 -0700275 bb.putInt(this.sequence);
276 bb.putInt(this.acknowledge);
277 bb.putShort((short) (this.flags | this.dataOffset << 12));
278 bb.putShort(this.windowSize);
279 bb.putShort(this.checksum);
280 bb.putShort(this.urgentPointer);
281 if (this.dataOffset > 5) {
282 int padding;
283 bb.put(this.options);
284 padding = (this.dataOffset << 2) - 20 - this.options.length;
285 for (int i = 0; i < padding; i++) {
286 bb.put((byte) 0);
287 }
288 }
289 if (payloadData != null) {
290 bb.put(payloadData);
291 }
292
293 if (this.parent != null && this.parent instanceof IPv4) {
294 ((IPv4) this.parent).setProtocol(IPv4.PROTOCOL_TCP);
295 }
296
297 // compute checksum if needed
298 if (this.checksum == 0) {
299 bb.rewind();
300 int accumulation = 0;
301
302 // compute pseudo header mac
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800303 if (this.parent != null) {
304 if (this.parent instanceof IPv4) {
305 final IPv4 ipv4 = (IPv4) this.parent;
306 accumulation += (ipv4.getSourceAddress() >> 16 & 0xffff)
307 + (ipv4.getSourceAddress() & 0xffff);
308 accumulation += (ipv4.getDestinationAddress() >> 16 & 0xffff)
309 + (ipv4.getDestinationAddress() & 0xffff);
310 accumulation += ipv4.getProtocol() & 0xff;
311 accumulation += length & 0xffff;
312 } else if (this.parent instanceof IPv6) {
313 final IPv6 ipv6 = (IPv6) this.parent;
314 final int bbLength =
315 Ip6Address.BYTE_LENGTH * 2 // IPv6 src, dst
316 + 2 // nextHeader (with padding)
317 + 4; // length
318 final ByteBuffer bbChecksum = ByteBuffer.allocate(bbLength);
319 bbChecksum.put(ipv6.getSourceAddress());
320 bbChecksum.put(ipv6.getDestinationAddress());
321 bbChecksum.put((byte) 0); // padding
322 bbChecksum.put(ipv6.getNextHeader());
323 bbChecksum.putInt(length);
324 bbChecksum.rewind();
325 for (int i = 0; i < bbLength / 2; ++i) {
326 accumulation += 0xffff & bbChecksum.getShort();
327 }
328 }
alshabibc4901cd2014-09-05 16:50:40 -0700329 }
330
331 for (int i = 0; i < length / 2; ++i) {
332 accumulation += 0xffff & bb.getShort();
333 }
334 // pad to an even number of shorts
335 if (length % 2 > 0) {
336 accumulation += (bb.get() & 0xff) << 8;
337 }
338
339 accumulation = (accumulation >> 16 & 0xffff)
340 + (accumulation & 0xffff);
341 this.checksum = (short) (~accumulation & 0xffff);
342 bb.putShort(16, this.checksum);
343 }
344 return data;
345 }
346
Jonathan Hart2a655752015-04-07 16:46:33 -0700347 @Override
348 public IPacket deserialize(final byte[] data, final int offset,
349 final int length) {
350 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700351 this.sourcePort = (bb.getShort() & 0xffff);
352 this.destinationPort = (bb.getShort() & 0xffff);
Jonathan Hart2a655752015-04-07 16:46:33 -0700353 this.sequence = bb.getInt();
354 this.acknowledge = bb.getInt();
355 this.flags = bb.getShort();
356 this.dataOffset = (byte) (this.flags >> 12 & 0xf);
357 this.flags = (short) (this.flags & 0x1ff);
358 this.windowSize = bb.getShort();
359 this.checksum = bb.getShort();
360 this.urgentPointer = bb.getShort();
361 if (this.dataOffset > 5) {
362 int optLength = (this.dataOffset << 2) - 20;
363 if (bb.limit() < bb.position() + optLength) {
364 optLength = bb.limit() - bb.position();
365 }
366 try {
367 this.options = new byte[optLength];
368 bb.get(this.options, 0, optLength);
369 } catch (final IndexOutOfBoundsException e) {
370 this.options = null;
371 }
372 }
373
374 this.payload = new Data();
375 this.payload = this.payload.deserialize(data, bb.position(), bb.limit()
376 - bb.position());
377 this.payload.setParent(this);
378 return this;
379 }
380
alshabibc4901cd2014-09-05 16:50:40 -0700381 /*
382 * (non-Javadoc)
383 *
384 * @see java.lang.Object#hashCode()
385 */
386 @Override
387 public int hashCode() {
388 final int prime = 5807;
389 int result = super.hashCode();
390 result = prime * result + this.checksum;
391 result = prime * result + this.destinationPort;
392 result = prime * result + this.sourcePort;
393 return result;
394 }
395
396 /*
397 * (non-Javadoc)
398 *
399 * @see java.lang.Object#equals(java.lang.Object)
400 */
401 @Override
402 public boolean equals(final Object obj) {
403 if (this == obj) {
404 return true;
405 }
406 if (!super.equals(obj)) {
407 return false;
408 }
409 if (!(obj instanceof TCP)) {
410 return false;
411 }
412 final TCP other = (TCP) obj;
413 // May want to compare fields based on the flags set
414 return this.checksum == other.checksum
415 && this.destinationPort == other.destinationPort
416 && this.sourcePort == other.sourcePort
417 && this.sequence == other.sequence
418 && this.acknowledge == other.acknowledge
419 && this.dataOffset == other.dataOffset
420 && this.flags == other.flags
421 && this.windowSize == other.windowSize
422 && this.urgentPointer == other.urgentPointer
423 && (this.dataOffset == 5 || this.options.equals(other.options));
424 }
425
Jonathan Hart2a655752015-04-07 16:46:33 -0700426 /**
427 * Deserializer function for TCP packets.
428 *
429 * @return deserializer function
430 */
431 public static Deserializer<TCP> deserializer() {
432 return (data, offset, length) -> {
433 checkInput(data, offset, length, TCP_HEADER_LENGTH);
alshabibc4901cd2014-09-05 16:50:40 -0700434
Jonathan Hart2a655752015-04-07 16:46:33 -0700435 TCP tcp = new TCP();
436
437 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700438 tcp.sourcePort = (bb.getShort() & 0xffff);
439 tcp.destinationPort = (bb.getShort() & 0xffff);
Jonathan Hart2a655752015-04-07 16:46:33 -0700440 tcp.sequence = bb.getInt();
441 tcp.acknowledge = bb.getInt();
442 tcp.flags = bb.getShort();
443 tcp.dataOffset = (byte) (tcp.flags >> 12 & 0xf);
444 tcp.flags = (short) (tcp.flags & 0x1ff);
445 tcp.windowSize = bb.getShort();
446 tcp.checksum = bb.getShort();
447 tcp.urgentPointer = bb.getShort();
448 if (tcp.dataOffset > 5) {
449 int optLength = (tcp.dataOffset << 2) - 20;
450 checkHeaderLength(length, TCP_HEADER_LENGTH + tcp.dataOffset);
451 tcp.options = new byte[optLength];
452 bb.get(tcp.options, 0, optLength);
453 }
454
455 tcp.payload = Data.deserializer()
456 .deserialize(data, bb.position(), bb.limit() - bb.position());
457 tcp.payload.setParent(tcp);
458 return tcp;
459 };
alshabibc4901cd2014-09-05 16:50:40 -0700460 }
461}