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