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

Commit d6ba5ab1 authored by Maciej Żenczykowski's avatar Maciej Żenczykowski Committed by Gerrit Code Review
Browse files

Merge "Monitor interface added and update bpf interface map"

parents b85c5d76 eaca9e9a
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@ filegroup {
    srcs: [
        "src/com/android/server/net/NetworkIdentity*.java",
        "src/com/android/server/net/NetworkStats*.java",
        "src/com/android/server/net/BpfInterfaceMapUpdater.java",
        "src/com/android/server/net/InterfaceMapValue.java",
    ],
    path: "src",
    visibility: [
@@ -98,3 +100,28 @@ filegroup {
        "//packages/modules/Connectivity:__subpackages__",
    ],
}

cc_library_shared {
    name: "libcom_android_net_module_util_jni",
    min_sdk_version: "30",
    cflags: [
        "-Wall",
        "-Werror",
        "-Wno-unused-parameter",
        "-Wthread-safety",
    ],
    srcs: [
        "jni/onload.cpp",
    ],
    stl: "libc++_static",
    static_libs: [
        "libnet_utils_device_common_bpfjni",
    ],
    shared_libs: [
        "liblog",
        "libnativehelper",
    ],
    apex_available: [
        "//apex_available:platform",
    ],
}
+38 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

#include <nativehelper/JNIHelp.h>
#include <log/log.h>

namespace android {

int register_com_android_net_module_util_BpfMap(JNIEnv* env, char const* class_name);

extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
    JNIEnv *env;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
        ALOGE("GetEnv failed");
        return JNI_ERR;
    }

    if (register_com_android_net_module_util_BpfMap(env,
            "com/android/net/module/util/BpfMap") < 0) return JNI_ERR;

    return JNI_VERSION_1_6;
}

};
+139 −0
Original line number Diff line number Diff line
/*
 * 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 com.android.server.net;

import android.content.Context;
import android.net.INetd;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.system.ErrnoException;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
import com.android.net.module.util.BpfMap;
import com.android.net.module.util.IBpfMap;
import com.android.net.module.util.InterfaceParams;
import com.android.net.module.util.Struct.U32;

/**
 * Monitor interface added (without removed) and right interface name and its index to bpf map.
 */
public class BpfInterfaceMapUpdater {
    private static final String TAG = BpfInterfaceMapUpdater.class.getSimpleName();
    // This is current path but may be changed soon.
    private static final String IFACE_INDEX_NAME_MAP_PATH =
            "/sys/fs/bpf/map_netd_iface_index_name_map";
    private final IBpfMap<U32, InterfaceMapValue> mBpfMap;
    private final INetd mNetd;
    private final Handler mHandler;
    private final Dependencies mDeps;

    public BpfInterfaceMapUpdater(Context ctx, Handler handler) {
        this(ctx, handler, new Dependencies());
    }

    @VisibleForTesting
    public BpfInterfaceMapUpdater(Context ctx, Handler handler, Dependencies deps) {
        mDeps = deps;
        mBpfMap = deps.getInterfaceMap();
        mNetd = deps.getINetd(ctx);
        mHandler = handler;
    }

    /**
     * Dependencies of BpfInerfaceMapUpdater, for injection in tests.
     */
    @VisibleForTesting
    public static class Dependencies {
        /** Create BpfMap for updating interface and index mapping. */
        public IBpfMap<U32, InterfaceMapValue> getInterfaceMap() {
            try {
                return new BpfMap<>(IFACE_INDEX_NAME_MAP_PATH, BpfMap.BPF_F_RDWR,
                    U32.class, InterfaceMapValue.class);
            } catch (ErrnoException e) {
                Log.e(TAG, "Cannot create interface map: " + e);
                return null;
            }
        }

        /** Get InterfaceParams for giving interface name. */
        public InterfaceParams getInterfaceParams(String ifaceName) {
            return InterfaceParams.getByName(ifaceName);
        }

