blob: 99d36c504e5c537cb316db3560cc8fbde285ee90 [file] [log] [blame]
markchien150e1912018-12-27 22:49:51 +08001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * 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
7 *
8 * 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.
15 */
16package android.net;
17
18import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
19
junyulai2ed5d4c2019-01-30 19:11:45 +080020import android.annotation.NonNull;
markchien150e1912018-12-27 22:49:51 +080021import android.annotation.Nullable;
22import android.net.SocketKeepalive.InvalidPacketException;
23import android.net.util.IpUtils;
24import android.os.Parcel;
25import android.os.Parcelable;
26import android.system.OsConstants;
27
28import java.net.Inet4Address;
29import java.net.InetAddress;
30import java.nio.ByteBuffer;
31import java.nio.ByteOrder;
32import java.util.Objects;
33
34/**
35 * Represents the actual tcp keep alive packets which will be used for hardware offload.
36 * @hide
37 */
38public class TcpKeepalivePacketData extends KeepalivePacketData implements Parcelable {
39 private static final String TAG = "TcpKeepalivePacketData";
40
41 /** TCP sequence number. */
42 public final int tcpSeq;
43
44 /** TCP ACK number. */
45 public final int tcpAck;
46
47 /** TCP RCV window. */
48 public final int tcpWnd;
49
50 /** TCP RCV window scale. */
51 public final int tcpWndScale;
52
53 private static final int IPV4_HEADER_LENGTH = 20;
54 private static final int IPV6_HEADER_LENGTH = 40;
55 private static final int TCP_HEADER_LENGTH = 20;
56
57 // This should only be constructed via static factory methods, such as
58 // tcpKeepalivePacket.
59 private TcpKeepalivePacketData(TcpSocketInfo tcpDetails, byte[] data)
60 throws InvalidPacketException {
61 super(tcpDetails.srcAddress, tcpDetails.srcPort, tcpDetails.dstAddress,
62 tcpDetails.dstPort, data);
63 tcpSeq = tcpDetails.seq;
64 tcpAck = tcpDetails.ack;
65 // In the packet, the window is shifted right by the window scale.
66 tcpWnd = tcpDetails.rcvWnd;
67 tcpWndScale = tcpDetails.rcvWndScale;
68 }
69
70 /**
71 * Factory method to create tcp keepalive packet structure.
72 */
73 public static TcpKeepalivePacketData tcpKeepalivePacket(
74 TcpSocketInfo tcpDetails) throws InvalidPacketException {
75 final byte[] packet;
76 if ((tcpDetails.srcAddress instanceof Inet4Address)
77 && (tcpDetails.dstAddress instanceof Inet4Address)) {
78 packet = buildV4Packet(tcpDetails);
79 } else {
80 // TODO: support ipv6
81 throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
82 }
83
84 return new TcpKeepalivePacketData(tcpDetails, packet);
85 }
86
87 /**
88 * Build ipv4 tcp keepalive packet, not including the link-layer header.
89 */
90 // TODO : if this code is ever moved to the network stack, factorize constants with the ones
91 // over there.
92 private static byte[] buildV4Packet(TcpSocketInfo tcpDetails) {
93 final int length = IPV4_HEADER_LENGTH + TCP_HEADER_LENGTH;
94 ByteBuffer buf = ByteBuffer.allocate(length);
95 buf.order(ByteOrder.BIG_ENDIAN);
96 // IP version and TOS. TODO : fetch this from getsockopt(SOL_IP, IP_TOS)
97 buf.putShort((short) 0x4500);
98 buf.putShort((short) length);
99 buf.putInt(0x4000); // ID, flags=DF, offset
100 // TODO : fetch TTL from getsockopt(SOL_IP, IP_TTL)
101 buf.put((byte) 64);
102 buf.put((byte) OsConstants.IPPROTO_TCP);
103 final int ipChecksumOffset = buf.position();
104 buf.putShort((short) 0); // IP checksum
105 buf.put(tcpDetails.srcAddress.getAddress());
106 buf.put(tcpDetails.dstAddress.getAddress());
107 buf.putShort((short) tcpDetails.srcPort);
108 buf.putShort((short) tcpDetails.dstPort);
109 buf.putInt(tcpDetails.seq); // Sequence Number
110 buf.putInt(tcpDetails.ack); // ACK
111 buf.putShort((short) 0x5010); // TCP length=5, flags=ACK
112 buf.putShort((short) (tcpDetails.rcvWnd >> tcpDetails.rcvWndScale)); // Window size
113 final int tcpChecksumOffset = buf.position();
114 buf.putShort((short) 0); // TCP checksum
115 // URG is not set therefore the urgent pointer is not included
116 buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0));
117 buf.putShort(tcpChecksumOffset, IpUtils.tcpChecksum(
118 buf, 0, IPV4_HEADER_LENGTH, TCP_HEADER_LENGTH));
119
120 return buf.array();
121 }
122
123 // TODO: add buildV6Packet.
124
125 /** Represents tcp/ip information. */
junyulai2ed5d4c2019-01-30 19:11:45 +0800126 // TODO: Replace TcpSocketInfo with TcpKeepalivePacketDataParcelable.
markchien150e1912018-12-27 22:49:51 +0800127 public static class TcpSocketInfo {
128 public final InetAddress srcAddress;
129 public final InetAddress dstAddress;
130 public final int srcPort;
131 public final int dstPort;
132 public final int seq;
133 public final int ack;
134 public final int rcvWnd;
135 public final int rcvWndScale;
136
137 public TcpSocketInfo(InetAddress sAddr, int sPort, InetAddress dAddr,
138 int dPort, int writeSeq, int readSeq, int rWnd, int rWndScale) {
139 srcAddress = sAddr;
140 dstAddress = dAddr;
141 srcPort = sPort;
142 dstPort = dPort;
143 seq = writeSeq;
144 ack = readSeq;
145 rcvWnd = rWnd;
146 rcvWndScale = rWndScale;
147 }
148 }
149
150 @Override
151 public boolean equals(@Nullable final Object o) {
152 if (!(o instanceof TcpKeepalivePacketData)) return false;
153 final TcpKeepalivePacketData other = (TcpKeepalivePacketData) o;
154 return this.srcAddress.equals(other.srcAddress)
155 && this.dstAddress.equals(other.dstAddress)
156 && this.srcPort == other.srcPort
157 && this.dstPort == other.dstPort
158 && this.tcpAck == other.tcpAck
159 && this.tcpSeq == other.tcpSeq
160 && this.tcpWnd == other.tcpWnd
161 && this.tcpWndScale == other.tcpWndScale;
162 }
163
164 @Override
165 public int hashCode() {
166 return Objects.hash(srcAddress, dstAddress, srcPort, dstPort, tcpAck, tcpSeq, tcpWnd,
167 tcpWndScale);
168 }
169
170 /* Parcelable Implementation. */
junyulai2ed5d4c2019-01-30 19:11:45 +0800171 /* Note that this object implements parcelable (and needs to keep doing this as it inherits
172 * from a class that does), but should usually be parceled as a stable parcelable using
173 * the toStableParcelable() and fromStableParcelable() methods.
174 */
markchien150e1912018-12-27 22:49:51 +0800175 public int describeContents() {
176 return 0;
177 }
178
179 /** Write to parcel. */
180 public void writeToParcel(Parcel out, int flags) {
181 super.writeToParcel(out, flags);
182 out.writeInt(tcpSeq);
183 out.writeInt(tcpAck);
184 out.writeInt(tcpWnd);
185 out.writeInt(tcpWndScale);
186 }
187
188 private TcpKeepalivePacketData(Parcel in) {
189 super(in);
190 tcpSeq = in.readInt();
191 tcpAck = in.readInt();
192 tcpWnd = in.readInt();
193 tcpWndScale = in.readInt();
194 }
195
196 /** Parcelable Creator. */
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700197 public static final @android.annotation.NonNull Parcelable.Creator<TcpKeepalivePacketData> CREATOR =
markchien150e1912018-12-27 22:49:51 +0800198 new Parcelable.Creator<TcpKeepalivePacketData>() {
199 public TcpKeepalivePacketData createFromParcel(Parcel in) {
200 return new TcpKeepalivePacketData(in);
201 }
202
203 public TcpKeepalivePacketData[] newArray(int size) {
204 return new TcpKeepalivePacketData[size];
205 }
206 };
junyulai2ed5d4c2019-01-30 19:11:45 +0800207
208 /**
209 * Convert this TcpKeepalivePacketData to a TcpKeepalivePacketDataParcelable.
210 */
211 @NonNull
212 public TcpKeepalivePacketDataParcelable toStableParcelable() {
213 final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable();
214 parcel.srcAddress = srcAddress.getAddress();
215 parcel.srcPort = srcPort;
216 parcel.dstAddress = dstAddress.getAddress();
217 parcel.dstPort = dstPort;
218 parcel.seq = tcpSeq;
219 parcel.ack = tcpAck;
220 return parcel;
221 }
222
223 @Override
224 public String toString() {
225 return "saddr: " + srcAddress
226 + " daddr: " + dstAddress
227 + " sport: " + srcPort
228 + " dport: " + dstPort
229 + " seq: " + tcpSeq
230 + " ack: " + tcpAck
231 + " wnd: " + tcpWnd
232 + " wndScale: " + tcpWndScale;
233 }
markchien150e1912018-12-27 22:49:51 +0800234}