diff options
author | Remi NGUYEN VAN <reminv@google.com> | 2020-11-10 15:29:01 +0900 |
---|---|---|
committer | Remi NGUYEN VAN <reminv@google.com> | 2020-12-14 18:43:44 +0900 |
commit | 103ec4aa8d2c9c573840346e18ea9a20a5adc356 (patch) | |
tree | b4504c07c624c8c7dc40d9a85909c97f649c17d3 | |
parent | 294dd8f1da16d03367e4af6f9fbf120e4d8674d5 (diff) |
Add TcpKeepalivePacketData to SystemApi
This is consistent with NattKeepalivePacketData, which is also a
subclass of KeepalivePacketData.
TcpKeepalivePacketData is already used by the wifi module, but
statically linked.
Bug: 172789687
Test: m
Change-Id: I6aee1ae205987521bea4a3838bbece279ffa0e37
-rw-r--r-- | core/api/system-current.txt | 13 | ||||
-rw-r--r-- | core/java/android/net/TcpKeepalivePacketData.java | 163 | ||||
-rw-r--r-- | services/core/java/com/android/server/connectivity/TcpKeepaliveController.java | 5 | ||||
-rw-r--r-- | services/net/Android.bp | 1 | ||||
-rw-r--r-- | services/net/java/android/net/TcpKeepalivePacketData.java | 268 | ||||
-rw-r--r-- | services/net/java/android/net/ip/IpClientManager.java | 14 | ||||
-rw-r--r-- | services/net/java/android/net/util/KeepalivePacketDataUtil.java | 120 | ||||
-rw-r--r-- | tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt | 106 | ||||
-rw-r--r-- | tests/net/java/android/net/KeepalivePacketDataUtilTest.java (renamed from tests/net/java/android/net/TcpKeepalivePacketDataTest.java) | 20 |
9 files changed, 425 insertions, 285 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index a48ad95ea40e..3980774468ab 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -6400,6 +6400,19 @@ package android.net { method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@Nullable android.net.LinkAddress); } + public final class TcpKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable { + ctor public TcpKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[], int, int, int, int, int, int) throws android.net.InvalidPacketException; + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.TcpKeepalivePacketData> CREATOR; + field public final int ipTos; + field public final int ipTtl; + field public final int tcpAck; + field public final int tcpSeq; + field public final int tcpWindow; + field public final int tcpWindowScale; + } + public class TrafficStats { method public static void setThreadStatsTagApp(); method public static void setThreadStatsTagBackup(); diff --git a/core/java/android/net/TcpKeepalivePacketData.java b/core/java/android/net/TcpKeepalivePacketData.java new file mode 100644 index 000000000000..ddb3a6a72fb4 --- /dev/null +++ b/core/java/android/net/TcpKeepalivePacketData.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.net; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.net.InetAddress; +import java.util.Objects; + +/** + * Represents the actual tcp keep alive packets which will be used for hardware offload. + * @hide + */ +@SystemApi +public final class TcpKeepalivePacketData extends KeepalivePacketData implements Parcelable { + private static final String TAG = "TcpKeepalivePacketData"; + + /** TCP sequence number. */ + public final int tcpSeq; + + /** TCP ACK number. */ + public final int tcpAck; + + /** TCP RCV window. */ + public final int tcpWindow; + + /** TCP RCV window scale. */ + public final int tcpWindowScale; + + /** IP TOS. */ + public final int ipTos; + + /** IP TTL. */ + public final int ipTtl; + + public TcpKeepalivePacketData(@NonNull final InetAddress srcAddress, int srcPort, + @NonNull final InetAddress dstAddress, int dstPort, @NonNull final byte[] data, + int tcpSeq, int tcpAck, int tcpWindow, int tcpWindowScale, int ipTos, int ipTtl) + throws InvalidPacketException { + super(srcAddress, srcPort, dstAddress, dstPort, data); + this.tcpSeq = tcpSeq; + this.tcpAck = tcpAck; + this.tcpWindow = tcpWindow; + this.tcpWindowScale = tcpWindowScale; + this.ipTos = ipTos; + this.ipTtl = ipTtl; + } + + @Override + public boolean equals(@Nullable final Object o) { + if (!(o instanceof TcpKeepalivePacketData)) return false; + final TcpKeepalivePacketData other = (TcpKeepalivePacketData) o; + final InetAddress srcAddress = getSrcAddress(); + final InetAddress dstAddress = getDstAddress(); + return srcAddress.equals(other.getSrcAddress()) + && dstAddress.equals(other.getDstAddress()) + && getSrcPort() == other.getSrcPort() + && getDstPort() == other.getDstPort() + && this.tcpAck == other.tcpAck + && this.tcpSeq == other.tcpSeq + && this.tcpWindow == other.tcpWindow + && this.tcpWindowScale == other.tcpWindowScale + && this.ipTos == other.ipTos + && this.ipTtl == other.ipTtl; + } + + @Override + public int hashCode() { + return Objects.hash(getSrcAddress(), getDstAddress(), getSrcPort(), getDstPort(), + tcpAck, tcpSeq, tcpWindow, tcpWindowScale, ipTos, ipTtl); + } + + /** + * Parcelable Implementation. + * Note that this object implements parcelable (and needs to keep doing this as it inherits + * from a class that does), but should usually be parceled as a stable parcelable using + * the toStableParcelable() and fromStableParcelable() methods. + */ + @Override + public int describeContents() { + return 0; + } + + /** Write to parcel. */ + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeString(getSrcAddress().getHostAddress()); + out.writeString(getDstAddress().getHostAddress()); + out.writeInt(getSrcPort()); + out.writeInt(getDstPort()); + out.writeByteArray(getPacket()); + out.writeInt(tcpSeq); + out.writeInt(tcpAck); + out.writeInt(tcpWindow); + out.writeInt(tcpWindowScale); + out.writeInt(ipTos); + out.writeInt(ipTtl); + } + + private static TcpKeepalivePacketData readFromParcel(Parcel in) throws InvalidPacketException { + InetAddress srcAddress = InetAddresses.parseNumericAddress(in.readString()); + InetAddress dstAddress = InetAddresses.parseNumericAddress(in.readString()); + int srcPort = in.readInt(); + int dstPort = in.readInt(); + byte[] packet = in.createByteArray(); + int tcpSeq = in.readInt(); + int tcpAck = in.readInt(); + int tcpWnd = in.readInt(); + int tcpWndScale = in.readInt(); + int ipTos = in.readInt(); + int ipTtl = in.readInt(); + return new TcpKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, packet, tcpSeq, + tcpAck, tcpWnd, tcpWndScale, ipTos, ipTtl); + } + + /** Parcelable Creator. */ + public static final @NonNull Parcelable.Creator<TcpKeepalivePacketData> CREATOR = + new Parcelable.Creator<TcpKeepalivePacketData>() { + public TcpKeepalivePacketData createFromParcel(Parcel in) { + try { + return readFromParcel(in); + } catch (InvalidPacketException e) { + throw new IllegalArgumentException( + "Invalid TCP keepalive data: " + e.getError()); + } + } + + public TcpKeepalivePacketData[] newArray(int size) { + return new TcpKeepalivePacketData[size]; + } + }; + + @Override + public String toString() { + return "saddr: " + getSrcAddress() + + " daddr: " + getDstAddress() + + " sport: " + getSrcPort() + + " dport: " + getDstPort() + + " seq: " + tcpSeq + + " ack: " + tcpAck + + " window: " + tcpWindow + + " windowScale: " + tcpWindowScale + + " tos: " + ipTos + + " ttl: " + ipTtl; + } +} diff --git a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java index 1129899ee3ff..b5f20d70db7f 100644 --- a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java +++ b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java @@ -36,6 +36,7 @@ import android.net.SocketKeepalive.InvalidSocketException; import android.net.TcpKeepalivePacketData; import android.net.TcpKeepalivePacketDataParcelable; import android.net.TcpRepairWindow; +import android.net.util.KeepalivePacketDataUtil; import android.os.Handler; import android.os.MessageQueue; import android.os.Messenger; @@ -112,7 +113,7 @@ public class TcpKeepaliveController { throws InvalidPacketException, InvalidSocketException { try { final TcpKeepalivePacketDataParcelable tcpDetails = switchToRepairMode(fd); - return TcpKeepalivePacketData.tcpKeepalivePacket(tcpDetails); + return KeepalivePacketDataUtil.fromStableParcelable(tcpDetails); } catch (InvalidPacketException | InvalidSocketException e) { switchOutOfRepairMode(fd); throw e; @@ -122,7 +123,7 @@ public class TcpKeepaliveController { * Switch the tcp socket to repair mode and query detail tcp information. * * @param fd the fd of socket on which to use keepalive offload. - * @return a {@link TcpKeepalivePacketData#TcpKeepalivePacketDataParcelable} object for current + * @return a {@link TcpKeepalivePacketDataParcelable} object for current * tcp/ip information. */ private static TcpKeepalivePacketDataParcelable switchToRepairMode(FileDescriptor fd) diff --git a/services/net/Android.bp b/services/net/Android.bp index 29bf37460568..ae9bd41b9db0 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -37,7 +37,6 @@ java_library { "java/android/net/util/NetworkConstants.java", "java/android/net/IpMemoryStore.java", "java/android/net/NetworkMonitorManager.java", - "java/android/net/TcpKeepalivePacketData.java", ], sdk_version: "module_current", min_sdk_version: "30", diff --git a/services/net/java/android/net/TcpKeepalivePacketData.java b/services/net/java/android/net/TcpKeepalivePacketData.java deleted file mode 100644 index 4875c7cc4263..000000000000 --- a/services/net/java/android/net/TcpKeepalivePacketData.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.net; - -import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Parcel; -import android.os.Parcelable; -import android.system.OsConstants; - -import com.android.net.module.util.IpUtils; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.Objects; - -/** - * Represents the actual tcp keep alive packets which will be used for hardware offload. - * @hide - */ -public class TcpKeepalivePacketData extends KeepalivePacketData implements Parcelable { - private static final String TAG = "TcpKeepalivePacketData"; - - /** TCP sequence number. */ - public final int tcpSeq; - - /** TCP ACK number. */ - public final int tcpAck; - - /** TCP RCV window. */ - public final int tcpWnd; - - /** TCP RCV window scale. */ - public final int tcpWndScale; - - /** IP TOS. */ - public final int ipTos; - - /** IP TTL. */ - public final int ipTtl; - - private static final int IPV4_HEADER_LENGTH = 20; - private static final int IPV6_HEADER_LENGTH = 40; - private static final int TCP_HEADER_LENGTH = 20; - - // This should only be constructed via static factory methods, such as - // tcpKeepalivePacket. - private TcpKeepalivePacketData(final TcpKeepalivePacketDataParcelable tcpDetails, - final byte[] data) throws InvalidPacketException, UnknownHostException { - super(InetAddress.getByAddress(tcpDetails.srcAddress), tcpDetails.srcPort, - InetAddress.getByAddress(tcpDetails.dstAddress), tcpDetails.dstPort, data); - tcpSeq = tcpDetails.seq; - tcpAck = tcpDetails.ack; - // In the packet, the window is shifted right by the window scale. - tcpWnd = tcpDetails.rcvWnd; - tcpWndScale = tcpDetails.rcvWndScale; - ipTos = tcpDetails.tos; - ipTtl = tcpDetails.ttl; - } - - private 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) - throws InvalidPacketException { - super(srcAddress, srcPort, dstAddress, dstPort, data); - this.tcpSeq = tcpSeq; - this.tcpAck = tcpAck; - this.tcpWnd = tcpWnd; - this.tcpWndScale = tcpWndScale; - this.ipTos = ipTos; - this.ipTtl = ipTtl; - } - - /** - * Factory method to create tcp keepalive packet structure. - */ - public static TcpKeepalivePacketData tcpKeepalivePacket( - TcpKeepalivePacketDataParcelable tcpDetails) throws InvalidPacketException { - final byte[] packet; - try { - if ((tcpDetails.srcAddress != null) && (tcpDetails.dstAddress != null) - && (tcpDetails.srcAddress.length == 4 /* V4 IP length */) - && (tcpDetails.dstAddress.length == 4 /* V4 IP length */)) { - packet = buildV4Packet(tcpDetails); - } else { - // TODO: support ipv6 - throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS); - } - return new TcpKeepalivePacketData(tcpDetails, packet); - } catch (UnknownHostException e) { - throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS); - } - - } - - /** - * Build ipv4 tcp keepalive packet, not including the link-layer header. - */ - // TODO : if this code is ever moved to the network stack, factorize constants with the ones - // over there. - private static byte[] buildV4Packet(TcpKeepalivePacketDataParcelable tcpDetails) { - final int length = IPV4_HEADER_LENGTH + TCP_HEADER_LENGTH; - ByteBuffer buf = ByteBuffer.allocate(length); - buf.order(ByteOrder.BIG_ENDIAN); - buf.put((byte) 0x45); // IP version and IHL - buf.put((byte) tcpDetails.tos); // TOS - buf.putShort((short) length); - buf.putInt(0x00004000); // ID, flags=DF, offset - buf.put((byte) tcpDetails.ttl); // TTL - buf.put((byte) OsConstants.IPPROTO_TCP); - final int ipChecksumOffset = buf.position(); - buf.putShort((short) 0); // IP checksum - buf.put(tcpDetails.srcAddress); - buf.put(tcpDetails.dstAddress); - buf.putShort((short) tcpDetails.srcPort); - buf.putShort((short) tcpDetails.dstPort); - buf.putInt(tcpDetails.seq); // Sequence Number - buf.putInt(tcpDetails.ack); // ACK - buf.putShort((short) 0x5010); // TCP length=5, flags=ACK - buf.putShort((short) (tcpDetails.rcvWnd >> tcpDetails.rcvWndScale)); // Window size - final int tcpChecksumOffset = buf.position(); - buf.putShort((short) 0); // TCP checksum - // URG is not set therefore the urgent pointer is zero. - buf.putShort((short) 0); // Urgent pointer - - buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0)); - buf.putShort(tcpChecksumOffset, IpUtils.tcpChecksum( - buf, 0, IPV4_HEADER_LENGTH, TCP_HEADER_LENGTH)); - - return buf.array(); - } - - // TODO: add buildV6Packet. - - @Override - public boolean equals(@Nullable final Object o) { - if (!(o instanceof TcpKeepalivePacketData)) return false; - final TcpKeepalivePacketData other = (TcpKeepalivePacketData) o; - final InetAddress srcAddress = getSrcAddress(); - final InetAddress dstAddress = getDstAddress(); - return srcAddress.equals(other.getSrcAddress()) - && dstAddress.equals(other.getDstAddress()) - && getSrcPort() == other.getSrcPort() - && getDstPort() == other.getDstPort() - && this.tcpAck == other.tcpAck - && this.tcpSeq == other.tcpSeq - && this.tcpWnd == other.tcpWnd - && this.tcpWndScale == other.tcpWndScale - && this.ipTos == other.ipTos - && this.ipTtl == other.ipTtl; - } - - @Override - public int hashCode() { - return Objects.hash(getSrcAddress(), getDstAddress(), getSrcPort(), getDstPort(), - tcpAck, tcpSeq, tcpWnd, tcpWndScale, ipTos, ipTtl); - } - - /** - * Parcelable Implementation. - * Note that this object implements parcelable (and needs to keep doing this as it inherits - * from a class that does), but should usually be parceled as a stable parcelable using - * the toStableParcelable() and fromStableParcelable() methods. - */ - public int describeContents() { - return 0; - } - - /** Write to parcel. */ - public void writeToParcel(Parcel out, int flags) { - out.writeString(getSrcAddress().getHostAddress()); - out.writeString(getDstAddress().getHostAddress()); - out.writeInt(getSrcPort()); - out.writeInt(getDstPort()); - out.writeByteArray(getPacket()); - out.writeInt(tcpSeq); - out.writeInt(tcpAck); - out.writeInt(tcpWnd); - out.writeInt(tcpWndScale); - out.writeInt(ipTos); - out.writeInt(ipTtl); - } - - private static TcpKeepalivePacketData readFromParcel(Parcel in) throws InvalidPacketException { - InetAddress srcAddress = InetAddresses.parseNumericAddress(in.readString()); - InetAddress dstAddress = InetAddresses.parseNumericAddress(in.readString()); - int srcPort = in.readInt(); - int dstPort = in.readInt(); - byte[] packet = in.createByteArray(); - int tcpSeq = in.readInt(); - int tcpAck = in.readInt(); - int tcpWnd = in.readInt(); - int tcpWndScale = in.readInt(); - int ipTos = in.readInt(); - int ipTtl = in.readInt(); - return new TcpKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, packet, tcpSeq, - tcpAck, tcpWnd, tcpWndScale, ipTos, ipTtl); - } - - /** Parcelable Creator. */ - public static final @NonNull Parcelable.Creator<TcpKeepalivePacketData> CREATOR = - new Parcelable.Creator<TcpKeepalivePacketData>() { - public TcpKeepalivePacketData createFromParcel(Parcel in) { - try { - return readFromParcel(in); - } catch (InvalidPacketException e) { - throw new IllegalArgumentException( - "Invalid NAT-T keepalive data: " + e.getError()); - } - } - - public TcpKeepalivePacketData[] newArray(int size) { - return new TcpKeepalivePacketData[size]; - } - }; - - /** - * Convert this TcpKeepalivePacketData to a TcpKeepalivePacketDataParcelable. - */ - @NonNull - public TcpKeepalivePacketDataParcelable toStableParcelable() { - final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable(); - final InetAddress srcAddress = getSrcAddress(); - final InetAddress dstAddress = getDstAddress(); - parcel.srcAddress = srcAddress.getAddress(); - parcel.srcPort = getSrcPort(); - parcel.dstAddress = dstAddress.getAddress(); - parcel.dstPort = getDstPort(); - parcel.seq = tcpSeq; - parcel.ack = tcpAck; - parcel.rcvWnd = tcpWnd; - parcel.rcvWndScale = tcpWndScale; - parcel.tos = ipTos; - parcel.ttl = ipTtl; - return parcel; - } - - @Override - public String toString() { - return "saddr: " + getSrcAddress() - + " daddr: " + getDstAddress() - + " sport: " + getSrcPort() - + " dport: " + getDstPort() - + " seq: " + tcpSeq - + " ack: " + tcpAck - + " wnd: " + tcpWnd - + " wndScale: " + tcpWndScale - + " tos: " + ipTos - + " ttl: " + ipTtl; - } -} diff --git a/services/net/java/android/net/ip/IpClientManager.java b/services/net/java/android/net/ip/IpClientManager.java index db464e732e91..274b6dc1769b 100644 --- a/services/net/java/android/net/ip/IpClientManager.java +++ b/services/net/java/android/net/ip/IpClientManager.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.net.NattKeepalivePacketData; import android.net.ProxyInfo; import android.net.TcpKeepalivePacketData; +import android.net.TcpKeepalivePacketDataParcelable; import android.net.shared.Layer2Information; import android.net.shared.ProvisioningConfiguration; import android.net.util.KeepalivePacketDataUtil; @@ -215,9 +216,20 @@ public class IpClientManager { * Add a TCP keepalive packet filter before setting up keepalive offload. */ public boolean addKeepalivePacketFilter(int slot, TcpKeepalivePacketData pkt) { + return addKeepalivePacketFilter(slot, KeepalivePacketDataUtil.toStableParcelable(pkt)); + } + + /** + * Add a TCP keepalive packet filter before setting up keepalive offload. + * @deprecated This method is for use on pre-S platforms where TcpKeepalivePacketData is not + * system API. On newer platforms use + * addKeepalivePacketFilter(int, TcpKeepalivePacketData) instead. + */ + @Deprecated + public boolean addKeepalivePacketFilter(int slot, TcpKeepalivePacketDataParcelable pkt) { final long token = Binder.clearCallingIdentity(); try { - mIpClient.addKeepalivePacketFilter(slot, pkt.toStableParcelable()); + mIpClient.addKeepalivePacketFilter(slot, pkt); return true; } catch (RemoteException e) { log("Error adding Keepalive Packet Filter ", e); diff --git a/services/net/java/android/net/util/KeepalivePacketDataUtil.java b/services/net/java/android/net/util/KeepalivePacketDataUtil.java index 4466ea0abe0e..f06070b6870d 100644 --- a/services/net/java/android/net/util/KeepalivePacketDataUtil.java +++ b/services/net/java/android/net/util/KeepalivePacketDataUtil.java @@ -16,20 +16,41 @@ package android.net.util; +import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS; + import android.annotation.NonNull; +import android.net.InvalidPacketException; import android.net.NattKeepalivePacketData; import android.net.NattKeepalivePacketDataParcelable; +import android.net.TcpKeepalivePacketData; +import android.net.TcpKeepalivePacketDataParcelable; +import android.system.OsConstants; + +import com.android.net.module.util.IpUtils; import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; -/** @hide */ +/** + * Utility class to convert to/from keepalive data parcelables. + * + * TODO: move to networkstack-client library when it is moved to frameworks/libs/net. + * This class cannot go into other shared libraries as it depends on NetworkStack AIDLs. + * @hide + */ public final class KeepalivePacketDataUtil { - /** - * Convert this NattKeepalivePacketData to a NattKeepalivePacketDataParcelable. + private static final int IPV4_HEADER_LENGTH = 20; + private static final int IPV6_HEADER_LENGTH = 40; + private static final int TCP_HEADER_LENGTH = 20; + + /** + * Convert a NattKeepalivePacketData to a NattKeepalivePacketDataParcelable. */ @NonNull public static NattKeepalivePacketDataParcelable toStableParcelable( - NattKeepalivePacketData pkt) { + @NonNull NattKeepalivePacketData pkt) { final NattKeepalivePacketDataParcelable parcel = new NattKeepalivePacketDataParcelable(); final InetAddress srcAddress = pkt.getSrcAddress(); final InetAddress dstAddress = pkt.getDstAddress(); @@ -39,4 +60,95 @@ public final class KeepalivePacketDataUtil { parcel.dstPort = pkt.getDstPort(); return parcel; } + + /** + * Convert a TcpKeepalivePacketData to a TcpKeepalivePacketDataParcelable. + */ + @NonNull + public static TcpKeepalivePacketDataParcelable toStableParcelable( + @NonNull TcpKeepalivePacketData pkt) { + final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable(); + final InetAddress srcAddress = pkt.getSrcAddress(); + final InetAddress dstAddress = pkt.getDstAddress(); + parcel.srcAddress = srcAddress.getAddress(); + parcel.srcPort = pkt.getSrcPort(); + parcel.dstAddress = dstAddress.getAddress(); + parcel.dstPort = pkt.getDstPort(); + parcel.seq = pkt.tcpSeq; + parcel.ack = pkt.tcpAck; + parcel.rcvWnd = pkt.tcpWindow; + parcel.rcvWndScale = pkt.tcpWindowScale; + parcel.tos = pkt.ipTos; + parcel.ttl = pkt.ipTtl; + return parcel; + } + + /** + * Factory method to create tcp keepalive packet structure. + * @hide + */ + public static TcpKeepalivePacketData fromStableParcelable( + TcpKeepalivePacketDataParcelable tcpDetails) throws InvalidPacketException { + final byte[] packet; + try { + if ((tcpDetails.srcAddress != null) && (tcpDetails.dstAddress != null) + && (tcpDetails.srcAddress.length == 4 /* V4 IP length */) + && (tcpDetails.dstAddress.length == 4 /* V4 IP length */)) { + packet = buildV4Packet(tcpDetails); + } else { + // TODO: support ipv6 + throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS); + } + return new TcpKeepalivePacketData( + InetAddress.getByAddress(tcpDetails.srcAddress), + tcpDetails.srcPort, + InetAddress.getByAddress(tcpDetails.dstAddress), + tcpDetails.dstPort, + packet, + tcpDetails.seq, tcpDetails.ack, tcpDetails.rcvWnd, tcpDetails.rcvWndScale, + tcpDetails.tos, tcpDetails.ttl); + } catch (UnknownHostException e) { + throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS); + } + + } + + /** + * Build ipv4 tcp keepalive packet, not including the link-layer header. + */ + // TODO : if this code is ever moved to the network stack, factorize constants with the ones + // over there. + private static byte[] buildV4Packet(TcpKeepalivePacketDataParcelable tcpDetails) { + final int length = IPV4_HEADER_LENGTH + TCP_HEADER_LENGTH; + ByteBuffer buf = ByteBuffer.allocate(length); + buf.order(ByteOrder.BIG_ENDIAN); + buf.put((byte) 0x45); // IP version and IHL + buf.put((byte) tcpDetails.tos); // TOS + buf.putShort((short) length); + buf.putInt(0x00004000); // ID, flags=DF, offset + buf.put((byte) tcpDetails.ttl); // TTL + buf.put((byte) OsConstants.IPPROTO_TCP); + final int ipChecksumOffset = buf.position(); + buf.putShort((short) 0); // IP checksum + buf.put(tcpDetails.srcAddress); + buf.put(tcpDetails.dstAddress); + buf.putShort((short) tcpDetails.srcPort); + buf.putShort((short) tcpDetails.dstPort); + buf.putInt(tcpDetails.seq); // Sequence Number + buf.putInt(tcpDetails.ack); // ACK + buf.putShort((short) 0x5010); // TCP length=5, flags=ACK + buf.putShort((short) (tcpDetails.rcvWnd >> tcpDetails.rcvWndScale)); // Window size + final int tcpChecksumOffset = buf.position(); + buf.putShort((short) 0); // TCP checksum + // URG is not set therefore the urgent pointer is zero. + buf.putShort((short) 0); // Urgent pointer + + buf.putShort(ipChecksumOffset, com.android.net.module.util.IpUtils.ipChecksum(buf, 0)); + buf.putShort(tcpChecksumOffset, IpUtils.tcpChecksum( + buf, 0, IPV4_HEADER_LENGTH, TCP_HEADER_LENGTH)); + + return buf.array(); + } + + // TODO: add buildV6Packet. } diff --git a/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt b/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt new file mode 100644 index 000000000000..677006692f84 --- /dev/null +++ b/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net + +import android.net.InetAddresses.parseNumericAddress +import android.os.Build +import com.android.testutils.DevSdkIgnoreRule +import com.android.testutils.DevSdkIgnoreRunner +import com.android.testutils.assertFieldCountEquals +import com.android.testutils.assertParcelSane +import org.junit.Test +import org.junit.runner.RunWith +import java.net.InetAddress +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals +import kotlin.test.assertTrue + +@RunWith(DevSdkIgnoreRunner::class) +@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) // TcpKeepalivePacketData added to SDK in S +class TcpKeepalivePacketDataTest { + private fun makeData( + srcAddress: InetAddress = parseNumericAddress("192.0.2.123"), + srcPort: Int = 1234, + dstAddress: InetAddress = parseNumericAddress("192.0.2.231"), + dstPort: Int = 4321, + data: ByteArray = byteArrayOf(1, 2, 3), + tcpSeq: Int = 135, + tcpAck: Int = 246, + tcpWnd: Int = 1234, + tcpWndScale: Int = 2, + ipTos: Int = 0x12, + ipTtl: Int = 10 + ) = TcpKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, data, tcpSeq, tcpAck, + tcpWnd, tcpWndScale, ipTos, ipTtl) + + @Test + fun testEquals() { + val data1 = makeData() + val data2 = makeData() + assertEquals(data1, data2) + assertEquals(data1.hashCode(), data2.hashCode()) + } + + @Test + fun testNotEquals() { + assertNotEquals(makeData(srcAddress = parseNumericAddress("192.0.2.124")), makeData()) + assertNotEquals(makeData(srcPort = 1235), makeData()) + assertNotEquals(makeData(dstAddress = parseNumericAddress("192.0.2.232")), makeData()) + assertNotEquals(makeData(dstPort = 4322), makeData()) + // .equals does not test .packet, as it should be generated from the other fields + assertNotEquals(makeData(tcpSeq = 136), makeData()) + assertNotEquals(makeData(tcpAck = 247), makeData()) + assertNotEquals(makeData(tcpWnd = 1235), makeData()) + assertNotEquals(makeData(tcpWndScale = 3), makeData()) + assertNotEquals(makeData(ipTos = 0x14), makeData()) + assertNotEquals(makeData(ipTtl = 11), makeData()) + + // Update above assertions if field is added + assertFieldCountEquals(5, KeepalivePacketData::class.java) + assertFieldCountEquals(6, TcpKeepalivePacketData::class.java) + } + + @Test + fun testParcelUnparcel() { + assertParcelSane(makeData(), fieldCount = 6) { a, b -> + // .equals() does not verify .packet + a == b && a.packet contentEquals b.packet + } + } + + @Test + fun testToString() { + val data = makeData() + val str = data.toString() + + assertTrue(str.contains(data.srcAddress.hostAddress)) + assertTrue(str.contains(data.srcPort.toString())) + assertTrue(str.contains(data.dstAddress.hostAddress)) + assertTrue(str.contains(data.dstPort.toString())) + // .packet not included in toString() + assertTrue(str.contains(data.tcpSeq.toString())) + assertTrue(str.contains(data.tcpAck.toString())) + assertTrue(str.contains(data.tcpWindow.toString())) + assertTrue(str.contains(data.tcpWindowScale.toString())) + assertTrue(str.contains(data.ipTos.toString())) + assertTrue(str.contains(data.ipTtl.toString())) + + // Update above assertions if field is added + assertFieldCountEquals(5, KeepalivePacketData::class.java) + assertFieldCountEquals(6, TcpKeepalivePacketData::class.java) + } +}
\ No newline at end of file diff --git a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java b/tests/net/java/android/net/KeepalivePacketDataUtilTest.java index c5b25bdcac48..9ae3595adbdb 100644 --- a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java +++ b/tests/net/java/android/net/KeepalivePacketDataUtilTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,8 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; +import android.net.util.KeepalivePacketDataUtil; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -31,7 +33,7 @@ import java.net.InetAddress; import java.nio.ByteBuffer; @RunWith(JUnit4.class) -public final class TcpKeepalivePacketDataTest { +public final class KeepalivePacketDataUtilTest { private static final byte[] IPV4_KEEPALIVE_SRC_ADDR = {10, 0, 0, 1}; private static final byte[] IPV4_KEEPALIVE_DST_ADDR = {10, 0, 0, 5}; @@ -39,7 +41,7 @@ public final class TcpKeepalivePacketDataTest { public void setUp() {} @Test - public void testV4TcpKeepalivePacket() throws Exception { + public void testFromTcpKeepaliveStableParcelable() throws Exception { final int srcPort = 1234; final int dstPort = 4321; final int seq = 0x11111111; @@ -61,7 +63,7 @@ public final class TcpKeepalivePacketDataTest { testInfo.tos = tos; testInfo.ttl = ttl; try { - resultData = TcpKeepalivePacketData.tcpKeepalivePacket(testInfo); + resultData = KeepalivePacketDataUtil.fromStableParcelable(testInfo); } catch (InvalidPacketException e) { fail("InvalidPacketException: " + e); } @@ -72,8 +74,8 @@ public final class TcpKeepalivePacketDataTest { assertEquals(testInfo.dstPort, resultData.getDstPort()); assertEquals(testInfo.seq, resultData.tcpSeq); assertEquals(testInfo.ack, resultData.tcpAck); - assertEquals(testInfo.rcvWnd, resultData.tcpWnd); - assertEquals(testInfo.rcvWndScale, resultData.tcpWndScale); + assertEquals(testInfo.rcvWnd, resultData.tcpWindow); + assertEquals(testInfo.rcvWndScale, resultData.tcpWindowScale); assertEquals(testInfo.tos, resultData.ipTos); assertEquals(testInfo.ttl, resultData.ipTtl); @@ -113,7 +115,7 @@ public final class TcpKeepalivePacketDataTest { //TODO: add ipv6 test when ipv6 supported @Test - public void testParcel() throws Exception { + public void testToTcpKeepaliveStableParcelable() throws Exception { final int srcPort = 1234; final int dstPort = 4321; final int sequence = 0x11111111; @@ -135,8 +137,8 @@ public final class TcpKeepalivePacketDataTest { testInfo.ttl = ttl; TcpKeepalivePacketData testData = null; TcpKeepalivePacketDataParcelable resultData = null; - testData = TcpKeepalivePacketData.tcpKeepalivePacket(testInfo); - resultData = testData.toStableParcelable(); + testData = KeepalivePacketDataUtil.fromStableParcelable(testInfo); + resultData = KeepalivePacketDataUtil.toStableParcelable(testData); assertArrayEquals(resultData.srcAddress, IPV4_KEEPALIVE_SRC_ADDR); assertArrayEquals(resultData.dstAddress, IPV4_KEEPALIVE_DST_ADDR); assertEquals(resultData.srcPort, srcPort); |