Move TrafficController relevant files from netd to mainline module
git diff packages/modules/Connectivity/service/native/TrafficController.cpp system/netd/server/TrafficController.cpp
git diff packages/modules/Connectivity/service/native/TrafficControllerTest.cpp system/netd/server/TrafficControllerTest.cpp
git diff packages/modules/Connectivity/service/native/include/TrafficController.h system/netd/server/TrafficController.h
Test: m
No-Typo-Check: clean move
BYPASS_INCLUSIVE_LANGUAGE_REASON= clean move
Change-Id: I7c0200e28e5e4459477d370912277fa1281b786b
diff --git a/service/native/include/TrafficController.h b/service/native/include/TrafficController.h
new file mode 100644
index 0000000..2f38f6c
--- /dev/null
+++ b/service/native/include/TrafficController.h
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef NETD_SERVER_TRAFFIC_CONTROLLER_H
+#define NETD_SERVER_TRAFFIC_CONTROLLER_H
+
+#include <linux/bpf.h>
+
+#include "Network.h"
+#include "android-base/thread_annotations.h"
+#include "android-base/unique_fd.h"
+#include "bpf/BpfMap.h"
+#include "bpf_shared.h"
+#include "netdutils/DumpWriter.h"
+#include "netdutils/NetlinkListener.h"
+#include "netdutils/StatusOr.h"
+#include "utils/String16.h"
+
+namespace android {
+namespace net {
+
+class TrafficController {
+ public:
+ TrafficController();
+ /*
+ * Initialize the whole controller
+ */
+ netdutils::Status start();
+ /*
+ * Tag the socket with the specified tag and uid. In the qtaguid module, the
+ * first tag request that grab the spinlock of rb_tree can update the tag
+ * information first and other request need to wait until it finish. All the
+ * tag request will be addressed in the order of they obtaining the spinlock.
+ * In the eBPF implementation, the kernel will try to update the eBPF map
+ * entry with the tag request. And the hashmap update process is protected by
+ * the spinlock initialized with the map. So the behavior of two modules
+ * should be the same. No additional lock needed.
+ */
+ int tagSocket(int sockFd, uint32_t tag, uid_t uid, uid_t callingUid) EXCLUDES(mMutex);
+
+ /*
+ * Similar as tagSocket, but skip UPDATE_DEVICE_STATS permission check.
+ */
+ int privilegedTagSocket(int sockFd, uint32_t tag, uid_t uid) EXCLUDES(mMutex);
+
+ /*
+ * The untag process is similiar to tag socket and both old qtaguid module and
+ * new eBPF module have spinlock inside the kernel for concurrent update. No
+ * external lock is required.
+ */
+ int untagSocket(int sockFd);
+
+ /*
+ * Similiar as above, no external lock required.
+ */
+ int setCounterSet(int counterSetNum, uid_t uid, uid_t callingUid) EXCLUDES(mMutex);
+
+ /*
+ * When deleting a tag data, the qtaguid module will grab the spinlock of each
+ * related rb_tree one by one and delete the tag information, counterSet
+ * information, iface stats information and uid stats information one by one.
+ * The new eBPF implementation is done similiarly by removing the entry on
+ * each map one by one. And deleting processes are also protected by the
+ * spinlock of the map. So no additional lock is required.
+ */
+ int deleteTagData(uint32_t tag, uid_t uid, uid_t callingUid) EXCLUDES(mMutex);
+
+ /*
+ * Swap the stats map config from current active stats map to the idle one.
+ */
+ netdutils::Status swapActiveStatsMap() EXCLUDES(mMutex);
+
+ /*
+ * Add the interface name and index pair into the eBPF map.
+ */
+ int addInterface(const char* name, uint32_t ifaceIndex);
+
+ int changeUidOwnerRule(ChildChain chain, const uid_t uid, FirewallRule rule, FirewallType type);
+
+ int removeUidOwnerRule(const uid_t uid);
+
+ int replaceUidOwnerMap(const std::string& name, bool isAllowlist,
+ const std::vector<int32_t>& uids);
+
+ enum IptOp { IptOpInsert, IptOpDelete };
+
+ netdutils::Status updateOwnerMapEntry(UidOwnerMatchType match, uid_t uid, FirewallRule rule,
+ FirewallType type) EXCLUDES(mMutex);
+
+ void dump(netdutils::DumpWriter& dw, bool verbose) EXCLUDES(mMutex);
+
+ netdutils::Status replaceRulesInMap(UidOwnerMatchType match, const std::vector<int32_t>& uids)
+ EXCLUDES(mMutex);
+
+ netdutils::Status addUidInterfaceRules(const int ifIndex, const std::vector<int32_t>& uids)
+ EXCLUDES(mMutex);
+ netdutils::Status removeUidInterfaceRules(const std::vector<int32_t>& uids) EXCLUDES(mMutex);
+
+ netdutils::Status updateUidOwnerMap(const std::vector<uint32_t>& appStrUids,
+ UidOwnerMatchType matchType, IptOp op) EXCLUDES(mMutex);
+ static const String16 DUMP_KEYWORD;
+
+ int toggleUidOwnerMap(ChildChain chain, bool enable) EXCLUDES(mMutex);
+
+ static netdutils::StatusOr<std::unique_ptr<netdutils::NetlinkListenerInterface>>
+ makeSkDestroyListener();
+
+ void setPermissionForUids(int permission, const std::vector<uid_t>& uids) EXCLUDES(mMutex);
+
+ FirewallType getFirewallType(ChildChain);
+
+ static const char* LOCAL_DOZABLE;
+ static const char* LOCAL_STANDBY;
+ static const char* LOCAL_POWERSAVE;
+ static const char* LOCAL_RESTRICTED;
+
+ private:
+ /*
+ * mCookieTagMap: Store the corresponding tag and uid for a specific socket.
+ * DO NOT hold any locks when modifying this map, otherwise when the untag
+ * operation is waiting for a lock hold by other process and there are more
+ * sockets being closed than can fit in the socket buffer of the netlink socket
+ * that receives them, then the kernel will drop some of these sockets and we
+ * won't delete their tags.
+ * Map Key: uint64_t socket cookie
+ * Map Value: UidTagValue, contains a uint32 uid and a uint32 tag.
+ */
+ bpf::BpfMap<uint64_t, UidTagValue> mCookieTagMap GUARDED_BY(mMutex);
+
+ /*
+ * mUidCounterSetMap: Store the counterSet of a specific uid.
+ * Map Key: uint32 uid.
+ * Map Value: uint32 counterSet specifies if the traffic is a background
+ * or foreground traffic.
+ */
+ bpf::BpfMap<uint32_t, uint8_t> mUidCounterSetMap GUARDED_BY(mMutex);
+
+ /*
+ * mAppUidStatsMap: Store the total traffic stats for a uid regardless of
+ * tag, counterSet and iface. The stats is used by TrafficStats.getUidStats
+ * API to return persistent stats for a specific uid since device boot.
+ */
+ bpf::BpfMap<uint32_t, StatsValue> mAppUidStatsMap;
+
+ /*
+ * mStatsMapA/mStatsMapB: Store the traffic statistics for a specific
+ * combination of uid, tag, iface and counterSet. These two maps contain
+ * both tagged and untagged traffic.
+ * Map Key: StatsKey contains the uid, tag, counterSet and ifaceIndex
+ * information.
+ * Map Value: Stats, contains packet count and byte count of each
+ * transport protocol on egress and ingress direction.
+ */
+ bpf::BpfMap<StatsKey, StatsValue> mStatsMapA GUARDED_BY(mMutex);
+
+ bpf::BpfMap<StatsKey, StatsValue> mStatsMapB GUARDED_BY(mMutex);
+
+ /*
+ * mIfaceIndexNameMap: Store the index name pair of each interface show up
+ * on the device since boot. The interface index is used by the eBPF program
+ * to correctly match the iface name when receiving a packet.
+ */
+ bpf::BpfMap<uint32_t, IfaceValue> mIfaceIndexNameMap;
+
+ /*
+ * mIfaceStataMap: Store per iface traffic stats gathered from xt_bpf
+ * filter.
+ */
+ bpf::BpfMap<uint32_t, StatsValue> mIfaceStatsMap;
+
+ /*
+ * mConfigurationMap: Store the current network policy about uid filtering
+ * and the current stats map in use. There are two configuration entries in
+ * the map right now:
+ * - Entry with UID_RULES_CONFIGURATION_KEY:
+ * Store the configuration for the current uid rules. It indicates the device
+ * is in doze/powersave/standby/restricted mode.
+ * - Entry with CURRENT_STATS_MAP_CONFIGURATION_KEY:
+ * Stores the current live stats map that kernel program is writing to.
+ * Userspace can do scraping and cleaning job on the other one depending on the
+ * current configs.
+ */
+ bpf::BpfMap<uint32_t, uint8_t> mConfigurationMap GUARDED_BY(mMutex);
+
+ /*
+ * mUidOwnerMap: Store uids that are used for bandwidth control uid match.
+ */
+ bpf::BpfMap<uint32_t, UidOwnerValue> mUidOwnerMap GUARDED_BY(mMutex);
+
+ /*
+ * mUidOwnerMap: Store uids that are used for INTERNET permission check.
+ */
+ bpf::BpfMap<uint32_t, uint8_t> mUidPermissionMap GUARDED_BY(mMutex);
+
+ std::unique_ptr<netdutils::NetlinkListenerInterface> mSkDestroyListener;
+
+ netdutils::Status removeRule(uint32_t uid, UidOwnerMatchType match) REQUIRES(mMutex);
+
+ netdutils::Status addRule(uint32_t uid, UidOwnerMatchType match, uint32_t iif = 0)
+ REQUIRES(mMutex);
+
+ // mMutex guards all accesses to mConfigurationMap, mUidOwnerMap, mUidPermissionMap,
+ // mStatsMapA, mStatsMapB and mPrivilegedUser. It is designed to solve the following
+ // problems:
+ // 1. Prevent concurrent access and modification to mConfigurationMap, mUidOwnerMap,
+ // mUidPermissionMap, and mPrivilegedUser. These data members are controlled by netd but can
+ // be modified from different threads. TrafficController provides several APIs directly
+ // called by the binder RPC, and different binder threads can concurrently access these data
+ // members mentioned above. Some of the data members such as mUidPermissionMap and
+ // mPrivilegedUsers are also accessed from a different thread when tagging sockets or
+ // setting the counterSet through FwmarkServer
+ // 2. Coordinate the deletion of uid stats in mStatsMapA and mStatsMapB. The system server
+ // always call into netd to ask for a live stats map change before it pull and clean up the
+ // stats from the inactive map. The mMutex will block netd from accessing the stats map when
+ // the mConfigurationMap is updating the current stats map so netd will not accidentally
+ // read the map that system_server is cleaning up.
+ std::mutex mMutex;
+
+ // The limit on the number of stats entries a uid can have in the per uid stats map.
+ // TrafficController will block that specific uid from tagging new sockets after the limit is
+ // reached.
+ const uint32_t mPerUidStatsEntriesLimit;
+
+ // The limit on the total number of stats entries in the per uid stats map. TrafficController
+ // will block all tagging requests after the limit is reached.
+ const uint32_t mTotalUidStatsEntriesLimit;
+
+ netdutils::Status loadAndAttachProgram(bpf_attach_type type, const char* path, const char* name,
+ base::unique_fd& cg_fd);
+
+ netdutils::Status initMaps() EXCLUDES(mMutex);
+
+ // Keep track of uids that have permission UPDATE_DEVICE_STATS so we don't
+ // need to call back to system server for permission check.
+ std::set<uid_t> mPrivilegedUser GUARDED_BY(mMutex);
+
+ bool hasUpdateDeviceStatsPermission(uid_t uid) REQUIRES(mMutex);
+
+ int privilegedTagSocketLocked(int sockFd, uint32_t tag, uid_t uid) REQUIRES(mMutex);
+
+ // For testing
+ TrafficController(uint32_t perUidLimit, uint32_t totalLimit);
+
+ // For testing
+ friend class TrafficControllerTest;
+};
+
+} // namespace net
+} // namespace android
+
+#endif // NETD_SERVER_TRAFFIC_CONTROLLER_H