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