Have ConnectivityService install packet filters when possible

Listen for ICMP6 router advertisements on networks that support
packet filters.  Construct packet filters and install them to
ignore redundant future ICMP6 router advertisements.

Bug: 26238573
Change-Id: If78300b9fda257c21f3ee6533e1da7de9f897cb4
This commit is contained in:
Paul Jensen
2016-01-14 14:54:39 -05:00
committed by Lorenzo Colitti
parent 6a6903553b
commit 9eca1f6072
6 changed files with 130 additions and 2 deletions

View File

@@ -200,6 +200,14 @@ public abstract class NetworkAgent extends Handler {
*/
public static final int CMD_PREVENT_AUTOMATIC_RECONNECT = BASE + 15;
/**
* Sent by ConnectivityService to the NetworkAgent to install an APF program in the network
* chipset for use to filter packets.
*
* obj = byte[] containing the APF program bytecode.
*/
public static final int CMD_PUSH_APF_PROGRAM = BASE + 16;
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score) {
this(looper, context, logTag, ni, nc, lp, score, null);
@@ -319,6 +327,10 @@ public abstract class NetworkAgent extends Handler {
preventAutomaticReconnect();
break;
}
case CMD_PUSH_APF_PROGRAM: {
installPacketFilter((byte[]) msg.obj);
break;
}
}
}
@@ -494,6 +506,15 @@ public abstract class NetworkAgent extends Handler {
protected void preventAutomaticReconnect() {
}
/**
* Install a packet filter.
* @param filter an APF program to filter incoming packets.
* @return {@code true} if filter successfully installed, {@code false} otherwise.
*/
protected boolean installPacketFilter(byte[] filter) {
return false;
}
protected void log(String s) {
Log.d(LOG_TAG, "NetworkAgent: " + s);
}

View File

@@ -56,6 +56,22 @@ public class NetworkMisc implements Parcelable {
*/
public String subscriberId;
/**
* Version of APF instruction set supported for packet filtering. 0 indicates no support for
* packet filtering using APF programs.
*/
public int apfVersionSupported;
/**
* Maximum size of APF program allowed.
*/
public int maximumApfProgramSize;
/**
* Format of packets passed to APF filter. Should be one of ARPHRD_*
*/
public int apfPacketFormat;
public NetworkMisc() {
}
@@ -65,6 +81,9 @@ public class NetworkMisc implements Parcelable {
explicitlySelected = nm.explicitlySelected;
acceptUnvalidated = nm.acceptUnvalidated;
subscriberId = nm.subscriberId;
apfVersionSupported = nm.apfVersionSupported;
maximumApfProgramSize = nm.maximumApfProgramSize;
apfPacketFormat = nm.apfPacketFormat;
}
}
@@ -79,6 +98,9 @@ public class NetworkMisc implements Parcelable {
out.writeInt(explicitlySelected ? 1 : 0);
out.writeInt(acceptUnvalidated ? 1 : 0);
out.writeString(subscriberId);
out.writeInt(apfVersionSupported);
out.writeInt(maximumApfProgramSize);
out.writeInt(apfPacketFormat);
}
public static final Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() {
@@ -89,6 +111,9 @@ public class NetworkMisc implements Parcelable {
networkMisc.explicitlySelected = in.readInt() != 0;
networkMisc.acceptUnvalidated = in.readInt() != 0;
networkMisc.subscriberId = in.readString();
networkMisc.apfVersionSupported = in.readInt();
networkMisc.maximumApfProgramSize = in.readInt();
networkMisc.apfPacketFormat = in.readInt();
return networkMisc;
}

View File

@@ -61,6 +61,13 @@ public class NetworkUtils {
*/
public native static void attachDhcpFilter(FileDescriptor fd) throws SocketException;
/**
* Attaches a socket filter that accepts ICMP6 router advertisement packets to the given socket.
* @param fd the socket's {@link FileDescriptor}.
* @param packetType the hardware address type, one of ARPHRD_*.
*/
public native static void attachRaFilter(FileDescriptor fd, int packetType) throws SocketException;
/**
* Binds the current process to the network designated by {@code netId}. All sockets created
* in the future (and not explicitly bound via a bound {@link SocketFactory} (see

View File

@@ -26,10 +26,13 @@
#include <net/if.h>
#include <linux/filter.h>
#include <linux/if.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <net/if_ether.h>
#include <netinet/icmp6.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/udp.h>
#include <cutils/properties.h>
@@ -64,10 +67,9 @@ static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz,
static void android_net_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd)
{
int fd = jniGetFDFromFileDescriptor(env, javaFd);
uint32_t ip_offset = sizeof(ether_header);
uint32_t proto_offset = ip_offset + offsetof(iphdr, protocol);
uint32_t flags_offset = ip_offset + offsetof(iphdr, frag_off);
uint32_t flags_offset = ip_offset + offsetof(iphdr, frag_off);
uint32_t dport_indirect_offset = ip_offset + offsetof(udphdr, dest);
struct sock_filter filter_code[] = {
// Check the protocol is UDP.
@@ -94,6 +96,45 @@ static void android_net_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobje
filter_code,
};
int fd = jniGetFDFromFileDescriptor(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
}
}
static void android_net_utils_attachRaFilter(JNIEnv *env, jobject clazz, jobject javaFd,
jint hardwareAddressType)
{
if (hardwareAddressType != ARPHRD_ETHER) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"attachRaFilter only supports ARPHRD_ETHER");
return;
}
uint32_t ipv6_offset = sizeof(ether_header);
uint32_t ipv6_next_header_offset = ipv6_offset + offsetof(ip6_hdr, ip6_nxt);
uint32_t icmp6_offset = ipv6_offset + sizeof(ip6_hdr);
uint32_t icmp6_type_offset = icmp6_offset + offsetof(icmp6_hdr, icmp6_type);
struct sock_filter filter_code[] = {
// Check IPv6 Next Header is ICMPv6.
BPF_STMT(BPF_LD | BPF_B | BPF_ABS, ipv6_next_header_offset),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3),
// Check ICMPv6 type is Router Advertisement.
BPF_STMT(BPF_LD | BPF_B | BPF_ABS, icmp6_type_offset),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_ROUTER_ADVERT, 0, 1),
// Accept or reject.
BPF_STMT(BPF_RET | BPF_K, 0xffff),
BPF_STMT(BPF_RET | BPF_K, 0)
};
struct sock_fprog filter = {
sizeof(filter_code) / sizeof(filter_code[0]),
filter_code,
};
int fd = jniGetFDFromFileDescriptor(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
@@ -148,6 +189,7 @@ static const JNINativeMethod gNetworkUtilMethods[] = {
{ "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
{ "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
{ "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter },
{ "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter },
};
int register_android_net_NetworkUtils(JNIEnv* env)

View File

@@ -125,6 +125,7 @@ import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkMonitor;
import com.android.server.connectivity.PacManager;
import com.android.server.connectivity.PermissionMonitor;
import com.android.server.connectivity.ApfFilter;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
import com.android.server.net.BaseNetworkObserver;
@@ -359,6 +360,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/
private static final int EVENT_REGISTER_NETWORK_LISTENER_WITH_INTENT = 31;
/**
* used to push APF program to NetworkAgent
* replyTo = NetworkAgent message handler
* obj = byte[] of APF program
*/
private static final int EVENT_PUSH_APF_PROGRAM_TO_NETWORK = 32;
/** Handler thread used for both of the handlers below. */
@VisibleForTesting
protected final HandlerThread mHandlerThread;
@@ -2188,6 +2196,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
mKeepaliveTracker.handleStopAllKeepalives(nai,
ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK);
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
if (nai.apfFilter != null) nai.apfFilter.shutdown();
mNetworkAgentInfos.remove(msg.replyTo);
updateClat(null, nai.linkProperties, nai);
synchronized (mNetworkForNetId) {
@@ -2402,6 +2411,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
accept ? 1 : 0, always ? 1: 0, network));
}
public void pushApfProgramToNetwork(NetworkAgentInfo nai, byte[] program) {
enforceConnectivityInternalPermission();
Message msg = mHandler.obtainMessage(EVENT_PUSH_APF_PROGRAM_TO_NETWORK, program);
msg.replyTo = nai.messenger;
mHandler.sendMessage(msg);
}
private void handleSetAcceptUnvalidated(Network network, boolean accept, boolean always) {
if (DBG) log("handleSetAcceptUnvalidated network=" + network +
" accept=" + accept + " always=" + always);
@@ -2556,6 +2572,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
handleMobileDataAlwaysOn();
break;
}
case EVENT_PUSH_APF_PROGRAM_TO_NETWORK: {
NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
if (nai == null) {
loge("EVENT_PUSH_APF_PROGRAM_TO_NETWORK from unknown NetworkAgent");
} else {
nai.asyncChannel.sendMessage(NetworkAgent.CMD_PUSH_APF_PROGRAM,
(byte[]) msg.obj);
}
break;
}
// Sent by KeepaliveTracker to process an app request on the state machine thread.
case NetworkAgent.CMD_START_PACKET_KEEPALIVE: {
mKeepaliveTracker.handleStartKeepalive(msg);
@@ -3944,6 +3970,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (networkAgent.clatd != null) {
networkAgent.clatd.fixupLinkProperties(oldLp);
}
if (networkAgent.apfFilter != null) {
networkAgent.apfFilter.updateFilter();
}
updateInterfaces(newLp, oldLp, netId);
updateMtu(newLp, oldLp);

View File

@@ -32,6 +32,7 @@ import android.util.SparseArray;
import com.android.internal.util.AsyncChannel;
import com.android.server.ConnectivityService;
import com.android.server.connectivity.NetworkMonitor;
import com.android.server.connectivity.ApfFilter;
import java.util.ArrayList;
import java.util.Comparator;
@@ -163,6 +164,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
// Used by ConnectivityService to keep track of 464xlat.
public Nat464Xlat clatd;
public ApfFilter apfFilter;
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
NetworkMisc misc, NetworkRequest defaultRequest, ConnectivityService connService) {
@@ -175,6 +178,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
currentScore = score;
networkMonitor = connService.createNetworkMonitor(context, handler, this, defaultRequest);
networkMisc = misc;
apfFilter.maybeInstall(connService, this);
}
/**