使用libnftnl(1.2.4)与libmnl(1.0.4)创建nftables表时mnl_cb_run返回‘无效参数’错误的排查求助
解决libnftnl/libmnl创建nftables表时mnl_cb_run返回“无效参数”的问题
我帮你排查了代码里的几个关键问题,这些问题是导致错误的核心原因,下面一步步给你修复方案:
1. 清理无用代码并修复内存泄漏
你一开始调用nfnlmsg_alloc_simple创建了struct nl_msg *msg,但后续完全没用到这个对象,也没释放它,这会造成内存泄漏。直接删掉这部分冗余代码即可。
2. 修正回调函数的消息类型验证逻辑
你的nft_error_cb回调直接假设收到的消息是NLMSG_ERROR类型,但mnl_cb_run会把所有接收到的Netlink消息传给回调。如果遇到非预期的消息类型,直接解析struct nlmsgerr会导致内存访问错误,进而触发mnl_cb_run返回EINVAL(无效参数)。
修改后的安全回调函数:
static int nft_error_cb(const struct nlmsghdr *nlh, void *data) { log_info("[DEBUG] nft_error_cb invoked, msg type: %u", nlh->nlmsg_type); // 只处理内核返回的ACK/错误消息(NLMSG_ERROR类型) if (nlh->nlmsg_type != NLMSG_ERROR) { log_info("[DEBUG] Ignoring non-error message type"); return MNL_CB_OK; // 继续处理其他消息(如果存在) } struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh); if (err->error == 0) { // 收到ACK,说明操作成功 log_info("[DEBUG] Received success ACK from kernel"); return MNL_CB_STOP; // 终止回调处理 } // 内核返回具体错误 int kernel_err = -err->error; log_info("[KERNEL ERROR] errno=%d (%s)", kernel_err, strerror(kernel_err)); return MNL_CB_ERROR; // 告知mnl_cb_run处理出错 }
3. 移除冗余的消息标志位
在nftnl_table_nlmsg_build_hdr中,你添加了NLM_F_REQUEST标志,但这个是Netlink消息的默认要求,libnftnl会自动处理,不需要手动添加。保留NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK即可。
修改后的消息头构建代码:
nlh = nftnl_table_nlmsg_build_hdr( buf, NFT_MSG_NEWTABLE, family, NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK, seq );
4. 完善错误路径的资源释放
你的代码在部分错误路径中没有关闭mnl_socket(比如nftnl_table_alloc失败后),会导致文件句柄泄漏。所有错误分支都要确保socket被关闭、表结构被释放。
完整修复后的核心函数
调整后的nftablehandler_create_table函数:
int nftablehandler_create_table(char* table_name, int family){ struct mnl_socket *sock = mnl_socket_open(NETLINK_NETFILTER); if (!sock) { log_info("Error: mnl_socket_open failed"); return -1; } if (mnl_socket_bind(sock, 0, MNL_SOCKET_AUTOPID) < 0) { log_info("Error: mnl_socket_bind failed"); mnl_socket_close(sock); return -1; } struct nftnl_table *t = nftnl_table_alloc(); if (!t) { log_info("Error: Could not allocate table structure.\n"); mnl_socket_close(sock); return -1; } nftnl_table_set_str(t, NFTNL_TABLE_NAME, table_name); nftnl_table_set_u32(t, NFTNL_TABLE_FAMILY, family); static uint32_t seq_counter = 0; uint32_t seq = ++seq_counter; uint32_t portid = mnl_socket_get_portid(sock); char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh = nftnl_table_nlmsg_build_hdr( buf, NFT_MSG_NEWTABLE, family, NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK, seq ); if (!nlh) { log_info("Error: Could not build table message header.\n"); nftnl_table_free(t); mnl_socket_close(sock); return -1; } nftnl_table_nlmsg_build_payload(nlh, t); int err = mnl_socket_sendto(sock, nlh, nlh->nlmsg_len); if (err < 0){ log_info("[ERROR][NFTABLE][TABLE CREATION FAILED - SEND][TABLE NAME: %s]", table_name); nftnl_table_free(t); mnl_socket_close(sock); return err; } err = mnl_socket_recvfrom(sock, buf, sizeof(buf)); if (err < 0){ log_info("[ERROR][NFTABLE][TABLE CREATION FAILED - RECV][TABLE NAME: %s]", table_name); nftnl_table_free(t); mnl_socket_close(sock); return err; } err = mnl_cb_run(buf, err, seq, portid, nft_error_cb, NULL); if (err < 0){ log_info("[ERROR][NFTABLE][CALLBACK FAILED / KERNEL ERROR][TABLE NAME: %s][ERROR: %s]%d", table_name, strerror(errno), errno); nftnl_table_free(t); mnl_socket_close(sock); return err; } log_info("[INFO][NFTABLE][TABLE CREATION SUCCESSFUL][TABLE NAME: %s]", table_name); nftnl_table_free(t); mnl_socket_close(sock); return 0; }
额外注意事项
- 调用
strdup("test_table")后记得手动释放内存:char* table = strdup("test_table"); int res = nftablehandler_create_table(table, family); free(table); - 可以用
nft list tables命令手动验证表是否创建成功,排除代码外的环境问题。
内容的提问来源于stack exchange,提问作者Vignesh
相关产品推荐
相关产品推荐