        /** Get INetd binder object. */
        public INetd getINetd(Context ctx) {
            return INetd.Stub.asInterface((IBinder) ctx.getSystemService(Context.NETD_SERVICE));
        }
    }

    /**
     * Start listening interface update event.
     * Query current interface names before listening.
     */
    public void start() {
        mHandler.post(() -> {
            if (mBpfMap == null) {
                Log.wtf(TAG, "Fail to start: Null bpf map");
                return;
            }

            try {
                // TODO: use a NetlinkMonitor and listen for RTM_NEWLINK messages instead.
                mNetd.registerUnsolicitedEventListener(new InterfaceChangeObserver());
            } catch (RemoteException e) {
                Log.wtf(TAG, "Unable to register netd UnsolicitedEventListener, " + e);
            }

            final String[] ifaces;
            try {
                // TODO: use a netlink dump to get the current interface list.
                ifaces = mNetd.interfaceGetList();
            } catch (RemoteException | ServiceSpecificException e) {
                Log.wtf(TAG, "Unable to query interface names by netd, " + e);
                return;
            }

            for (String ifaceName : ifaces) {
                addInterface(ifaceName);
            }
        });
    }

    private void addInterface(String ifaceName) {
        final InterfaceParams iface = mDeps.getInterfaceParams(ifaceName);
        if (iface == null) {
            Log.e(TAG, "Unable to get InterfaceParams for " + ifaceName);
            return;
        }

        try {
            mBpfMap.updateEntry(new U32(iface.index), new InterfaceMapValue(ifaceName));
        } catch (ErrnoException e) {
            Log.e(TAG, "Unable to update entry for " + ifaceName + ", " + e);
        }
    }

    private class InterfaceChangeObserver extends BaseNetdUnsolicitedEventListener {
        @Override
        public void onInterfaceAdded(String ifName) {
            mHandler.post(() -> addInterface(ifName));
        }
    }
}
+35 −0
Original line number Diff line number Diff line
/*
 * 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 com.android.server.net;

import com.android.net.module.util.Struct;
import com.android.net.module.util.Struct.Field;
import com.android.net.module.util.Struct.Type;

/**
 * The value of bpf interface index map which is used for NetworkStatsService.
 */
public class InterfaceMapValue extends Struct {
    @Field(order = 0, type = Type.ByteArray, arraysize = 16)
    public final byte[] interfaceName;

    public InterfaceMapValue(String iface) {
        final byte[] ifaceArray = iface.getBytes();
        interfaceName = new byte[16];
        // All array bytes after the interface name, if any, must be 0.
        System.arraycopy(ifaceArray, 0, interfaceName, 0, ifaceArray.length);
    }
}
+12 −0
Original line number Diff line number Diff line
@@ -357,6 +357,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
    @NonNull
    private final LocationPermissionChecker mLocationPermissionChecker;

    @NonNull
    private final BpfInterfaceMapUpdater mInterfaceMapUpdater;

    private static @NonNull File getDefaultSystemDir() {
        return new File(Environment.getDataDirectory(), "system");
    }
@@ -454,6 +457,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
        mContentObserver = mDeps.makeContentObserver(mHandler, mSettings,
                mNetworkStatsSubscriptionsMonitor);
        mLocationPermissionChecker = mDeps.makeLocationPermissionChecker(mContext);
        mInterfaceMapUpdater = mDeps.makeBpfInterfaceMapUpdater(mContext, mHandler);
        mInterfaceMapUpdater.start();
    }

    /**
@@ -508,6 +513,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
        public LocationPermissionChecker makeLocationPermissionChecker(final Context context) {
            return new LocationPermissionChecker(context);
        }

        /** Create BpfInterfaceMapUpdater to update bpf interface map. */
        @NonNull
        public BpfInterfaceMapUpdater makeBpfInterfaceMapUpdater(
                @NonNull Context ctx, @NonNull Handler handler) {
            return new BpfInterfaceMapUpdater(ctx, handler);
        }
    }

    /**
Loading