1 /*
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  */
16 package android.net;
17 
18 import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
19 
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.net.util.IpUtils;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 import android.system.OsConstants;
26 
27 import java.net.InetAddress;
28 import java.net.UnknownHostException;
29 import java.nio.ByteBuffer;
30 import java.nio.ByteOrder;
31 import java.util.Objects;
32 
33 /**
34  * Represents the actual tcp keep alive packets which will be used for hardware offload.
35  * @hide
36  */
37 public class TcpKeepalivePacketData extends KeepalivePacketData implements Parcelable {
38     private static final String TAG = "TcpKeepalivePacketData";
39 
40     /** TCP sequence number. */
41     public final int tcpSeq;
42 
43     /** TCP ACK number. */
44     public final int tcpAck;
45 
46     /** TCP RCV window. */
47     public final int tcpWnd;
48 
49     /** TCP RCV window scale. */
50     public final int tcpWndScale;
51 
52     /** IP TOS. */
53     public final int ipTos;
54 
55     /** IP TTL. */
56     public final int ipTtl;
57 
58     private static final int IPV4_HEADER_LENGTH = 20;
59     private static final int IPV6_HEADER_LENGTH = 40;
60     private static final int TCP_HEADER_LENGTH = 20;
61 
62     // This should only be constructed via static factory methods, such as
63     // tcpKeepalivePacket.
TcpKeepalivePacketData(final TcpKeepalivePacketDataParcelable tcpDetails, final byte[] data)64     private TcpKeepalivePacketData(final TcpKeepalivePacketDataParcelable tcpDetails,
65             final byte[] data) throws InvalidPacketException, UnknownHostException {
66         super(InetAddress.getByAddress(tcpDetails.srcAddress), tcpDetails.srcPort,
67                 InetAddress.getByAddress(tcpDetails.dstAddress), tcpDetails.dstPort, data);
68         tcpSeq = tcpDetails.seq;
69         tcpAck = tcpDetails.ack;
70         // In the packet, the window is shifted right by the window scale.
71         tcpWnd = tcpDetails.rcvWnd;
72         tcpWndScale = tcpDetails.rcvWndScale;
73         ipTos = tcpDetails.tos;
74         ipTtl = tcpDetails.ttl;
75     }
76 
TcpKeepalivePacketData(final InetAddress srcAddress, int srcPort, final InetAddress dstAddress, int dstPort, final byte[] data, int tcpSeq, int tcpAck, int tcpWnd, int tcpWndScale, int ipTos, int ipTtl)77     private TcpKeepalivePacketData(final InetAddress srcAddress, int srcPort,
78             final InetAddress dstAddress, int dstPort, final byte[] data, int tcpSeq,
79             int tcpAck, int tcpWnd, int tcpWndScale, int ipTos, int ipTtl)
80             throws InvalidPacketException {
81         super(srcAddress, srcPort, dstAddress, dstPort, data);
82         this.tcpSeq = tcpSeq;
83         this.tcpAck = tcpAck;
84         this.tcpWnd = tcpWnd;
85         this.tcpWndScale = tcpWndScale;
86         this.ipTos = ipTos;
87         this.ipTtl = ipTtl;
88     }
89 
90     /**
91      * Factory method to create tcp keepalive packet structure.
92      */
tcpKeepalivePacket( TcpKeepalivePacketDataParcelable tcpDetails)93     public static TcpKeepalivePacketData tcpKeepalivePacket(
94             TcpKeepalivePacketDataParcelable tcpDetails) throws InvalidPacketException {
95         final byte[] packet;
96         try {
97             if ((tcpDetails.srcAddress != null) && (tcpDetails.dstAddress != null)
98                     && (tcpDetails.srcAddress.length == 4 /* V4 IP length */)
99                     && (tcpDetails.dstAddress.length == 4 /* V4 IP length */)) {
100                 packet = buildV4Packet(tcpDetails);
101             } else {
102                 // TODO: support ipv6
103                 throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
104             }
105             return new TcpKeepalivePacketData(tcpDetails, packet);
106         } catch (UnknownHostException e) {
107             throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
108         }
109 
110     }
111 
112     /**
113      * Build ipv4 tcp keepalive packet, not including the link-layer header.
114      */
115     // TODO : if this code is ever moved to the network stack, factorize constants with the ones
116     // over there.
buildV4Packet(TcpKeepalivePacketDataParcelable tcpDetails)117     private static byte[] buildV4Packet(TcpKeepalivePacketDataParcelable tcpDetails) {
118         final int length = IPV4_HEADER_LENGTH + TCP_HEADER_LENGTH;
119         ByteBuffer buf = ByteBuffer.allocate(length);
120         buf.order(ByteOrder.BIG_ENDIAN);
121         buf.put((byte) 0x45);                       // IP version and IHL
122         buf.put((byte) tcpDetails.tos);             // TOS
123         buf.putShort((short) length);
124         buf.putInt(0x00004000);                     // ID, flags=DF, offset
125         buf.put((byte) tcpDetails.ttl);             // TTL
126         buf.put((byte) OsConstants.IPPROTO_TCP);
127         final int ipChecksumOffset = buf.position();
128         buf.putShort((short) 0);                    // IP checksum
129         buf.put(tcpDetails.srcAddress);
130         buf.put(tcpDetails.dstAddress);
131         buf.putShort((short) tcpDetails.srcPort);
132         buf.putShort((short) tcpDetails.dstPort);
133         buf.putInt(tcpDetails.seq);                 // Sequence Number
134         buf.putInt(tcpDetails.ack);                 // ACK
135         buf.putShort((short) 0x5010);               // TCP length=5, flags=ACK
136         buf.putShort((short) (tcpDetails.rcvWnd >> tcpDetails.rcvWndScale));   // Window size
137         final int tcpChecksumOffset = buf.position();
138         buf.putShort((short) 0);                    // TCP checksum
139         // URG is not set therefore the urgent pointer is zero.
140         buf.putShort((short) 0);                    // Urgent pointer
141 
142         buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0));
143         buf.putShort(tcpChecksumOffset, IpUtils.tcpChecksum(
144                 buf, 0, IPV4_HEADER_LENGTH, TCP_HEADER_LENGTH));
145 
146         return buf.array();
147     }
148 
149     // TODO: add buildV6Packet.
150 
151     @Override
equals(@ullable final Object o)152     public boolean equals(@Nullable final Object o) {
153         if (!(o instanceof TcpKeepalivePacketData)) return false;
154         final TcpKeepalivePacketData other = (TcpKeepalivePacketData) o;
155         final InetAddress srcAddress = getSrcAddress();
156         final InetAddress dstAddress = getDstAddress();
157         return srcAddress.equals(other.getSrcAddress())
158                 && dstAddress.equals(other.getDstAddress())
159                 && getSrcPort() == other.getSrcPort()
160                 && getDstPort() == other.getDstPort()
161                 && this.tcpAck == other.tcpAck
162                 && this.tcpSeq == other.tcpSeq
163                 && this.tcpWnd == other.tcpWnd
164                 && this.tcpWndScale == other.tcpWndScale
165                 && this.ipTos == other.ipTos
166                 && this.ipTtl == other.ipTtl;
167     }
168 
169     @Override
hashCode()170     public int hashCode() {
171         return Objects.hash(getSrcAddress(), getDstAddress(), getSrcPort(), getDstPort(),
172                 tcpAck, tcpSeq, tcpWnd, tcpWndScale, ipTos, ipTtl);
173     }
174 
175     /**
176      * Parcelable Implementation.
177      * Note that this object implements parcelable (and needs to keep doing this as it inherits
178      * from a class that does), but should usually be parceled as a stable parcelable using
179      * the toStableParcelable() and fromStableParcelable() methods.
180      */
describeContents()181     public int describeContents() {
182         return 0;
183     }
184 
185     /** Write to parcel. */
writeToParcel(Parcel out, int flags)186     public void writeToParcel(Parcel out, int flags) {
187         out.writeString(getSrcAddress().getHostAddress());
188         out.writeString(getDstAddress().getHostAddress());
189         out.writeInt(getSrcPort());
190         out.writeInt(getDstPort());
191         out.writeByteArray(getPacket());
192         out.writeInt(tcpSeq);
193         out.writeInt(tcpAck);
194         out.writeInt(tcpWnd);
195         out.writeInt(tcpWndScale);
196         out.writeInt(ipTos);
197         out.writeInt(ipTtl);
198     }
199 
readFromParcel(Parcel in)200     private static TcpKeepalivePacketData readFromParcel(Parcel in) throws InvalidPacketException {
201         InetAddress srcAddress = InetAddresses.parseNumericAddress(in.readString());
202         InetAddress dstAddress = InetAddresses.parseNumericAddress(in.readString());
203         int srcPort = in.readInt();
204         int dstPort = in.readInt();
205         byte[] packet = in.createByteArray();
206         int tcpSeq = in.readInt();
207         int tcpAck = in.readInt();
208         int tcpWnd = in.readInt();
209         int tcpWndScale = in.readInt();
210         int ipTos = in.readInt();
211         int ipTtl = in.readInt();
212         return new TcpKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, packet, tcpSeq,
213                 tcpAck, tcpWnd, tcpWndScale, ipTos, ipTtl);
214     }
215 
216     /** Parcelable Creator. */
217     public static final @NonNull Parcelable.Creator<TcpKeepalivePacketData> CREATOR =
218             new Parcelable.Creator<TcpKeepalivePacketData>() {
219                 public TcpKeepalivePacketData createFromParcel(Parcel in) {
220                     try {
221                         return readFromParcel(in);
222                     } catch (InvalidPacketException e) {
223                         throw new IllegalArgumentException(
224                                 "Invalid NAT-T keepalive data: " + e.getError());
225                     }
226                 }
227 
228                 public TcpKeepalivePacketData[] newArray(int size) {
229                     return new TcpKeepalivePacketData[size];
230                 }
231             };
232 
233     /**
234      * Convert this TcpKeepalivePacketData to a TcpKeepalivePacketDataParcelable.
235      */
236     @NonNull
toStableParcelable()237     public TcpKeepalivePacketDataParcelable toStableParcelable() {
238         final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable();
239         final InetAddress srcAddress = getSrcAddress();
240         final InetAddress dstAddress = getDstAddress();
241         parcel.srcAddress = srcAddress.getAddress();
242         parcel.srcPort = getSrcPort();
243         parcel.dstAddress = dstAddress.getAddress();
244         parcel.dstPort = getDstPort();
245         parcel.seq = tcpSeq;
246         parcel.ack = tcpAck;
247         parcel.rcvWnd = tcpWnd;
248         parcel.rcvWndScale = tcpWndScale;
249         parcel.tos = ipTos;
250         parcel.ttl = ipTtl;
251         return parcel;
252     }
253 
254     @Override
toString()255     public String toString() {
256         return "saddr: " + getSrcAddress()
257                 + " daddr: " + getDstAddress()
258                 + " sport: " + getSrcPort()
259                 + " dport: " + getDstPort()
260                 + " seq: " + tcpSeq
261                 + " ack: " + tcpAck
262                 + " wnd: " + tcpWnd
263                 + " wndScale: " + tcpWndScale
264                 + " tos: " + ipTos
265                 + " ttl: " + ipTtl;
266     }
267 }
268