blob: e089f272eb8f434ab719e5f3eadd5e7d9cffc216 [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;
HIGUCHI Yutad57cdc42015-09-04 14:59:06 -070022import java.util.Arrays;
alshabibc4901cd2014-09-05 16:50:40 -070023
Jonathan Hart2a655752015-04-07 16:46:33 -070024import static org.onlab.packet.PacketUtils.*;
25
alshabibc4901cd2014-09-05 16:50:40 -070026/**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080027 * Implements TCP packet format.
alshabibc4901cd2014-09-05 16:50:40 -070028 */
29
30public class TCP extends BasePacket {
Jonathan Hart2a655752015-04-07 16:46:33 -070031
32 private static final short TCP_HEADER_LENGTH = 20;
33
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070034 protected int sourcePort;
35 protected int destinationPort;
alshabibc4901cd2014-09-05 16:50:40 -070036 protected int sequence;
37 protected int acknowledge;
38 protected byte dataOffset;
39 protected short flags;
40 protected short windowSize;
41 protected short checksum;
42 protected short urgentPointer;
43 protected byte[] options;
44
45 /**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080046 * Gets TCP source port.
47 *
48 * @return TCP source port
alshabibc4901cd2014-09-05 16:50:40 -070049 */
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070050 public int getSourcePort() {
alshabibc4901cd2014-09-05 16:50:40 -070051 return this.sourcePort;
52 }
53
54 /**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080055 * Sets TCP source port.
56 *
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070057 * @param sourcePort the sourcePort to set (unsigned 16 bits integer)
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -080058 * @return this
alshabibc4901cd2014-09-05 16:50:40 -070059 */
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070060 public TCP setSourcePort(final int sourcePort) {
alshabibc4901cd2014-09-05 16:50:40 -070061 this.sourcePort = sourcePort;
62 return this;
63 }
64
65 /**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080066 * Gets TCP destination port.
67 *
alshabibc4901cd2014-09-05 16:50:40 -070068 * @return the destinationPort
69 */
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070070 public int getDestinationPort() {
alshabibc4901cd2014-09-05 16:50:40 -070071 return this.destinationPort;
72 }
73
74 /**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080075 * Sets TCP destination port.
76 *
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070077 * @param destinationPort the destinationPort to set (unsigned 16 bits integer)
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -080078 * @return this
alshabibc4901cd2014-09-05 16:50:40 -070079 */
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070080 public TCP setDestinationPort(final int destinationPort) {
alshabibc4901cd2014-09-05 16:50:40 -070081 this.destinationPort = destinationPort;
82 return this;
83 }
84
85 /**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080086 * Gets checksum.
87 *
alshabibc4901cd2014-09-05 16:50:40 -070088 * @return the checksum
89 */
90 public short getChecksum() {
91 return this.checksum;
92 }
93
Charles M.C. Chan197a0122015-04-08 18:15:34 +080094 /**
95 * Sets checksum.
96 *
97 * @param checksum the checksum to set
98 * @return this
99 */
100 public TCP setChecksum(final short checksum) {
101 this.checksum = checksum;
102 return this;
103 }
104
105 /**
106 * Gets sequence number.
107 *
108 * @return the sequence number
109 */
alshabibc4901cd2014-09-05 16:50:40 -0700110 public int getSequence() {
111 return this.sequence;
112 }
113
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800114 /**
115 * Sets sequence number.
116 *
117 * @param seq the sequence number to set
118 * @return this
119 */
alshabibc4901cd2014-09-05 16:50:40 -0700120 public TCP setSequence(final int seq) {
121 this.sequence = seq;
122 return this;
123 }
124
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800125 /**
126 * Gets acknowledge number.
127 *
128 * @return the acknowledge number
129 */
alshabibc4901cd2014-09-05 16:50:40 -0700130 public int getAcknowledge() {
131 return this.acknowledge;
132 }
133
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800134 /**
135 * Sets acknowledge number.
136 *
137 * @param ack the acknowledge number to set
138 * @return this
139 */
alshabibc4901cd2014-09-05 16:50:40 -0700140 public TCP setAcknowledge(final int ack) {
141 this.acknowledge = ack;
142 return this;
143 }
144
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800145 /**
146 * Gets offset.
147 *
148 * @return the offset
149 */
alshabibc4901cd2014-09-05 16:50:40 -0700150 public byte getDataOffset() {
151 return this.dataOffset;
152 }
153
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800154 /**
155 * Sets offset.
156 *
157 * @param offset the offset to set
158 * @return this
159 */
alshabibc4901cd2014-09-05 16:50:40 -0700160 public TCP setDataOffset(final byte offset) {
161 this.dataOffset = offset;
162 return this;
163 }
164
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800165 /**
166 * Gets TCP flags.
167 *
168 * @return the TCP flags
169 */
alshabibc4901cd2014-09-05 16:50:40 -0700170 public short getFlags() {
171 return this.flags;
172 }
173
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800174 /**
175 * Sets TCP flags.
176 *
177 * @param flags the TCP flags to set
178 * @return this
179 */
alshabibc4901cd2014-09-05 16:50:40 -0700180 public TCP setFlags(final short flags) {
181 this.flags = flags;
182 return this;
183 }
184
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800185 /**
186 * Gets TCP window size.
187 *
188 * @return the TCP window size
189 */
alshabibc4901cd2014-09-05 16:50:40 -0700190 public short getWindowSize() {
191 return this.windowSize;
192 }
193
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800194 /**
195 * Sets TCP window size.
196 *
197 * @param windowSize the TCP window size to set
198 * @return this
199 */
alshabibc4901cd2014-09-05 16:50:40 -0700200 public TCP setWindowSize(final short windowSize) {
201 this.windowSize = windowSize;
202 return this;
203 }
204
alshabibc4901cd2014-09-05 16:50:40 -0700205 @Override
206 public void resetChecksum() {
207 this.checksum = 0;
208 super.resetChecksum();
209 }
210
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800211 /**
212 * Gets urgent pointer.
213 *
214 * @return the urgent pointer
215 */
216 public short getUrgentPointer() {
alshabibc4901cd2014-09-05 16:50:40 -0700217 return this.urgentPointer;
218 }
219
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800220 /**
221 * Sets urgent pointer.
222 *
223 * @param urgentPointer the urgent pointer to set
224 * @return this
225 */
alshabibc4901cd2014-09-05 16:50:40 -0700226 public TCP setUrgentPointer(final short urgentPointer) {
227 this.urgentPointer = urgentPointer;
228 return this;
229 }
230
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800231 /**
232 * Gets TCP options.
233 *
234 * @return the TCP options
235 */
alshabibc4901cd2014-09-05 16:50:40 -0700236 public byte[] getOptions() {
237 return this.options;
238 }
239
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800240 /**
241 * Sets TCP options.
242 *
243 * @param options the options to set
244 * @return this
245 */
alshabibc4901cd2014-09-05 16:50:40 -0700246 public TCP setOptions(final byte[] options) {
247 this.options = options;
248 this.dataOffset = (byte) (20 + options.length + 3 >> 2);
249 return this;
250 }
251
252 /**
alshabibc4901cd2014-09-05 16:50:40 -0700253 * Serializes the packet. Will compute and set the following fields if they
254 * are set to specific values at the time serialize is called: -checksum : 0
255 * -length : 0
256 */
257 @Override
258 public byte[] serialize() {
259 int length;
260 if (this.dataOffset == 0) {
261 this.dataOffset = 5; // default header length
262 }
263 length = this.dataOffset << 2;
264 byte[] payloadData = null;
265 if (this.payload != null) {
266 this.payload.setParent(this);
267 payloadData = this.payload.serialize();
268 length += payloadData.length;
269 }
270
271 final byte[] data = new byte[length];
272 final ByteBuffer bb = ByteBuffer.wrap(data);
273
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700274 bb.putShort((short) (this.sourcePort & 0xffff));
275 bb.putShort((short) (this.destinationPort & 0xffff));
alshabibc4901cd2014-09-05 16:50:40 -0700276 bb.putInt(this.sequence);
277 bb.putInt(this.acknowledge);
278 bb.putShort((short) (this.flags | this.dataOffset << 12));
279 bb.putShort(this.windowSize);
280 bb.putShort(this.checksum);
281 bb.putShort(this.urgentPointer);
282 if (this.dataOffset > 5) {
283 int padding;
284 bb.put(this.options);
285 padding = (this.dataOffset << 2) - 20 - this.options.length;
286 for (int i = 0; i < padding; i++) {
287 bb.put((byte) 0);
288 }
289 }
290 if (payloadData != null) {
291 bb.put(payloadData);
292 }
293
294 if (this.parent != null && this.parent instanceof IPv4) {
295 ((IPv4) this.parent).setProtocol(IPv4.PROTOCOL_TCP);
296 }
297
298 // compute checksum if needed
299 if (this.checksum == 0) {
300 bb.rewind();
301 int accumulation = 0;
302
303 // compute pseudo header mac
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800304 if (this.parent != null) {
305 if (this.parent instanceof IPv4) {
306 final IPv4 ipv4 = (IPv4) this.parent;
307 accumulation += (ipv4.getSourceAddress() >> 16 & 0xffff)
308 + (ipv4.getSourceAddress() & 0xffff);
309 accumulation += (ipv4.getDestinationAddress() >> 16 & 0xffff)
310 + (ipv4.getDestinationAddress() & 0xffff);
311 accumulation += ipv4.getProtocol() & 0xff;
312 accumulation += length & 0xffff;
313 } else if (this.parent instanceof IPv6) {
314 final IPv6 ipv6 = (IPv6) this.parent;
315 final int bbLength =
316 Ip6Address.BYTE_LENGTH * 2 // IPv6 src, dst
317 + 2 // nextHeader (with padding)
318 + 4; // length
319 final ByteBuffer bbChecksum = ByteBuffer.allocate(bbLength);
320 bbChecksum.put(ipv6.getSourceAddress());
321 bbChecksum.put(ipv6.getDestinationAddress());
322 bbChecksum.put((byte) 0); // padding
323 bbChecksum.put(ipv6.getNextHeader());
324 bbChecksum.putInt(length);
325 bbChecksum.rewind();
326 for (int i = 0; i < bbLength / 2; ++i) {
327 accumulation += 0xffff & bbChecksum.getShort();
328 }
329 }
alshabibc4901cd2014-09-05 16:50:40 -0700330 }
331
332 for (int i = 0; i < length / 2; ++i) {
333 accumulation += 0xffff & bb.getShort();
334 }
335 // pad to an even number of shorts
336 if (length % 2 > 0) {
337 accumulation += (bb.get() & 0xff) << 8;
338 }
339
340 accumulation = (accumulation >> 16 & 0xffff)
341 + (accumulation & 0xffff);
342 this.checksum = (short) (~accumulation & 0xffff);
343 bb.putShort(16, this.checksum);
344 }
345 return data;
346 }
347
Jonathan Hart2a655752015-04-07 16:46:33 -0700348 @Override
349 public IPacket deserialize(final byte[] data, final int offset,
350 final int length) {
351 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700352 this.sourcePort = (bb.getShort() & 0xffff);
353 this.destinationPort = (bb.getShort() & 0xffff);
Jonathan Hart2a655752015-04-07 16:46:33 -0700354 this.sequence = bb.getInt();
355 this.acknowledge = bb.getInt();
356 this.flags = bb.getShort();
357 this.dataOffset = (byte) (this.flags >> 12 & 0xf);
358 this.flags = (short) (this.flags & 0x1ff);
359 this.windowSize = bb.getShort();
360 this.checksum = bb.getShort();
361 this.urgentPointer = bb.getShort();
362 if (this.dataOffset > 5) {
363 int optLength = (this.dataOffset << 2) - 20;
364 if (bb.limit() < bb.position() + optLength) {
365 optLength = bb.limit() - bb.position();
366 }
367 try {
368 this.options = new byte[optLength];
369 bb.get(this.options, 0, optLength);
370 } catch (final IndexOutOfBoundsException e) {
371 this.options = null;
372 }
373 }
374
375 this.payload = new Data();
376 this.payload = this.payload.deserialize(data, bb.position(), bb.limit()
377 - bb.position());
378 this.payload.setParent(this);
379 return this;
380 }
381
alshabibc4901cd2014-09-05 16:50:40 -0700382 /*
383 * (non-Javadoc)
384 *
385 * @see java.lang.Object#hashCode()
386 */
387 @Override
388 public int hashCode() {
389 final int prime = 5807;
390 int result = super.hashCode();
391 result = prime * result + this.checksum;
392 result = prime * result + this.destinationPort;
393 result = prime * result + this.sourcePort;
394 return result;
395 }
396
397 /*
398 * (non-Javadoc)
399 *
400 * @see java.lang.Object#equals(java.lang.Object)
401 */
402 @Override
403 public boolean equals(final Object obj) {
404 if (this == obj) {
405 return true;
406 }
407 if (!super.equals(obj)) {
408 return false;
409 }
410 if (!(obj instanceof TCP)) {
411 return false;
412 }
413 final TCP other = (TCP) obj;
414 // May want to compare fields based on the flags set
415 return this.checksum == other.checksum
416 && this.destinationPort == other.destinationPort
417 && this.sourcePort == other.sourcePort
418 && this.sequence == other.sequence
419 && this.acknowledge == other.acknowledge
420 && this.dataOffset == other.dataOffset
421 && this.flags == other.flags
422 && this.windowSize == other.windowSize
423 && this.urgentPointer == other.urgentPointer
HIGUCHI Yutad57cdc42015-09-04 14:59:06 -0700424 && (this.dataOffset == 5 || Arrays.equals(this.options, other.options));
alshabibc4901cd2014-09-05 16:50:40 -0700425 }
426
Jonathan Hart2a655752015-04-07 16:46:33 -0700427 /**
428 * Deserializer function for TCP packets.
429 *
430 * @return deserializer function
431 */
432 public static Deserializer<TCP> deserializer() {
433 return (data, offset, length) -> {
434 checkInput(data, offset, length, TCP_HEADER_LENGTH);
alshabibc4901cd2014-09-05 16:50:40 -0700435
Jonathan Hart2a655752015-04-07 16:46:33 -0700436 TCP tcp = new TCP();
437
438 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700439 tcp.sourcePort = (bb.getShort() & 0xffff);
440 tcp.destinationPort = (bb.getShort() & 0xffff);
Jonathan Hart2a655752015-04-07 16:46:33 -0700441 tcp.sequence = bb.getInt();
442 tcp.acknowledge = bb.getInt();
443 tcp.flags = bb.getShort();
444 tcp.dataOffset = (byte) (tcp.flags >> 12 & 0xf);
445 tcp.flags = (short) (tcp.flags & 0x1ff);
446 tcp.windowSize = bb.getShort();
447 tcp.checksum = bb.getShort();
448 tcp.urgentPointer = bb.getShort();
449 if (tcp.dataOffset > 5) {
450 int optLength = (tcp.dataOffset << 2) - 20;
451 checkHeaderLength(length, TCP_HEADER_LENGTH + tcp.dataOffset);
452 tcp.options = new byte[optLength];
453 bb.get(tcp.options, 0, optLength);
454 }
455
456 tcp.payload = Data.deserializer()
457 .deserialize(data, bb.position(), bb.limit() - bb.position());
458 tcp.payload.setParent(tcp);
459 return tcp;
460 };
alshabibc4901cd2014-09-05 16:50:40 -0700461 }
462}