blob: 1c843243f825e8ec75134d1b95b104a63e052b93 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2014-present Open Networking Foundation
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;
HIGUCHI Yutad57cdc42015-09-04 14:59:06 -070022import java.util.Arrays;
alshabibc4901cd2014-09-05 16:50:40 -070023
Jian Li5fc14292015-12-04 11:30:46 -080024import static com.google.common.base.MoreObjects.toStringHelper;
Jonathan Hart2a655752015-04-07 16:46:33 -070025import static org.onlab.packet.PacketUtils.*;
26
alshabibc4901cd2014-09-05 16:50:40 -070027/**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080028 * Implements TCP packet format.
alshabibc4901cd2014-09-05 16:50:40 -070029 */
30
31public class TCP extends BasePacket {
Jonathan Hart2a655752015-04-07 16:46:33 -070032
33 private static final short TCP_HEADER_LENGTH = 20;
34
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070035 protected int sourcePort;
36 protected int destinationPort;
alshabibc4901cd2014-09-05 16:50:40 -070037 protected int sequence;
38 protected int acknowledge;
39 protected byte dataOffset;
40 protected short flags;
41 protected short windowSize;
42 protected short checksum;
43 protected short urgentPointer;
44 protected byte[] options;
45
46 /**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080047 * Gets TCP source port.
48 *
49 * @return TCP source port
alshabibc4901cd2014-09-05 16:50:40 -070050 */
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070051 public int getSourcePort() {
alshabibc4901cd2014-09-05 16:50:40 -070052 return this.sourcePort;
53 }
54
55 /**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080056 * Sets TCP source port.
57 *
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070058 * @param sourcePort the sourcePort to set (unsigned 16 bits integer)
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -080059 * @return this
alshabibc4901cd2014-09-05 16:50:40 -070060 */
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070061 public TCP setSourcePort(final int sourcePort) {
alshabibc4901cd2014-09-05 16:50:40 -070062 this.sourcePort = sourcePort;
63 return this;
64 }
65
66 /**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080067 * Gets TCP destination port.
68 *
alshabibc4901cd2014-09-05 16:50:40 -070069 * @return the destinationPort
70 */
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070071 public int getDestinationPort() {
alshabibc4901cd2014-09-05 16:50:40 -070072 return this.destinationPort;
73 }
74
75 /**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080076 * Sets TCP destination port.
77 *
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070078 * @param destinationPort the destinationPort to set (unsigned 16 bits integer)
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -080079 * @return this
alshabibc4901cd2014-09-05 16:50:40 -070080 */
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070081 public TCP setDestinationPort(final int destinationPort) {
alshabibc4901cd2014-09-05 16:50:40 -070082 this.destinationPort = destinationPort;
83 return this;
84 }
85
86 /**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080087 * Gets checksum.
88 *
alshabibc4901cd2014-09-05 16:50:40 -070089 * @return the checksum
90 */
91 public short getChecksum() {
92 return this.checksum;
93 }
94
Charles M.C. Chan197a0122015-04-08 18:15:34 +080095 /**
96 * Sets checksum.
97 *
98 * @param checksum the checksum to set
99 * @return this
100 */
101 public TCP setChecksum(final short checksum) {
102 this.checksum = checksum;
103 return this;
104 }
105
106 /**
107 * Gets sequence number.
108 *
109 * @return the sequence number
110 */
alshabibc4901cd2014-09-05 16:50:40 -0700111 public int getSequence() {
112 return this.sequence;
113 }
114
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800115 /**
116 * Sets sequence number.
117 *
118 * @param seq the sequence number to set
119 * @return this
120 */
alshabibc4901cd2014-09-05 16:50:40 -0700121 public TCP setSequence(final int seq) {
122 this.sequence = seq;
123 return this;
124 }
125
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800126 /**
127 * Gets acknowledge number.
128 *
129 * @return the acknowledge number
130 */
alshabibc4901cd2014-09-05 16:50:40 -0700131 public int getAcknowledge() {
132 return this.acknowledge;
133 }
134
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800135 /**
136 * Sets acknowledge number.
137 *
138 * @param ack the acknowledge number to set
139 * @return this
140 */
alshabibc4901cd2014-09-05 16:50:40 -0700141 public TCP setAcknowledge(final int ack) {
142 this.acknowledge = ack;
143 return this;
144 }
145
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800146 /**
147 * Gets offset.
148 *
149 * @return the offset
150 */
alshabibc4901cd2014-09-05 16:50:40 -0700151 public byte getDataOffset() {
152 return this.dataOffset;
153 }
154
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800155 /**
156 * Sets offset.
157 *
158 * @param offset the offset to set
159 * @return this
160 */
alshabibc4901cd2014-09-05 16:50:40 -0700161 public TCP setDataOffset(final byte offset) {
162 this.dataOffset = offset;
163 return this;
164 }
165
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800166 /**
167 * Gets TCP flags.
168 *
169 * @return the TCP flags
170 */
alshabibc4901cd2014-09-05 16:50:40 -0700171 public short getFlags() {
172 return this.flags;
173 }
174
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800175 /**
176 * Sets TCP flags.
177 *
178 * @param flags the TCP flags to set
179 * @return this
180 */
alshabibc4901cd2014-09-05 16:50:40 -0700181 public TCP setFlags(final short flags) {
182 this.flags = flags;
183 return this;
184 }
185
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800186 /**
187 * Gets TCP window size.
188 *
189 * @return the TCP window size
190 */
alshabibc4901cd2014-09-05 16:50:40 -0700191 public short getWindowSize() {
192 return this.windowSize;
193 }
194
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800195 /**
196 * Sets TCP window size.
197 *
198 * @param windowSize the TCP window size to set
199 * @return this
200 */
alshabibc4901cd2014-09-05 16:50:40 -0700201 public TCP setWindowSize(final short windowSize) {
202 this.windowSize = windowSize;
203 return this;
204 }
205
alshabibc4901cd2014-09-05 16:50:40 -0700206 @Override
207 public void resetChecksum() {
208 this.checksum = 0;
209 super.resetChecksum();
210 }
211
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800212 /**
213 * Gets urgent pointer.
214 *
215 * @return the urgent pointer
216 */
217 public short getUrgentPointer() {
alshabibc4901cd2014-09-05 16:50:40 -0700218 return this.urgentPointer;
219 }
220
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800221 /**
222 * Sets urgent pointer.
223 *
224 * @param urgentPointer the urgent pointer to set
225 * @return this
226 */
alshabibc4901cd2014-09-05 16:50:40 -0700227 public TCP setUrgentPointer(final short urgentPointer) {
228 this.urgentPointer = urgentPointer;
229 return this;
230 }
231
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800232 /**
233 * Gets TCP options.
234 *
235 * @return the TCP options
236 */
alshabibc4901cd2014-09-05 16:50:40 -0700237 public byte[] getOptions() {
238 return this.options;
239 }
240
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800241 /**
242 * Sets TCP options.
243 *
244 * @param options the options to set
245 * @return this
246 */
alshabibc4901cd2014-09-05 16:50:40 -0700247 public TCP setOptions(final byte[] options) {
248 this.options = options;
249 this.dataOffset = (byte) (20 + options.length + 3 >> 2);
250 return this;
251 }
252
253 /**
alshabibc4901cd2014-09-05 16:50:40 -0700254 * Serializes the packet. Will compute and set the following fields if they
255 * are set to specific values at the time serialize is called: -checksum : 0
256 * -length : 0
257 */
258 @Override
259 public byte[] serialize() {
260 int length;
261 if (this.dataOffset == 0) {
262 this.dataOffset = 5; // default header length
263 }
264 length = this.dataOffset << 2;
265 byte[] payloadData = null;
266 if (this.payload != null) {
267 this.payload.setParent(this);
268 payloadData = this.payload.serialize();
269 length += payloadData.length;
270 }
271
272 final byte[] data = new byte[length];
273 final ByteBuffer bb = ByteBuffer.wrap(data);
274
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700275 bb.putShort((short) (this.sourcePort & 0xffff));
276 bb.putShort((short) (this.destinationPort & 0xffff));
alshabibc4901cd2014-09-05 16:50:40 -0700277 bb.putInt(this.sequence);
278 bb.putInt(this.acknowledge);
279 bb.putShort((short) (this.flags | this.dataOffset << 12));
280 bb.putShort(this.windowSize);
281 bb.putShort(this.checksum);
282 bb.putShort(this.urgentPointer);
283 if (this.dataOffset > 5) {
284 int padding;
285 bb.put(this.options);
286 padding = (this.dataOffset << 2) - 20 - this.options.length;
287 for (int i = 0; i < padding; i++) {
288 bb.put((byte) 0);
289 }
290 }
291 if (payloadData != null) {
292 bb.put(payloadData);
293 }
294
295 if (this.parent != null && this.parent instanceof IPv4) {
296 ((IPv4) this.parent).setProtocol(IPv4.PROTOCOL_TCP);
297 }
298
299 // compute checksum if needed
300 if (this.checksum == 0) {
301 bb.rewind();
302 int accumulation = 0;
303
304 // compute pseudo header mac
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800305 if (this.parent != null) {
306 if (this.parent instanceof IPv4) {
307 final IPv4 ipv4 = (IPv4) this.parent;
308 accumulation += (ipv4.getSourceAddress() >> 16 & 0xffff)
309 + (ipv4.getSourceAddress() & 0xffff);
310 accumulation += (ipv4.getDestinationAddress() >> 16 & 0xffff)
311 + (ipv4.getDestinationAddress() & 0xffff);
312 accumulation += ipv4.getProtocol() & 0xff;
313 accumulation += length & 0xffff;
314 } else if (this.parent instanceof IPv6) {
315 final IPv6 ipv6 = (IPv6) this.parent;
316 final int bbLength =
317 Ip6Address.BYTE_LENGTH * 2 // IPv6 src, dst
318 + 2 // nextHeader (with padding)
319 + 4; // length
320 final ByteBuffer bbChecksum = ByteBuffer.allocate(bbLength);
321 bbChecksum.put(ipv6.getSourceAddress());
322 bbChecksum.put(ipv6.getDestinationAddress());
323 bbChecksum.put((byte) 0); // padding
324 bbChecksum.put(ipv6.getNextHeader());
325 bbChecksum.putInt(length);
326 bbChecksum.rewind();
327 for (int i = 0; i < bbLength / 2; ++i) {
328 accumulation += 0xffff & bbChecksum.getShort();
329 }
330 }
alshabibc4901cd2014-09-05 16:50:40 -0700331 }
332
333 for (int i = 0; i < length / 2; ++i) {
334 accumulation += 0xffff & bb.getShort();
335 }
336 // pad to an even number of shorts
337 if (length % 2 > 0) {
338 accumulation += (bb.get() & 0xff) << 8;
339 }
340
341 accumulation = (accumulation >> 16 & 0xffff)
342 + (accumulation & 0xffff);
343 this.checksum = (short) (~accumulation & 0xffff);
344 bb.putShort(16, this.checksum);
345 }
346 return data;
347 }
348
Jonathan Hart2a655752015-04-07 16:46:33 -0700349 @Override
350 public IPacket deserialize(final byte[] data, final int offset,
351 final int length) {
352 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700353 this.sourcePort = (bb.getShort() & 0xffff);
354 this.destinationPort = (bb.getShort() & 0xffff);
Jonathan Hart2a655752015-04-07 16:46:33 -0700355 this.sequence = bb.getInt();
356 this.acknowledge = bb.getInt();
357 this.flags = bb.getShort();
358 this.dataOffset = (byte) (this.flags >> 12 & 0xf);
359 this.flags = (short) (this.flags & 0x1ff);
360 this.windowSize = bb.getShort();
361 this.checksum = bb.getShort();
362 this.urgentPointer = bb.getShort();
363 if (this.dataOffset > 5) {
364 int optLength = (this.dataOffset << 2) - 20;
365 if (bb.limit() < bb.position() + optLength) {
366 optLength = bb.limit() - bb.position();
367 }
368 try {
369 this.options = new byte[optLength];
370 bb.get(this.options, 0, optLength);
371 } catch (final IndexOutOfBoundsException e) {
372 this.options = null;
373 }
374 }
375
376 this.payload = new Data();
377 this.payload = this.payload.deserialize(data, bb.position(), bb.limit()
378 - bb.position());
379 this.payload.setParent(this);
380 return this;
381 }
382
alshabibc4901cd2014-09-05 16:50:40 -0700383 /*
384 * (non-Javadoc)
385 *
386 * @see java.lang.Object#hashCode()
387 */
388 @Override
389 public int hashCode() {
390 final int prime = 5807;
391 int result = super.hashCode();
392 result = prime * result + this.checksum;
393 result = prime * result + this.destinationPort;
394 result = prime * result + this.sourcePort;
395 return result;
396 }
397
398 /*
399 * (non-Javadoc)
400 *
401 * @see java.lang.Object#equals(java.lang.Object)
402 */
403 @Override
404 public boolean equals(final Object obj) {
405 if (this == obj) {
406 return true;
407 }
408 if (!super.equals(obj)) {
409 return false;
410 }
411 if (!(obj instanceof TCP)) {
412 return false;
413 }
414 final TCP other = (TCP) obj;
415 // May want to compare fields based on the flags set
416 return this.checksum == other.checksum
417 && this.destinationPort == other.destinationPort
418 && this.sourcePort == other.sourcePort
419 && this.sequence == other.sequence
420 && this.acknowledge == other.acknowledge
421 && this.dataOffset == other.dataOffset
422 && this.flags == other.flags
423 && this.windowSize == other.windowSize
424 && this.urgentPointer == other.urgentPointer
HIGUCHI Yutad57cdc42015-09-04 14:59:06 -0700425 && (this.dataOffset == 5 || Arrays.equals(this.options, other.options));
alshabibc4901cd2014-09-05 16:50:40 -0700426 }
427
Jonathan Hart2a655752015-04-07 16:46:33 -0700428 /**
429 * Deserializer function for TCP packets.
430 *
431 * @return deserializer function
432 */
433 public static Deserializer<TCP> deserializer() {
434 return (data, offset, length) -> {
435 checkInput(data, offset, length, TCP_HEADER_LENGTH);
alshabibc4901cd2014-09-05 16:50:40 -0700436
Jonathan Hart2a655752015-04-07 16:46:33 -0700437 TCP tcp = new TCP();
438
439 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700440 tcp.sourcePort = (bb.getShort() & 0xffff);
441 tcp.destinationPort = (bb.getShort() & 0xffff);
Jonathan Hart2a655752015-04-07 16:46:33 -0700442 tcp.sequence = bb.getInt();
443 tcp.acknowledge = bb.getInt();
444 tcp.flags = bb.getShort();
445 tcp.dataOffset = (byte) (tcp.flags >> 12 & 0xf);
446 tcp.flags = (short) (tcp.flags & 0x1ff);
447 tcp.windowSize = bb.getShort();
448 tcp.checksum = bb.getShort();
449 tcp.urgentPointer = bb.getShort();
450 if (tcp.dataOffset > 5) {
451 int optLength = (tcp.dataOffset << 2) - 20;
452 checkHeaderLength(length, TCP_HEADER_LENGTH + tcp.dataOffset);
453 tcp.options = new byte[optLength];
454 bb.get(tcp.options, 0, optLength);
455 }
456
457 tcp.payload = Data.deserializer()
458 .deserialize(data, bb.position(), bb.limit() - bb.position());
459 tcp.payload.setParent(tcp);
460 return tcp;
461 };
alshabibc4901cd2014-09-05 16:50:40 -0700462 }
Jian Li5fc14292015-12-04 11:30:46 -0800463
464 @Override
465 public String toString() {
466 return toStringHelper(getClass())
467 .add("sourcePort", Integer.toString(sourcePort))
468 .add("destinationPort", Integer.toString(destinationPort))
469 .add("sequence", Integer.toString(sequence))
470 .add("acknowledge", Integer.toString(acknowledge))
471 .add("dataOffset", Byte.toString(dataOffset))
472 .add("flags", Short.toString(flags))
473 .add("windowSize", Short.toString(windowSize))
474 .add("checksum", Short.toString(checksum))
475 .add("urgentPointer", Short.toString(urgentPointer))
476 .add("options", Arrays.toString(options))
477 .toString();
478 }
alshabibc4901cd2014-09-05 16:50:40 -0700479}