diff options
Diffstat (limited to 'common/device/com/android/net/module/util/BpfMap.java')
-rw-r--r-- | common/device/com/android/net/module/util/BpfMap.java | 312 |
1 files changed, 0 insertions, 312 deletions
diff --git a/common/device/com/android/net/module/util/BpfMap.java b/common/device/com/android/net/module/util/BpfMap.java deleted file mode 100644 index d45caceb..00000000 --- a/common/device/com/android/net/module/util/BpfMap.java +++ /dev/null @@ -1,312 +0,0 @@ -/* - * 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.net.module.util; - -import static android.system.OsConstants.EEXIST; -import static android.system.OsConstants.ENOENT; - -import android.os.ParcelFileDescriptor; -import android.system.ErrnoException; -import android.util.Pair; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; - -/** - * BpfMap is a key -> value mapping structure that is designed to maintained the bpf map entries. - * This is a wrapper class of in-kernel data structure. The in-kernel data can be read/written by - * passing syscalls with map file descriptor. - * - * @param <K> the key of the map. - * @param <V> the value of the map. - */ -public class BpfMap<K extends Struct, V extends Struct> implements IBpfMap<K, V> { - static { - System.loadLibrary(JniUtil.getJniLibraryName(BpfMap.class.getPackage())); - } - - // Following definitions from kernel include/uapi/linux/bpf.h - public static final int BPF_F_RDWR = 0; - public static final int BPF_F_RDONLY = 1 << 3; - public static final int BPF_F_WRONLY = 1 << 4; - - public static final int BPF_MAP_TYPE_HASH = 1; - - private static final int BPF_F_NO_PREALLOC = 1; - - private static final int BPF_ANY = 0; - private static final int BPF_NOEXIST = 1; - private static final int BPF_EXIST = 2; - - private final ParcelFileDescriptor mMapFd; - private final Class<K> mKeyClass; - private final Class<V> mValueClass; - private final int mKeySize; - private final int mValueSize; - - private static ConcurrentHashMap<Pair<String, Integer>, ParcelFileDescriptor> sFdCache = - new ConcurrentHashMap<>(); - - private static ParcelFileDescriptor cachedBpfFdGet(String path, int mode, - int keySize, int valueSize) - throws ErrnoException, NullPointerException { - // Supports up to 1023 byte key and 65535 byte values - // Creating a BpfMap with larger keys/values seems like a bad idea any way... - keySize &= 1023; // 10-bits - valueSize &= 65535; // 16-bits - var key = Pair.create(path, (mode << 26) ^ (keySize << 16) ^ valueSize); - // unlocked fetch is safe: map is concurrent read capable, and only inserted into - ParcelFileDescriptor fd = sFdCache.get(key); - if (fd != null) return fd; - // ok, no cached fd present, need to grab a lock - synchronized (BpfMap.class) { - // need to redo the check - fd = sFdCache.get(key); - if (fd != null) return fd; - // okay, we really haven't opened this before... - fd = ParcelFileDescriptor.adoptFd(nativeBpfFdGet(path, mode, keySize, valueSize)); - sFdCache.put(key, fd); - return fd; - } - } - - /** - * Create a BpfMap map wrapper with "path" of filesystem. - * - * @param flag the access mode, one of BPF_F_RDWR, BPF_F_RDONLY, or BPF_F_WRONLY. - * @throws ErrnoException if the BPF map associated with {@code path} cannot be retrieved. - * @throws NullPointerException if {@code path} is null. - */ - public BpfMap(@NonNull final String path, final int flag, final Class<K> key, - final Class<V> value) throws ErrnoException, NullPointerException { - mKeyClass = key; - mValueClass = value; - mKeySize = Struct.getSize(key); - mValueSize = Struct.getSize(value); - mMapFd = cachedBpfFdGet(path, flag, mKeySize, mValueSize); - } - - /** - * Update an existing or create a new key -> value entry in an eBbpf map. - * (use insertOrReplaceEntry() if you need to know whether insert or replace happened) - */ - @Override - public void updateEntry(K key, V value) throws ErrnoException { - nativeWriteToMapEntry(mMapFd.getFd(), key.writeToBytes(), value.writeToBytes(), BPF_ANY); - } - - /** - * If the key does not exist in the map, insert key -> value entry into eBpf map. - * Otherwise IllegalStateException will be thrown. - */ - @Override - public void insertEntry(K key, V value) - throws ErrnoException, IllegalStateException { - try { - nativeWriteToMapEntry(mMapFd.getFd(), key.writeToBytes(), value.writeToBytes(), - BPF_NOEXIST); - } catch (ErrnoException e) { - if (e.errno == EEXIST) throw new IllegalStateException(key + " already exists"); - - throw e; - } - } - - /** - * If the key already exists in the map, replace its value. Otherwise NoSuchElementException - * will be thrown. - */ - @Override - public void replaceEntry(K key, V value) - throws ErrnoException, NoSuchElementException { - try { - nativeWriteToMapEntry(mMapFd.getFd(), key.writeToBytes(), value.writeToBytes(), - BPF_EXIST); - } catch (ErrnoException e) { - if (e.errno == ENOENT) throw new NoSuchElementException(key + " not found"); - - throw e; - } - } - - /** - * Update an existing or create a new key -> value entry in an eBbpf map. - * Returns true if inserted, false if replaced. - * (use updateEntry() if you don't care whether insert or replace happened) - * Note: see inline comment below if running concurrently with delete operations. - */ - @Override - public boolean insertOrReplaceEntry(K key, V value) - throws ErrnoException { - try { - nativeWriteToMapEntry(mMapFd.getFd(), key.writeToBytes(), value.writeToBytes(), - BPF_NOEXIST); - return true; /* insert succeeded */ - } catch (ErrnoException e) { - if (e.errno != EEXIST) throw e; - } - try { - nativeWriteToMapEntry(mMapFd.getFd(), key.writeToBytes(), value.writeToBytes(), - BPF_EXIST); - return false; /* replace succeeded */ - } catch (ErrnoException e) { - if (e.errno != ENOENT) throw e; - } - /* If we reach here somebody deleted after our insert attempt and before our replace: - * this implies a race happened. The kernel bpf delete interface only takes a key, - * and not the value, so we can safely pretend the replace actually succeeded and - * was immediately followed by the other thread's delete, since the delete cannot - * observe the potential change to the value. - */ - return false; /* pretend replace succeeded */ - } - - /** Remove existing key from eBpf map. Return false if map was not modified. */ - @Override - public boolean deleteEntry(K key) throws ErrnoException { - return nativeDeleteMapEntry(mMapFd.getFd(), key.writeToBytes()); - } - - /** Returns {@code true} if this map contains no elements. */ - @Override - public boolean isEmpty() throws ErrnoException { - return getFirstKey() == null; - } - - private K getNextKeyInternal(@Nullable K key) throws ErrnoException { - byte[] rawKey = new byte[mKeySize]; - - if (!nativeGetNextMapKey(mMapFd.getFd(), - key == null ? null : key.writeToBytes(), - rawKey)) return null; - - final ByteBuffer buffer = ByteBuffer.wrap(rawKey); - buffer.order(ByteOrder.nativeOrder()); - return Struct.parse(mKeyClass, buffer); - } - - /** - * Get the next key of the passed-in key. If the passed-in key is not found, return the first - * key. If the passed-in key is the last one, return null. - * - * TODO: consider allowing null passed-in key. - */ - @Override - public K getNextKey(@NonNull K key) throws ErrnoException { - Objects.requireNonNull(key); - return getNextKeyInternal(key); - } - - /** Get the first key of eBpf map. */ - @Override - public K getFirstKey() throws ErrnoException { - return getNextKeyInternal(null); - } - - /** Check whether a key exists in the map. */ - @Override - public boolean containsKey(@NonNull K key) throws ErrnoException { - Objects.requireNonNull(key); - - byte[] rawValue = new byte[mValueSize]; - return nativeFindMapEntry(mMapFd.getFd(), key.writeToBytes(), rawValue); - } - - /** Retrieve a value from the map. Return null if there is no such key. */ - @Override - public V getValue(@NonNull K key) throws ErrnoException { - Objects.requireNonNull(key); - - byte[] rawValue = new byte[mValueSize]; - if (!nativeFindMapEntry(mMapFd.getFd(), key.writeToBytes(), rawValue)) return null; - - final ByteBuffer buffer = ByteBuffer.wrap(rawValue); - buffer.order(ByteOrder.nativeOrder()); - return Struct.parse(mValueClass, buffer); - } - - /** - * Iterate through the map and handle each key -> value retrieved base on the given BiConsumer. - * The given BiConsumer may to delete the passed-in entry, but is not allowed to perform any - * other structural modifications to the map, such as adding entries or deleting other entries. - * Otherwise, iteration will result in undefined behaviour. - */ - @Override - public void forEach(ThrowingBiConsumer<K, V> action) throws ErrnoException { - @Nullable K nextKey = getFirstKey(); - - while (nextKey != null) { - @NonNull final K curKey = nextKey; - @NonNull final V value = getValue(curKey); - - nextKey = getNextKey(curKey); - action.accept(curKey, value); - } - } - - /* Empty implementation to implement AutoCloseable, so we can use BpfMaps - * with try with resources, but due to persistent FD cache, there is no actual - * need to close anything. File descriptors will actually be closed when we - * unlock the BpfMap class and destroy the ParcelFileDescriptor objects. - */ - @Override - public void close() throws IOException { - } - - /** - * Clears the map. The map may already be empty. - * - * @throws ErrnoException if the map is already closed, if an error occurred during iteration, - * or if a non-ENOENT error occurred when deleting a key. - */ - @Override - public void clear() throws ErrnoException { - K key = getFirstKey(); - while (key != null) { - deleteEntry(key); // ignores ENOENT. - key = getFirstKey(); - } - } - - private static native int nativeBpfFdGet(String path, int mode, int keySize, int valueSize) - throws ErrnoException, NullPointerException; - - // Note: the following methods appear to not require the object by virtue of taking the - // fd as an int argument, but the hidden reference to this is actually what prevents - // the object from being garbage collected (and thus potentially maps closed) prior - // to the native code actually running (with a possibly already closed fd). - - private native void nativeWriteToMapEntry(int fd, byte[] key, byte[] value, int flags) - throws ErrnoException; - - private native boolean nativeDeleteMapEntry(int fd, byte[] key) throws ErrnoException; - - // If key is found, the operation returns true and the nextKey would reference to the next - // element. If key is not found, the operation returns true and the nextKey would reference to - // the first element. If key is the last element, false is returned. - private native boolean nativeGetNextMapKey(int fd, byte[] key, byte[] nextKey) - throws ErrnoException; - - private native boolean nativeFindMapEntry(int fd, byte[] key, byte[] value) - throws ErrnoException; -} |