网络报文过滤能力在系统内核网络协议栈中拦截网络数据包,根据预设规则决定报文的放行或丢弃。典型应用场景包括:
报文过滤能力需要系统权限支持,应用需在module.json5中声明ohos.permission.kernel.TRAFFIC_FILTER权限,并在原生侧调用C API。该权限为系统权限,仅系统应用或经签名的特权应用可申请使用。
使用网络报文过滤能力的主要流程如下:
添加动态链接库
在CMakeLists.txt的target_link_libraries中添加以下共享库:
libace_napi.z.so libhilog_ndk.z.so libnet_trafficfilter.so
头文件
#include "napi/native_api.h" #include <cstdint> #include <cstring> #include <string> #include <vector> #include <map> #include <algorithm> #include <arpa/inet.h> #include "hilog/log.h" #include "network/netmanager_ext/net_trafficfilter.h"
在C++源文件中引入以下头文件:
在开始实现前,请确保已完成以下前置条件:
在源文件中编写调用该API的代码,实现报文控制器的创建。
使用OH_TrafficFilter_CreatePacketController接口创建报文控制器。其中group_id用于标识控制器的分组,priority控制规则优先级,packetCopyMode与 packetCopyLen用于配置NFQueue(Netfilter Queue,内核与用户态交互报文拷贝的队列机制)报文拷贝模式与长度,nfqueueMaxlen与nfqueueFlags用于设置队列长度与标志位。
// 报文控制器创建:解析 JS 入参并创建 NFQueue 报文控制器
constexpr int BUFFER_SIZE = 128;
constexpr int GLOBAL_NETSTACK = 0xFF00;
constexpr int IP_ADDR_BUF_LEN = 16;
constexpr int IPV4_ADDR_LEN = 4;
constexpr int MAX_STR_ARRAY_LEN = 46;
// 默认配置常量:分组 ID、优先级、NFQueue 拷贝长度/队列长度/标志位/拷贝模式
constexpr uint32_t DEFAULT_GROUP_ID = 1001;
constexpr uint32_t DEFAULT_PRIORITY = 100;
constexpr uint32_t DEFAULT_PACKET_COPY_LEN = 0xFFFF;
constexpr uint32_t DEFAULT_NFQUEUE_MAXLEN = 1024;
constexpr uint32_t DEFAULT_NFQUEUE_FLAGS = 1;
constexpr uint32_t DEFAULT_PACKET_COPY_MODE = 2;
constexpr int PORT_MIN_VALUE = 0;
constexpr int PORT_MAX_VALUE = 65535;
constexpr int MAX_PORT_MULTI_COUNT = 16;
constexpr int MAX_IP_MULTI_COUNT = 8;
constexpr int32_t ERR_CONTROLLER_NOT_FOUND = 29410101;
constexpr int DUMMY_CALLBACK_ARG = 23;
// CreatePacketControllerNapi的参数索引
constexpr int PACKET_CTRL_ARG_IDX_GROUP_ID = 0;
constexpr int PACKET_CTRL_ARG_IDX_PRIORITY = 1;
constexpr int PACKET_CTRL_ARG_IDX_PACKET_COPY_LEN = 2;
constexpr int PACKET_CTRL_ARG_IDX_NFQUEUE_MAXLEN = 3;
constexpr int PACKET_CTRL_ARG_IDX_NFQUEUE_FLAGS = 4;
constexpr int PACKET_CTRL_ARG_IDX_PACKET_COPY_MODE = 5;
// Netfilter钩子点数值映射
constexpr int HOOK_INPUT_VALUE = 0;
constexpr int HOOK_OUTPUT_VALUE = 1;
constexpr int HOOK_FORWARD_VALUE = 2;
constexpr int HOOK_PREROUTING_VALUE = 3;
constexpr int HOOK_POSTROUTING_VALUE = 4;
constexpr int ARG_IDX_JS_CALLBACK = 2;
constexpr int ARG_IDX_RULE_CONFIG = 2;
constexpr size_t MAX_PORT_STRING_LEN = 1024;
// 全局控制器映射表与自增ID
map<int, OH_TrafficFilter_PacketController*> g_controllerMap;
int g_controllerId = 1;
static const char *TAG = "[packet]";
// 线程安全函数与回调上下文
napi_threadsafe_function tsFn;
static int g_value = 0;
struct PacketCallbackCtx {
napi_env env; // N-API环境
napi_ref jsCallbackRef; // JS回调引用
const OH_TrafficFilter_PacketDesc* packet; // 报文描述指针
};
// 全局异步回调上下文
auto g_asyncContext = new PacketCallbackCtx();
static napi_value CreatePacketControllerNapi(napi_env env, napi_callback_info info)
{
// 获取JS调用参数
size_t argc = PACKET_CTRL_ARG_IDX_PACKET_COPY_MODE + 1;
napi_value args[PACKET_CTRL_ARG_IDX_PACKET_COPY_MODE + 1] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
// 使用默认值初始化参数,若JS端传入则覆盖
uint32_t groupId = DEFAULT_GROUP_ID;
uint32_t priority = DEFAULT_PRIORITY;
uint32_t packetCopyLen = DEFAULT_PACKET_COPY_LEN;
uint32_t nfqueueMaxlen = DEFAULT_NFQUEUE_MAXLEN;
uint32_t nfqueueFlags = DEFAULT_NFQUEUE_FLAGS;
uint32_t packetCopyMode = DEFAULT_PACKET_COPY_MODE;
if (argc > PACKET_CTRL_ARG_IDX_GROUP_ID) {
napi_get_value_uint32(env, args[PACKET_CTRL_ARG_IDX_GROUP_ID], &groupId);
}
if (argc > PACKET_CTRL_ARG_IDX_PRIORITY) {
napi_get_value_uint32(env, args[PACKET_CTRL_ARG_IDX_PRIORITY], &priority);
}
if (argc > PACKET_CTRL_ARG_IDX_PACKET_COPY_LEN) {
napi_get_value_uint32(env, args[PACKET_CTRL_ARG_IDX_PACKET_COPY_LEN], &packetCopyLen);
}
if (argc > PACKET_CTRL_ARG_IDX_NFQUEUE_MAXLEN) {
napi_get_value_uint32(env, args[PACKET_CTRL_ARG_IDX_NFQUEUE_MAXLEN], &nfqueueMaxlen);
}
if (argc > PACKET_CTRL_ARG_IDX_PACKET_COPY_MODE) {
napi_get_value_uint32(env, args[PACKET_CTRL_ARG_IDX_PACKET_COPY_MODE], &packetCopyMode);
}
// 填充OH_TrafficFilter_Config配置结构体
OH_TrafficFilter_Config config;
config.size = sizeof(OH_TrafficFilter_Config);
config.packetCopyLen = packetCopyLen;
config.nfqueueMaxlen = nfqueueMaxlen;
config.nfqueueFlags = nfqueueFlags;
config.packetCopyMode = packetCopyMode;
// 调用系统API创建报文控制器
OH_TrafficFilter_PacketController* controller = nullptr;
int32_t ret = OH_TrafficFilter_CreatePacketController(groupId, priority, &config, &controller);
// ...
// 将控制器存入全局映射表,便于后续按ID操作
g_controllerMap[g_controllerId] = controller;
napi_value resultObj;
napi_create_object(env, &resultObj);
napi_value retValue;
napi_create_int32(env, ret, &retValue);
napi_set_named_property(env, resultObj, "ret", retValue);
napi_value id;
napi_create_int32(env, g_controllerId, &id);
napi_set_named_property(env, resultObj, "id", id);
g_controllerId++;
return resultObj;
} 添加报文过滤规则。规则中可配置源/目的IP、端口、接口、UID、MAC、TCP标志位以及连接跟踪状态。
使用OH_TrafficFilter_AddPacketRule接口向指定控制器添加规则。OH_TrafficFilter_FilterRule支持以下匹配条件:
规则间逻辑:
// 添加报文过滤规则:按控制器ID查找控制器并添加过滤规则
static napi_value AddPacketRuleNapi(napi_env env, napi_callback_info info)
{
// 获取JS调用参数
size_t argc = ARG_IDX_RULE_CONFIG;
napi_value args[ARG_IDX_RULE_CONFIG] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
// 解析控制器ID并查找对应控制器
uint32_t id = -1;
napi_get_value_uint32(env, args[0], &id);
OH_TrafficFilter_PacketController* controller = g_controllerMap[id];
if (controller == nullptr) {
napi_value result;
napi_create_int32(env, -1, &result);
return result;
}
// 规则配置对象
napi_value configObj = args[1];
// 从配置对象中读取规则优先级
uint32_t priority = DEFAULT_PRIORITY;
bool hasProp = false;
napi_value propVal;
if (napi_has_named_property(env, configObj, "priority", &hasProp) == napi_ok && hasProp) {
napi_get_named_property(env, configObj, "priority", &propVal);
napi_get_value_uint32(env, propVal, &priority);
}
// 解析钩子点与协议类型
OH_TrafficFilter_HookPoint hookPoint = ParseHookPointFromConfig(env, configObj);
uint32_t protocol = ParseProtocolFromConfig(env, configObj);
// 根据配置构建OH_TrafficFilter_FilterRule过滤规则
OH_TrafficFilter_FilterRule rule = BuildFilterRuleFromConfig(env, configObj, priority, hookPoint, protocol);
OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_NETSTACK, TAG,
"AddPacketRuleNapi srcMac: %{public}s", rule.macMatch.srcMac);
// 调用系统API添加过滤规则
int ret = OH_TrafficFilter_AddPacketRule(controller, &rule);
OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_NETSTACK, TAG,
"AddPacketRuleNapi ret: %{public}d", ret);
napi_value result;
napi_create_int32(env, ret, &result);
return result;
} 注册报文回调,在回调中根据报文信息返回放行或丢弃决策。
使用OH_TrafficFilter_RegisterPacketCallback 接口注册回调函数。回调运行在非JS线程,如需将报文信息展示到ArkTS层,需通过napi_threadsafe_function 创建线程安全函数,在回调中调用napi_call_threadsafe_function将数据转发到ArkTS线程。
// 注册报文回调:将内核报文通过线程安全函数转发到JS层
OH_TrafficFilter_PacketDecision MyPacketHandler(
const OH_TrafficFilter_PacketDesc* packet,
void* userData)
{
// 通过线程安全函数将报文信息转发到JS线程
napi_acquire_threadsafe_function(tsFn);
g_asyncContext->packet = packet;
napi_call_threadsafe_function(tsFn, g_asyncContext, napi_tsfn_nonblocking);
napi_release_threadsafe_function(tsFn, napi_tsfn_release);
// 默认丢弃该报文
return OH_TRAFFICFILTER_DECISION_DROP;
}
static napi_value RegisterPacketCallbackNapi(napi_env env, napi_callback_info info)
{
// 注册报文回调的N-API入口
size_t argc = ARG_IDX_JS_CALLBACK + 1;
napi_value args[ARG_IDX_JS_CALLBACK + 1] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
// 解析控制器ID并查找对应控制器
uint32_t id = -1;
napi_get_value_uint32(env, args[0], &id);
OH_TrafficFilter_PacketController* controller = g_controllerMap[id];
if (controller == nullptr) {
napi_value result;
napi_create_int32(env, ERR_CONTROLLER_NOT_FOUND, &result);
return result;
}
// 读取用户自定义数据
size_t copyLen = 0;
char buf[BUFFER_SIZE] = {0};
napi_get_value_string_utf8(env, args[1], buf, BUFFER_SIZE, ©Len);
void* userData = reinterpret_cast<void*>(buf);
// 创建线程安全函数,用于将报文信息转发到JS线程
napi_value workName;
napi_create_string_utf8(env, "ThreadSafeCase", NAPI_AUTO_LENGTH, &workName);
napi_create_threadsafe_function(env, nullptr, nullptr, workName, 0, 1, nullptr, nullptr,
nullptr, ThreadSafeCallJs, &tsFn);
g_asyncContext->env = env;
napi_create_reference(env, args[ARG_IDX_JS_CALLBACK], 1, &g_asyncContext->jsCallbackRef);
// 向控制器注册报文处理回调
int ret = OH_TrafficFilter_RegisterPacketCallback(controller, MyPacketHandler, userData);
napi_value result;
napi_create_int32(env, 0, &result);
return result;
} 注销回调、清除规则并销毁报文控制器。
测试完成后,建议按注销回调 → 清除规则 → 销毁控制器的顺序释放资源,避免清理过程中仍有报文进入回调。
static napi_value UnregisterPacketCallbackNapi(napi_env env, napi_callback_info info)
{
size_t argc = 1;
napi_value args[1] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
int ret = -1;
if (argc >= 1) {
uint32_t id;
napi_get_value_uint32(env, args[0], &id);
OH_TrafficFilter_PacketController* controller = g_controllerMap[id];
if (controller != nullptr) {
ret = OH_TrafficFilter_UnregisterPacketCallback(controller);
}
}
napi_value result;
napi_create_int32(env, ret, &result);
return result;
} static napi_value ClearPacketRuleNapi(napi_env env, napi_callback_info info)
{
size_t argc = 1;
napi_value args[1] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
uint32_t id;
if (argc >= 1) {
napi_get_value_uint32(env, args[0], &id);
}
int ret = -1;
OH_TrafficFilter_PacketController* controller = g_controllerMap[id];
if (controller != nullptr) {
ret = OH_TrafficFilter_ClearPacketRule(controller);
}
napi_value result;
napi_create_int32(env, ret, &result);
return result;
} static napi_value DestroyPacketControllerNapi(napi_env env, napi_callback_info info)
{
size_t argc = 1;
napi_value args[1] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
uint32_t id;
if (argc >= 1) {
napi_get_value_uint32(env, args[0], &id);
}
int ret = -1;
OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_NETSTACK, TAG,
"DestroyPacketControllerNapi id: %{public}d", id);
OH_TrafficFilter_PacketController* controller = g_controllerMap[id];
if (controller != nullptr) {
OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_NETSTACK, TAG,
"DestroyPacketControllerNapi id111: %{public}d", id);
ret = OH_TrafficFilter_DestroyPacketController(controller);
g_controllerMap[id] = nullptr;
}
napi_value result;
napi_create_int32(env, ret, &result);
return result;
} 初始化并导出通过N-API封装的napi_value类型对象。
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
napi_property_descriptor desc[] = {
{ "createPacketController", nullptr, CreatePacketControllerNapi, nullptr, nullptr,
nullptr, napi_default, nullptr },
{ "destroyPacketController", nullptr, DestroyPacketControllerNapi, nullptr, nullptr,
nullptr, napi_default, nullptr },
{ "addPacketRule", nullptr, AddPacketRuleNapi, nullptr, nullptr,
nullptr, napi_default, nullptr },
{ "clearPacketRule", nullptr, ClearPacketRuleNapi, nullptr, nullptr,
nullptr, napi_default, nullptr },
{ "registerPacketCallback", nullptr, RegisterPacketCallbackNapi, nullptr, nullptr,
nullptr, napi_default, nullptr },
{ "unregisterPacketCallback", nullptr, UnregisterPacketCallbackNapi, nullptr, nullptr,
nullptr, napi_default, nullptr }
};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END 将初始化成功的对象通过RegisterEntryModule函数,使用napi_module_register函数将模块注册到Node.js中。
static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "entry",
.nm_priv = nullptr,
.reserved = { 0 },
};
extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
{
napi_module_register(&demoModule);
}