Merge "[KA03] Support tcp keepalive offload"

am: 5f8ddc2eb1

Change-Id: I7715266c2c24d3f5dd65cd0e375d99b16be6aea6
This commit is contained in:
Chalard Jean
2019-02-05 22:19:41 -08:00
committed by android-build-merger
13 changed files with 848 additions and 28 deletions

View File

@@ -96,7 +96,7 @@ public class KeepalivePacketData implements Parcelable {
out.writeByteArray(mPacket);
}
private KeepalivePacketData(Parcel in) {
protected KeepalivePacketData(Parcel in) {
srcAddress = NetworkUtils.numericToInetAddress(in.readString());
dstAddress = NetworkUtils.numericToInetAddress(in.readString());
srcPort = in.readInt();

View File

@@ -16,6 +16,9 @@
package android.net;
import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
import static android.net.SocketKeepalive.ERROR_INVALID_PORT;
import android.net.SocketKeepalive.InvalidPacketException;
import android.net.util.IpUtils;
import android.system.OsConstants;
@@ -44,11 +47,11 @@ public final class NattKeepalivePacketData extends KeepalivePacketData {
throws InvalidPacketException {
if (!(srcAddress instanceof Inet4Address) || !(dstAddress instanceof Inet4Address)) {
throw new InvalidPacketException(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
}
if (dstPort != NattSocketKeepalive.NATT_PORT) {
throw new InvalidPacketException(SocketKeepalive.ERROR_INVALID_PORT);
throw new InvalidPacketException(ERROR_INVALID_PORT);
}
int length = IPV4_HEADER_LENGTH + UDP_HEADER_LENGTH + 1;

View File

@@ -70,6 +70,18 @@ public class NetworkUtils {
public native static void attachControlPacketFilter(FileDescriptor fd, int packetType)
throws SocketException;
/**
* Attaches a socket filter that drops all of incoming packets.
* @param fd the socket's {@link FileDescriptor}.
*/
public static native void attachDropAllBPFFilter(FileDescriptor fd) throws SocketException;
/**
* Detaches a socket filter.
* @param fd the socket's {@link FileDescriptor}.
*/
public static native void detachBPFFilter(FileDescriptor fd) throws SocketException;
/**
* Configures a socket for receiving ICMPv6 router solicitations and sending advertisements.
* @param fd the socket's {@link FileDescriptor}.
@@ -170,6 +182,16 @@ public class NetworkUtils {
private static native void addArpEntry(byte[] ethAddr, byte[] netAddr, String ifname,
FileDescriptor fd) throws IOException;
/**
* Get the tcp repair window associated with the {@code fd}.
*
* @param fd the tcp socket's {@link FileDescriptor}.
* @return a {@link TcpRepairWindow} object indicates tcp window size.
*/
public static native TcpRepairWindow getTcpRepairWindow(FileDescriptor fd)
throws ErrnoException;
/**
* @see Inet4AddressUtils#intToInet4AddressHTL(int)
* @deprecated Use either {@link Inet4AddressUtils#intToInet4AddressHTH(int)}

View File

@@ -109,15 +109,43 @@ public abstract class SocketKeepalive implements AutoCloseable {
**/
public static final int MAX_INTERVAL_SEC = 3600;
/**
* An exception that embarks an error code.
* @hide
*/
public static class ErrorCodeException extends Exception {
public final int error;
public ErrorCodeException(final int error, final Throwable e) {
super(e);
this.error = error;
}
public ErrorCodeException(final int error) {
this.error = error;
}
}
/**
* This socket is invalid.
* See the error code for details, and the optional cause.
* @hide
*/
public static class InvalidSocketException extends ErrorCodeException {
public InvalidSocketException(final int error, final Throwable e) {
super(error, e);
}
public InvalidSocketException(final int error) {
super(error);
}
}
/**
* This packet is invalid.
* See the error code for details.
* @hide
*/
public static class InvalidPacketException extends Exception {
public final int error;
public InvalidPacketException(int error) {
this.error = error;
public static class InvalidPacketException extends ErrorCodeException {
public InvalidPacketException(final int error) {
super(error);
}
}

View File

@@ -0,0 +1,20 @@
/*
* 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;
parcelable TcpKeepalivePacketData;

View File

@@ -0,0 +1,202 @@
/*
* 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.Nullable;
import android.net.SocketKeepalive.InvalidPacketException;
import android.net.util.IpUtils;
import android.os.Parcel;
import android.os.Parcelable;
import android.system.OsConstants;
import java.net.Inet4Address;
import java.net.InetAddress;
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;
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(TcpSocketInfo tcpDetails, byte[] data)
throws InvalidPacketException {
super(tcpDetails.srcAddress, tcpDetails.srcPort, 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;
}
/**
* Factory method to create tcp keepalive packet structure.
*/
public static TcpKeepalivePacketData tcpKeepalivePacket(
TcpSocketInfo tcpDetails) throws InvalidPacketException {
final byte[] packet;
if ((tcpDetails.srcAddress instanceof Inet4Address)
&& (tcpDetails.dstAddress instanceof Inet4Address)) {
packet = buildV4Packet(tcpDetails);
} else {
// TODO: support ipv6
throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
}
return new TcpKeepalivePacketData(tcpDetails, packet);
}
/**
* 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(TcpSocketInfo tcpDetails) {
final int length = IPV4_HEADER_LENGTH + TCP_HEADER_LENGTH;
ByteBuffer buf = ByteBuffer.allocate(length);
buf.order(ByteOrder.BIG_ENDIAN);
// IP version and TOS. TODO : fetch this from getsockopt(SOL_IP, IP_TOS)
buf.putShort((short) 0x4500);
buf.putShort((short) length);
buf.putInt(0x4000); // ID, flags=DF, offset
// TODO : fetch TTL from getsockopt(SOL_IP, IP_TTL)
buf.put((byte) 64);
buf.put((byte) OsConstants.IPPROTO_TCP);
final int ipChecksumOffset = buf.position();
buf.putShort((short) 0); // IP checksum
buf.put(tcpDetails.srcAddress.getAddress());
buf.put(tcpDetails.dstAddress.getAddress());
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 not included
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.
/** Represents tcp/ip information. */
public static class TcpSocketInfo {
public final InetAddress srcAddress;
public final InetAddress dstAddress;
public final int srcPort;
public final int dstPort;
public final int seq;
public final int ack;
public final int rcvWnd;
public final int rcvWndScale;
public TcpSocketInfo(InetAddress sAddr, int sPort, InetAddress dAddr,
int dPort, int writeSeq, int readSeq, int rWnd, int rWndScale) {
srcAddress = sAddr;
dstAddress = dAddr;
srcPort = sPort;
dstPort = dPort;
seq = writeSeq;
ack = readSeq;
rcvWnd = rWnd;
rcvWndScale = rWndScale;
}
}
@Override
public boolean equals(@Nullable final Object o) {
if (!(o instanceof TcpKeepalivePacketData)) return false;
final TcpKeepalivePacketData other = (TcpKeepalivePacketData) o;
return this.srcAddress.equals(other.srcAddress)
&& this.dstAddress.equals(other.dstAddress)
&& this.srcPort == other.srcPort
&& this.dstPort == other.dstPort
&& this.tcpAck == other.tcpAck
&& this.tcpSeq == other.tcpSeq
&& this.tcpWnd == other.tcpWnd
&& this.tcpWndScale == other.tcpWndScale;
}
@Override
public int hashCode() {
return Objects.hash(srcAddress, dstAddress, srcPort, dstPort, tcpAck, tcpSeq, tcpWnd,
tcpWndScale);
}
/* Parcelable Implementation. */
/** No special parcel contents. */
public int describeContents() {
return 0;
}
/** Write to parcel. */
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(tcpSeq);
out.writeInt(tcpAck);
out.writeInt(tcpWnd);
out.writeInt(tcpWndScale);
}
private TcpKeepalivePacketData(Parcel in) {
super(in);
tcpSeq = in.readInt();
tcpAck = in.readInt();
tcpWnd = in.readInt();
tcpWndScale = in.readInt();
}
/** Parcelable Creator. */
public static final Parcelable.Creator<TcpKeepalivePacketData> CREATOR =
new Parcelable.Creator<TcpKeepalivePacketData>() {
public TcpKeepalivePacketData createFromParcel(Parcel in) {
return new TcpKeepalivePacketData(in);
}
public TcpKeepalivePacketData[] newArray(int size) {
return new TcpKeepalivePacketData[size];
}
};
}

View File

@@ -0,0 +1,45 @@
/*
* 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;
/**
* Corresponds to C's {@code struct tcp_repair_window} from
* include/uapi/linux/tcp.h
*
* @hide
*/
public final class TcpRepairWindow {
public final int sndWl1;
public final int sndWnd;
public final int maxWindow;
public final int rcvWnd;
public final int rcvWup;
public final int rcvWndScale;
/**
* Constructs an instance with the given field values.
*/
public TcpRepairWindow(final int sndWl1, final int sndWnd, final int maxWindow,
final int rcvWnd, final int rcvWup, final int rcvWndScale) {
this.sndWl1 = sndWl1;
this.sndWnd = sndWnd;
this.maxWindow = maxWindow;
this.rcvWnd = rcvWnd;
this.rcvWup = rcvWup;
this.rcvWndScale = rcvWndScale;
}
}