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

Commit eaca9e9a authored by markchien's avatar markchien
Browse files

Monitor interface added and update bpf interface map

Have BpfInterfaceMapUpdater to update bpf interface map: adding
the interface and index mapping to bpf interface map when interface
added.

Bug: 215095957
Test: atest FrameworkNetTests

Change-Id: I2189a50c4869cfc0c33fc6f0228f40ee9f3ac1d4
parent 614c30b6
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: [
@@ -97,3 +99,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
@@ -356,6 +356,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");
    }
@@ -453,6 +456,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
        mContentObserver = mDeps.makeContentObserver(mHandler, mSettings,
                mNetworkStatsSubscriptionsMonitor);
        mLocationPermissionChecker = mDeps.makeLocationPermissionChecker(mContext);
        mInterfaceMapUpdater = mDeps.makeBpfInterfaceMapUpdater(mContext, mHandler);
        mInterfaceMapUpdater.start();
    }

    /**
@@ -507,6 +512,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