Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 578a76e7 authored by Paul Jensen's avatar Paul Jensen Committed by Lorenzo Colitti
Browse files

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
parent 853d741c
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -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);
    }
+25 −0
Original line number Diff line number Diff line
@@ -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;
        }

+7 −0
Original line number Diff line number Diff line
@@ -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
+44 −2
Original line number Diff line number Diff line
@@ -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,7 +67,6 @@ 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);
@@ -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)
+29 −0
Original line number Diff line number Diff line
@@ -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);
Loading