Support QosCallback for UDP socket -Filter

Add matchProtocol() to QosFilter.
Add remoteAddress check to validate().
Add exception EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED
Don't send ParcelFileDescriptor to QosProvider for security reason

Bug: 203146631
Test: atest & verified on LTE test equipment
Change-Id: I7102ae9ba7cb9e1cc8d06b252aad4dad75860f3e
This commit is contained in:
sewookseo 2022-03-07 11:03:13 +00:00
commit afc22b02e2
7 changed files with 187 additions and 31 deletions

View file

@ -46,8 +46,10 @@ public final class QosCallbackException extends Exception {
EX_TYPE_FILTER_NONE,
EX_TYPE_FILTER_NETWORK_RELEASED,
EX_TYPE_FILTER_SOCKET_NOT_BOUND,
EX_TYPE_FILTER_SOCKET_NOT_CONNECTED,
EX_TYPE_FILTER_NOT_SUPPORTED,
EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED,
EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ExceptionType {}
@ -65,10 +67,16 @@ public final class QosCallbackException extends Exception {
public static final int EX_TYPE_FILTER_SOCKET_NOT_BOUND = 2;
/** {@hide} */
public static final int EX_TYPE_FILTER_NOT_SUPPORTED = 3;
public static final int EX_TYPE_FILTER_SOCKET_NOT_CONNECTED = 3;
/** {@hide} */
public static final int EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED = 4;
public static final int EX_TYPE_FILTER_NOT_SUPPORTED = 4;
/** {@hide} */
public static final int EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED = 5;
/** {@hide} */
public static final int EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED = 6;
/**
* Creates exception based off of a type and message. Not all types of exceptions accept a
@ -83,12 +91,17 @@ public final class QosCallbackException extends Exception {
return new QosCallbackException(new NetworkReleasedException());
case EX_TYPE_FILTER_SOCKET_NOT_BOUND:
return new QosCallbackException(new SocketNotBoundException());
case EX_TYPE_FILTER_SOCKET_NOT_CONNECTED:
return new QosCallbackException(new SocketNotConnectedException());
case EX_TYPE_FILTER_NOT_SUPPORTED:
return new QosCallbackException(new UnsupportedOperationException(
"This device does not support the specified filter"));
case EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED:
return new QosCallbackException(
new SocketLocalAddressChangedException());
case EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED:
return new QosCallbackException(
new SocketRemoteAddressChangedException());
default:
Log.wtf(TAG, "create: No case setup for exception type: '" + type + "'");
return new QosCallbackException(

View file

@ -90,5 +90,15 @@ public abstract class QosFilter {
*/
public abstract boolean matchesRemoteAddress(@NonNull InetAddress address,
int startPort, int endPort);
/**
* Determines whether or not the parameter will be matched with this filter.
*
* @param protocol the protocol such as TCP or UDP included in IP packet filter set of a QoS
* flow assigned on {@link Network}.
* @return whether the parameters match the socket type of the filter
* @hide
*/
public abstract boolean matchesProtocol(int protocol);
}

View file

@ -104,7 +104,7 @@ public final class QosFilterParcelable implements Parcelable {
if (mQosFilter instanceof QosSocketFilter) {
dest.writeInt(QOS_SOCKET_FILTER);
final QosSocketFilter qosSocketFilter = (QosSocketFilter) mQosFilter;
qosSocketFilter.getQosSocketInfo().writeToParcel(dest, 0);
qosSocketFilter.getQosSocketInfo().writeToParcelWithoutFd(dest, 0);
return;
}
dest.writeInt(NO_FILTER_PRESENT);

View file

@ -18,6 +18,13 @@ package android.net;
import static android.net.QosCallbackException.EX_TYPE_FILTER_NONE;
import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED;
import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_BOUND;
import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_CONNECTED;
import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED;
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
import static android.system.OsConstants.SOCK_DGRAM;
import static android.system.OsConstants.SOCK_STREAM;
import android.annotation.NonNull;
import android.annotation.Nullable;
@ -74,19 +81,34 @@ public class QosSocketFilter extends QosFilter {
* 2. In the scenario that the socket is now bound to a different local address, which can
* happen in the case of UDP, then
* {@link QosCallbackException.EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED} is returned.
* 3. In the scenario that the UDP socket changed remote address, then
* {@link QosCallbackException.EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED} is returned.
*
* @return validation error code
*/
@Override
public int validate() {
final InetSocketAddress sa = getAddressFromFileDescriptor();
if (sa == null) {
return QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_BOUND;
final InetSocketAddress sa = getLocalAddressFromFileDescriptor();
if (sa == null || (sa.getAddress().isAnyLocalAddress() && sa.getPort() == 0)) {
return EX_TYPE_FILTER_SOCKET_NOT_BOUND;
}
if (!sa.equals(mQosSocketInfo.getLocalSocketAddress())) {
return EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED;
}
if (mQosSocketInfo.getRemoteSocketAddress() != null) {
final InetSocketAddress da = getRemoteAddressFromFileDescriptor();
if (da == null) {
return EX_TYPE_FILTER_SOCKET_NOT_CONNECTED;
}
if (!da.equals(mQosSocketInfo.getRemoteSocketAddress())) {
return EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED;
}
}
return EX_TYPE_FILTER_NONE;
}
@ -98,17 +120,14 @@ public class QosSocketFilter extends QosFilter {
* @return the local address
*/
@Nullable
private InetSocketAddress getAddressFromFileDescriptor() {
private InetSocketAddress getLocalAddressFromFileDescriptor() {
final ParcelFileDescriptor parcelFileDescriptor = mQosSocketInfo.getParcelFileDescriptor();
if (parcelFileDescriptor == null) return null;
final FileDescriptor fd = parcelFileDescriptor.getFileDescriptor();
if (fd == null) return null;
final SocketAddress address;
try {
address = Os.getsockname(fd);
} catch (final ErrnoException e) {
} catch (ErrnoException e) {
Log.e(TAG, "getAddressFromFileDescriptor: getLocalAddress exception", e);
return null;
}
@ -118,6 +137,31 @@ public class QosSocketFilter extends QosFilter {
return null;
}
/**
* The remote address of the socket's connected.
*
* <p>Note: If the socket is no longer connected, null is returned.
*
* @return the remote address
*/
@Nullable
private InetSocketAddress getRemoteAddressFromFileDescriptor() {
final ParcelFileDescriptor parcelFileDescriptor = mQosSocketInfo.getParcelFileDescriptor();
final FileDescriptor fd = parcelFileDescriptor.getFileDescriptor();
final SocketAddress address;
try {
address = Os.getpeername(fd);
} catch (ErrnoException e) {
Log.e(TAG, "getAddressFromFileDescriptor: getRemoteAddress exception", e);
return null;
}
if (address instanceof InetSocketAddress) {
return (InetSocketAddress) address;
}
return null;
}
/**
* The network used with this filter.
*
@ -155,6 +199,18 @@ public class QosSocketFilter extends QosFilter {
endPort);
}
/**
* @inheritDoc
*/
@Override
public boolean matchesProtocol(final int protocol) {
if ((mQosSocketInfo.getSocketType() == SOCK_STREAM && protocol == IPPROTO_TCP)
|| (mQosSocketInfo.getSocketType() == SOCK_DGRAM && protocol == IPPROTO_UDP)) {
return true;
}
return false;
}
/**
* Called from {@link QosSocketFilter#matchesLocalAddress(InetAddress, int, int)}
* and {@link QosSocketFilter#matchesRemoteAddress(InetAddress, int, int)} with the
@ -174,6 +230,7 @@ public class QosSocketFilter extends QosFilter {
final int startPort, final int endPort) {
return startPort <= filterSocketAddress.getPort()
&& endPort >= filterSocketAddress.getPort()
&& filterSocketAddress.getAddress().equals(address);
&& (address.isAnyLocalAddress()
|| filterSocketAddress.getAddress().equals(address));
}
}

View file

@ -165,25 +165,28 @@ public final class QosSocketInfo implements Parcelable {
/* Parcelable methods */
private QosSocketInfo(final Parcel in) {
mNetwork = Objects.requireNonNull(Network.CREATOR.createFromParcel(in));
mParcelFileDescriptor = ParcelFileDescriptor.CREATOR.createFromParcel(in);
final boolean withFd = in.readBoolean();
if (withFd) {
mParcelFileDescriptor = ParcelFileDescriptor.CREATOR.createFromParcel(in);
} else {
mParcelFileDescriptor = null;
}
final int localAddressLength = in.readInt();
mLocalSocketAddress = readSocketAddress(in, localAddressLength);
final int remoteAddressLength = in.readInt();
mRemoteSocketAddress = remoteAddressLength == 0 ? null
: readSocketAddress(in, remoteAddressLength);
mLocalSocketAddress = readSocketAddress(in);
mRemoteSocketAddress = readSocketAddress(in);
mSocketType = in.readInt();
}
private @NonNull InetSocketAddress readSocketAddress(final Parcel in, final int addressLength) {
final byte[] address = new byte[addressLength];
in.readByteArray(address);
private InetSocketAddress readSocketAddress(final Parcel in) {
final byte[] addrBytes = in.createByteArray();
if (addrBytes == null) {
return null;
}
final int port = in.readInt();
try {
return new InetSocketAddress(InetAddress.getByAddress(address), port);
return new InetSocketAddress(InetAddress.getByAddress(addrBytes), port);
} catch (final UnknownHostException e) {
/* This can never happen. UnknownHostException will never be thrown
since the address provided is numeric and non-null. */
@ -198,20 +201,35 @@ public final class QosSocketInfo implements Parcelable {
@Override
public void writeToParcel(@NonNull final Parcel dest, final int flags) {
mNetwork.writeToParcel(dest, 0);
mParcelFileDescriptor.writeToParcel(dest, 0);
writeToParcelInternal(dest, flags, /*includeFd=*/ true);
}
final byte[] localAddress = mLocalSocketAddress.getAddress().getAddress();
dest.writeInt(localAddress.length);
dest.writeByteArray(localAddress);
/**
* Used when sending QosSocketInfo to telephony, which does not need access to the socket FD.
* @hide
*/
public void writeToParcelWithoutFd(@NonNull final Parcel dest, final int flags) {
writeToParcelInternal(dest, flags, /*includeFd=*/ false);
}
private void writeToParcelInternal(
@NonNull final Parcel dest, final int flags, boolean includeFd) {
mNetwork.writeToParcel(dest, 0);
if (includeFd) {
dest.writeBoolean(true);
mParcelFileDescriptor.writeToParcel(dest, 0);
} else {
dest.writeBoolean(false);
}
dest.writeByteArray(mLocalSocketAddress.getAddress().getAddress());
dest.writeInt(mLocalSocketAddress.getPort());
if (mRemoteSocketAddress == null) {
dest.writeInt(0);
dest.writeByteArray(null);
} else {
final byte[] remoteAddress = mRemoteSocketAddress.getAddress().getAddress();
dest.writeInt(remoteAddress.length);
dest.writeByteArray(remoteAddress);
dest.writeByteArray(mRemoteSocketAddress.getAddress().getAddress());
dest.writeInt(mRemoteSocketAddress.getPort());
}
dest.writeInt(mSocketType);

View file

@ -0,0 +1,29 @@
/*
* Copyright (C) 2022 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;
/**
* Thrown when a previously bound socket becomes unbound.
*
* @hide
*/
public class SocketNotConnectedException extends Exception {
/** @hide */
public SocketNotConnectedException() {
super("The socket is not connected");
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (C) 2022 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;
/**
* Thrown when the local address of the socket has changed.
*
* @hide
*/
public class SocketRemoteAddressChangedException extends Exception {
/** @hide */
public SocketRemoteAddressChangedException() {
super("The remote address of the socket changed");
}
}