blob: 3b92c83fc94b7b45f872259e6b2f4eb493ef1aee [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
23/**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080024 * Implements TCP packet format.
alshabibc4901cd2014-09-05 16:50:40 -070025 */
26
27public class TCP extends BasePacket {
28 protected short sourcePort;
29 protected short destinationPort;
30 protected int sequence;
31 protected int acknowledge;
32 protected byte dataOffset;
33 protected short flags;
34 protected short windowSize;
35 protected short checksum;
36 protected short urgentPointer;
37 protected byte[] options;
38
39 /**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080040 * Gets TCP source port.
41 *
42 * @return TCP source port
alshabibc4901cd2014-09-05 16:50:40 -070043 */
44 public short getSourcePort() {
45 return this.sourcePort;
46 }
47
48 /**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080049 * Sets TCP source port.
50 *
51 * @param sourcePort the sourcePort to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -080052 * @return this
alshabibc4901cd2014-09-05 16:50:40 -070053 */
54 public TCP setSourcePort(final short sourcePort) {
55 this.sourcePort = sourcePort;
56 return this;
57 }
58
59 /**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080060 * Gets TCP destination port.
61 *
alshabibc4901cd2014-09-05 16:50:40 -070062 * @return the destinationPort
63 */
64 public short getDestinationPort() {
65 return this.destinationPort;
66 }
67
68 /**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080069 * Sets TCP destination port.
70 *
71 * @param destinationPort the destinationPort to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -080072 * @return this
alshabibc4901cd2014-09-05 16:50:40 -070073 */
74 public TCP setDestinationPort(final short destinationPort) {
75 this.destinationPort = destinationPort;
76 return this;
77 }
78
79 /**
Charles M.C. Chan197a0122015-04-08 18:15:34 +080080 * Gets checksum.
81 *
alshabibc4901cd2014-09-05 16:50:40 -070082 * @return the checksum
83 */
84 public short getChecksum() {
85 return this.checksum;
86 }
87
Charles M.C. Chan197a0122015-04-08 18:15:34 +080088 /**
89 * Sets checksum.
90 *
91 * @param checksum the checksum to set
92 * @return this
93 */
94 public TCP setChecksum(final short checksum) {
95 this.checksum = checksum;
96 return this;
97 }
98
99 /**
100 * Gets sequence number.
101 *
102 * @return the sequence number
103 */
alshabibc4901cd2014-09-05 16:50:40 -0700104 public int getSequence() {
105 return this.sequence;
106 }
107
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800108 /**
109 * Sets sequence number.
110 *
111 * @param seq the sequence number to set
112 * @return this
113 */
alshabibc4901cd2014-09-05 16:50:40 -0700114 public TCP setSequence(final int seq) {
115 this.sequence = seq;
116 return this;
117 }
118
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800119 /**
120 * Gets acknowledge number.
121 *
122 * @return the acknowledge number
123 */
alshabibc4901cd2014-09-05 16:50:40 -0700124 public int getAcknowledge() {
125 return this.acknowledge;
126 }
127
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800128 /**
129 * Sets acknowledge number.
130 *
131 * @param ack the acknowledge number to set
132 * @return this
133 */
alshabibc4901cd2014-09-05 16:50:40 -0700134 public TCP setAcknowledge(final int ack) {
135 this.acknowledge = ack;
136 return this;
137 }
138
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800139 /**
140 * Gets offset.
141 *
142 * @return the offset
143 */
alshabibc4901cd2014-09-05 16:50:40 -0700144 public byte getDataOffset() {
145 return this.dataOffset;
146 }
147
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800148 /**
149 * Sets offset.
150 *
151 * @param offset the offset to set
152 * @return this
153 */
alshabibc4901cd2014-09-05 16:50:40 -0700154 public TCP setDataOffset(final byte offset) {
155 this.dataOffset = offset;
156 return this;
157 }
158
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800159 /**
160 * Gets TCP flags.
161 *
162 * @return the TCP flags
163 */
alshabibc4901cd2014-09-05 16:50:40 -0700164 public short getFlags() {
165 return this.flags;
166 }
167
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800168 /**
169 * Sets TCP flags.
170 *
171 * @param flags the TCP flags to set
172 * @return this
173 */
alshabibc4901cd2014-09-05 16:50:40 -0700174 public TCP setFlags(final short flags) {
175 this.flags = flags;
176 return this;
177 }
178
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800179 /**
180 * Gets TCP window size.
181 *
182 * @return the TCP window size
183 */
alshabibc4901cd2014-09-05 16:50:40 -0700184 public short getWindowSize() {
185 return this.windowSize;
186 }
187
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800188 /**
189 * Sets TCP window size.
190 *
191 * @param windowSize the TCP window size to set
192 * @return this
193 */
alshabibc4901cd2014-09-05 16:50:40 -0700194 public TCP setWindowSize(final short windowSize) {
195 this.windowSize = windowSize;
196 return this;
197 }
198
alshabibc4901cd2014-09-05 16:50:40 -0700199 @Override
200 public void resetChecksum() {
201 this.checksum = 0;
202 super.resetChecksum();
203 }
204
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800205 /**
206 * Gets urgent pointer.
207 *
208 * @return the urgent pointer
209 */
210 public short getUrgentPointer() {
alshabibc4901cd2014-09-05 16:50:40 -0700211 return this.urgentPointer;
212 }
213
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800214 /**
215 * Sets urgent pointer.
216 *
217 * @param urgentPointer the urgent pointer to set
218 * @return this
219 */
alshabibc4901cd2014-09-05 16:50:40 -0700220 public TCP setUrgentPointer(final short urgentPointer) {
221 this.urgentPointer = urgentPointer;
222 return this;
223 }
224
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800225 /**
226 * Gets TCP options.
227 *
228 * @return the TCP options
229 */
alshabibc4901cd2014-09-05 16:50:40 -0700230 public byte[] getOptions() {
231 return this.options;
232 }
233
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800234 /**
235 * Sets TCP options.
236 *
237 * @param options the options to set
238 * @return this
239 */
alshabibc4901cd2014-09-05 16:50:40 -0700240 public TCP setOptions(final byte[] options) {
241 this.options = options;
242 this.dataOffset = (byte) (20 + options.length + 3 >> 2);
243 return this;
244 }
245
246 /**
alshabibc4901cd2014-09-05 16:50:40 -0700247 * Serializes the packet. Will compute and set the following fields if they
248 * are set to specific values at the time serialize is called: -checksum : 0
249 * -length : 0
250 */
251 @Override
252 public byte[] serialize() {
253 int length;
254 if (this.dataOffset == 0) {
255 this.dataOffset = 5; // default header length
256 }
257 length = this.dataOffset << 2;
258 byte[] payloadData = null;
259 if (this.payload != null) {
260 this.payload.setParent(this);
261 payloadData = this.payload.serialize();
262 length += payloadData.length;
263 }
264
265 final byte[] data = new byte[length];
266 final ByteBuffer bb = ByteBuffer.wrap(data);
267
268 bb.putShort(this.sourcePort);
269 bb.putShort(this.destinationPort);
270 bb.putInt(this.sequence);
271 bb.putInt(this.acknowledge);
272 bb.putShort((short) (this.flags | this.dataOffset << 12));
273 bb.putShort(this.windowSize);
274 bb.putShort(this.checksum);
275 bb.putShort(this.urgentPointer);
276 if (this.dataOffset > 5) {
277 int padding;
278 bb.put(this.options);
279 padding = (this.dataOffset << 2) - 20 - this.options.length;
280 for (int i = 0; i < padding; i++) {
281 bb.put((byte) 0);
282 }
283 }
284 if (payloadData != null) {
285 bb.put(payloadData);
286 }
287
288 if (this.parent != null && this.parent instanceof IPv4) {
289 ((IPv4) this.parent).setProtocol(IPv4.PROTOCOL_TCP);
290 }
291
292 // compute checksum if needed
293 if (this.checksum == 0) {
294 bb.rewind();
295 int accumulation = 0;
296
297 // compute pseudo header mac
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800298 if (this.parent != null) {
299 if (this.parent instanceof IPv4) {
300 final IPv4 ipv4 = (IPv4) this.parent;
301 accumulation += (ipv4.getSourceAddress() >> 16 & 0xffff)
302 + (ipv4.getSourceAddress() & 0xffff);
303 accumulation += (ipv4.getDestinationAddress() >> 16 & 0xffff)
304 + (ipv4.getDestinationAddress() & 0xffff);
305 accumulation += ipv4.getProtocol() & 0xff;
306 accumulation += length & 0xffff;
307 } else if (this.parent instanceof IPv6) {
308 final IPv6 ipv6 = (IPv6) this.parent;
309 final int bbLength =
310 Ip6Address.BYTE_LENGTH * 2 // IPv6 src, dst
311 + 2 // nextHeader (with padding)
312 + 4; // length
313 final ByteBuffer bbChecksum = ByteBuffer.allocate(bbLength);
314 bbChecksum.put(ipv6.getSourceAddress());
315 bbChecksum.put(ipv6.getDestinationAddress());
316 bbChecksum.put((byte) 0); // padding
317 bbChecksum.put(ipv6.getNextHeader());
318 bbChecksum.putInt(length);
319 bbChecksum.rewind();
320 for (int i = 0; i < bbLength / 2; ++i) {
321 accumulation += 0xffff & bbChecksum.getShort();
322 }
323 }
alshabibc4901cd2014-09-05 16:50:40 -0700324 }
325
326 for (int i = 0; i < length / 2; ++i) {
327 accumulation += 0xffff & bb.getShort();
328 }
329 // pad to an even number of shorts
330 if (length % 2 > 0) {
331 accumulation += (bb.get() & 0xff) << 8;
332 }
333
334 accumulation = (accumulation >> 16 & 0xffff)
335 + (accumulation & 0xffff);
336 this.checksum = (short) (~accumulation & 0xffff);
337 bb.putShort(16, this.checksum);
338 }
339 return data;
340 }
341
342 /*
343 * (non-Javadoc)
344 *
345 * @see java.lang.Object#hashCode()
346 */
347 @Override
348 public int hashCode() {
349 final int prime = 5807;
350 int result = super.hashCode();
351 result = prime * result + this.checksum;
352 result = prime * result + this.destinationPort;
353 result = prime * result + this.sourcePort;
354 return result;
355 }
356
357 /*
358 * (non-Javadoc)
359 *
360 * @see java.lang.Object#equals(java.lang.Object)
361 */
362 @Override
363 public boolean equals(final Object obj) {
364 if (this == obj) {
365 return true;
366 }
367 if (!super.equals(obj)) {
368 return false;
369 }
370 if (!(obj instanceof TCP)) {
371 return false;
372 }
373 final TCP other = (TCP) obj;
374 // May want to compare fields based on the flags set
375 return this.checksum == other.checksum
376 && this.destinationPort == other.destinationPort
377 && this.sourcePort == other.sourcePort
378 && this.sequence == other.sequence
379 && this.acknowledge == other.acknowledge
380 && this.dataOffset == other.dataOffset
381 && this.flags == other.flags
382 && this.windowSize == other.windowSize
383 && this.urgentPointer == other.urgentPointer
384 && (this.dataOffset == 5 || this.options.equals(other.options));
385 }
386
387 @Override
388 public IPacket deserialize(final byte[] data, final int offset,
389 final int length) {
390 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
391 this.sourcePort = bb.getShort();
392 this.destinationPort = bb.getShort();
393 this.sequence = bb.getInt();
394 this.acknowledge = bb.getInt();
395 this.flags = bb.getShort();
396 this.dataOffset = (byte) (this.flags >> 12 & 0xf);
397 this.flags = (short) (this.flags & 0x1ff);
398 this.windowSize = bb.getShort();
399 this.checksum = bb.getShort();
400 this.urgentPointer = bb.getShort();
401 if (this.dataOffset > 5) {
402 int optLength = (this.dataOffset << 2) - 20;
403 if (bb.limit() < bb.position() + optLength) {
404 optLength = bb.limit() - bb.position();
405 }
406 try {
407 this.options = new byte[optLength];
408 bb.get(this.options, 0, optLength);
409 } catch (final IndexOutOfBoundsException e) {
410 this.options = null;
411 }
412 }
413
414 this.payload = new Data();
415 this.payload = this.payload.deserialize(data, bb.position(), bb.limit()
416 - bb.position());
417 this.payload.setParent(this);
418 return this;
419 }
420}