• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.net.util;
18 
19 import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.net.InvalidPacketException;
24 import android.net.KeepalivePacketData;
25 import android.net.NattKeepalivePacketData;
26 import android.net.NattKeepalivePacketDataParcelable;
27 import android.net.TcpKeepalivePacketData;
28 import android.net.TcpKeepalivePacketDataParcelable;
29 import android.os.Build;
30 import android.system.OsConstants;
31 import android.util.Log;
32 
33 import com.android.net.module.util.IpUtils;
34 
35 import java.net.InetAddress;
36 import java.net.UnknownHostException;
37 import java.nio.ByteBuffer;
38 import java.nio.ByteOrder;
39 
40 /**
41  * Utility class to convert to/from keepalive data parcelables.
42  *
43  * TODO: move to networkstack-client library when it is moved to frameworks/libs/net.
44  * This class cannot go into other shared libraries as it depends on NetworkStack AIDLs.
45  * @hide
46  */
47 public final class KeepalivePacketDataUtil {
48     private static final int IPV4_HEADER_LENGTH = 20;
49     private static final int IPV6_HEADER_LENGTH = 40;
50     private static final int TCP_HEADER_LENGTH = 20;
51 
52     private static final String TAG = KeepalivePacketDataUtil.class.getSimpleName();
53 
54     /**
55      * Convert a NattKeepalivePacketData to a NattKeepalivePacketDataParcelable.
56      */
57     @NonNull
toStableParcelable( @onNull NattKeepalivePacketData pkt)58     public static NattKeepalivePacketDataParcelable toStableParcelable(
59             @NonNull NattKeepalivePacketData pkt) {
60         final NattKeepalivePacketDataParcelable parcel = new NattKeepalivePacketDataParcelable();
61         final InetAddress srcAddress = pkt.getSrcAddress();
62         final InetAddress dstAddress = pkt.getDstAddress();
63         parcel.srcAddress = srcAddress.getAddress();
64         parcel.srcPort = pkt.getSrcPort();
65         parcel.dstAddress = dstAddress.getAddress();
66         parcel.dstPort = pkt.getDstPort();
67         return parcel;
68     }
69 
70     /**
71      * Convert a TcpKeepalivePacketData to a TcpKeepalivePacketDataParcelable.
72      */
73     @NonNull
toStableParcelable( @onNull TcpKeepalivePacketData pkt)74     public static TcpKeepalivePacketDataParcelable toStableParcelable(
75             @NonNull TcpKeepalivePacketData pkt) {
76         final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable();
77         final InetAddress srcAddress = pkt.getSrcAddress();
78         final InetAddress dstAddress = pkt.getDstAddress();
79         parcel.srcAddress = srcAddress.getAddress();
80         parcel.srcPort = pkt.getSrcPort();
81         parcel.dstAddress = dstAddress.getAddress();
82         parcel.dstPort = pkt.getDstPort();
83         parcel.seq = pkt.getTcpSeq();
84         parcel.ack = pkt.getTcpAck();
85         parcel.rcvWnd = pkt.getTcpWindow();
86         parcel.rcvWndScale = pkt.getTcpWindowScale();
87         parcel.tos = pkt.getIpTos();
88         parcel.ttl = pkt.getIpTtl();
89         return parcel;
90     }
91 
92     /**
93      * Factory method to create tcp keepalive packet structure.
94      * @hide
95      */
fromStableParcelable( TcpKeepalivePacketDataParcelable tcpDetails)96     public static TcpKeepalivePacketData fromStableParcelable(
97             TcpKeepalivePacketDataParcelable tcpDetails) throws InvalidPacketException {
98         final byte[] packet;
99         try {
100             if ((tcpDetails.srcAddress != null) && (tcpDetails.dstAddress != null)
101                     && (tcpDetails.srcAddress.length == 4 /* V4 IP length */)
102                     && (tcpDetails.dstAddress.length == 4 /* V4 IP length */)) {
103                 packet = buildV4Packet(tcpDetails);
104             } else {
105                 // TODO: support ipv6
106                 throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
107             }
108             return new TcpKeepalivePacketData(
109                     InetAddress.getByAddress(tcpDetails.srcAddress),
110                     tcpDetails.srcPort,
111                     InetAddress.getByAddress(tcpDetails.dstAddress),
112                     tcpDetails.dstPort,
113                     packet,
114                     tcpDetails.seq, tcpDetails.ack, tcpDetails.rcvWnd, tcpDetails.rcvWndScale,
115                     tcpDetails.tos, tcpDetails.ttl);
116         } catch (UnknownHostException e) {
117             throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
118         }
119 
120     }
121 
122     /**
123      * Build ipv4 tcp keepalive packet, not including the link-layer header.
124      */
125     // TODO : if this code is ever moved to the network stack, factorize constants with the ones
126     // over there.
buildV4Packet(TcpKeepalivePacketDataParcelable tcpDetails)127     private static byte[] buildV4Packet(TcpKeepalivePacketDataParcelable tcpDetails) {
128         final int length = IPV4_HEADER_LENGTH + TCP_HEADER_LENGTH;
129         ByteBuffer buf = ByteBuffer.allocate(length);
130         buf.order(ByteOrder.BIG_ENDIAN);
131         buf.put((byte) 0x45);                       // IP version and IHL
132         buf.put((byte) tcpDetails.tos);             // TOS
133         buf.putShort((short) length);
134         buf.putInt(0x00004000);                     // ID, flags=DF, offset
135         buf.put((byte) tcpDetails.ttl);             // TTL
136         buf.put((byte) OsConstants.IPPROTO_TCP);
137         final int ipChecksumOffset = buf.position();
138         buf.putShort((short) 0);                    // IP checksum
139         buf.put(tcpDetails.srcAddress);
140         buf.put(tcpDetails.dstAddress);
141         buf.putShort((short) tcpDetails.srcPort);
142         buf.putShort((short) tcpDetails.dstPort);
143         buf.putInt(tcpDetails.seq);                 // Sequence Number
144         buf.putInt(tcpDetails.ack);                 // ACK
145         buf.putShort((short) 0x5010);               // TCP length=5, flags=ACK
146         buf.putShort((short) (tcpDetails.rcvWnd >> tcpDetails.rcvWndScale));   // Window size
147         final int tcpChecksumOffset = buf.position();
148         buf.putShort((short) 0);                    // TCP checksum
149         // URG is not set therefore the urgent pointer is zero.
150         buf.putShort((short) 0);                    // Urgent pointer
151 
152         buf.putShort(ipChecksumOffset, com.android.net.module.util.IpUtils.ipChecksum(buf, 0));
153         buf.putShort(tcpChecksumOffset, IpUtils.tcpChecksum(
154                 buf, 0, IPV4_HEADER_LENGTH, TCP_HEADER_LENGTH));
155 
156         return buf.array();
157     }
158 
159     // TODO: add buildV6Packet.
160 
161     /**
162      * Get a {@link TcpKeepalivePacketDataParcelable} from {@link KeepalivePacketData}, if the
163      * generic class actually contains TCP keepalive data.
164      *
165      * @deprecated This method is used on R platforms where android.net.TcpKeepalivePacketData was
166      * not yet system API. Newer platforms should use android.net.TcpKeepalivePacketData directly.
167      *
168      * @param data A {@link KeepalivePacketData} that may contain TCP keepalive data.
169      * @return A parcelable containing TCP keepalive data, or null if the input data does not
170      *         contain TCP keepalive data.
171      */
172     @Deprecated
173     @SuppressWarnings("AndroidFrameworkCompatChange") // API version check used to Log.wtf
174     @Nullable
parseTcpKeepalivePacketData( @ullable KeepalivePacketData data)175     public static TcpKeepalivePacketDataParcelable parseTcpKeepalivePacketData(
176             @Nullable KeepalivePacketData data) {
177         if (data == null) return null;
178 
179         if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
180             Log.wtf(TAG, "parseTcpKeepalivePacketData should not be used after R, use "
181                     + "TcpKeepalivePacketData instead.");
182         }
183 
184         // Reconstruct TcpKeepalivePacketData from the packet contained in KeepalivePacketData
185         final ByteBuffer buffer = ByteBuffer.wrap(data.getPacket());
186         buffer.order(ByteOrder.BIG_ENDIAN);
187 
188         // Most of the fields are accessible from the KeepalivePacketData superclass: instead of
189         // using Struct to parse everything, just extract the extra fields necessary for
190         // TcpKeepalivePacketData.
191         final int tcpSeq;
192         final int tcpAck;
193         final int wndSize;
194         final int ipTos;
195         final int ttl;
196         try {
197             // This only support IPv4, because TcpKeepalivePacketData only supports IPv4 for R and
198             // below, and this method should not be used on newer platforms.
199             tcpSeq = buffer.getInt(IPV4_HEADER_LENGTH + 4);
200             tcpAck = buffer.getInt(IPV4_HEADER_LENGTH + 8);
201             wndSize = buffer.getShort(IPV4_HEADER_LENGTH + 14);
202             ipTos = buffer.get(1);
203             ttl = buffer.get(8);
204         } catch (IndexOutOfBoundsException e) {
205             return null;
206         }
207 
208         final TcpKeepalivePacketDataParcelable p = new TcpKeepalivePacketDataParcelable();
209         p.srcAddress = data.getSrcAddress().getAddress();
210         p.srcPort = data.getSrcPort();
211         p.dstAddress = data.getDstAddress().getAddress();
212         p.dstPort = data.getDstPort();
213         p.seq = tcpSeq;
214         p.ack = tcpAck;
215         // TcpKeepalivePacketData could actually use non-zero wndScale, but this does not affect
216         // actual functionality as generated packets will be the same (no wndScale option added)
217         p.rcvWnd = wndSize;
218         p.rcvWndScale = 0;
219         p.tos = ipTos;
220         p.ttl = ttl;
221         return p;
222     }
223 }
224