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