You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux环境下如何使用C/C++代码为IPv6添加邻居表条目

IPv6邻居表条目添加实现方案

IPv6场景下没有ARP协议,对应的邻居发现功能由NDP(邻居发现协议)实现,内核没有提供类似struct arpreq的专用ioctl结构体,你需要通过RTNETLINK(路由netlink套接字) 发送RTM_NEWNEIGH请求实现静态邻居条目添加,具体实现如下:

核心实现步骤

  • 创建AF_NETLINK域、NETLINK_ROUTE协议的套接字
  • 构造RTM_NEWNEIGH类型的netlink消息,配置为永久静态邻居条目(对应IPv4实现里的ATF_PERM属性)
  • 填充三个核心信息:目标IPv6地址、对应MAC地址、网卡接口索引
  • 发送请求到内核,校验操作结果

代码示例

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/neighbour.h>
#include <net/if.h>
#include <arpa/inet.h>

void assocNd6(const std::string& ipv6Addr, const uint8_t* macAddress, const char_t interface[])
{
    int sockfd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
    if (sockfd < 0) {
        std::cout << "\nassocNd6: 创建NETLINK_ROUTE套接字失败\n";
        return;
    }

    // 构造netlink消息缓冲区
    char buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct ndmsg)) + 
             NLMSG_ALIGN(sizeof(struct rtattr)) + 16 + // NDA_DST: IPv6地址占16字节
             NLMSG_ALIGN(sizeof(struct rtattr)) + 6];  // NDA_LLADDR: MAC地址占6字节
    memset(buf, 0, sizeof(buf));

    struct nlmsghdr* nlh = (struct nlmsghdr*)buf;
    nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
    nlh->nlmsg_type = RTM_NEWNEIGH;
    // 标志位:请求+不存在则创建+存在则覆盖
    nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK;
    nlh->nlmsg_seq = 1;
    nlh->nlmsg_pid = getpid();

    // 填充邻居消息结构体
    struct ndmsg* ndm = (struct ndmsg*)NLMSG_DATA(nlh);
    ndm->ndm_family = AF_INET6;
    ndm->ndm_state = NUD_PERMANENT; // 永久静态条目,不会过期
    ndm->ndm_ifindex = if_nametoindex(interface); // 网卡名转接口索引
    if (ndm->ndm_ifindex == 0) {
        std::cout << "\nassocNd6: 无效的网卡接口名\n";
        close(sockfd);
        return;
    }

    // 添加属性:目标IPv6地址
    struct rtattr* rta = (struct rtattr*)((char*)ndm + NLMSG_ALIGN(sizeof(struct ndmsg)));
    rta->rta_type = NDA_DST;
    rta->rta_len = RTA_LENGTH(16);
    if (inet_pton(AF_INET6, ipv6Addr.c_str(), RTA_DATA(rta)) != 1) {
        std::cout << "\nassocNd6: 无效的IPv6地址格式\n";
        close(sockfd);
        return;
    }
    nlh->nlmsg_len += NLMSG_ALIGN(rta->rta_len);

    // 添加属性:链路层MAC地址
    rta = (struct rtattr*)((char*)rta + NLMSG_ALIGN(rta->rta_len));
    rta->rta_type = NDA_LLADDR;
    rta->rta_len = RTA_LENGTH(6);
    memcpy(RTA_DATA(rta), macAddress, 6);
    nlh->nlmsg_len += NLMSG_ALIGN(rta->rta_len);

    // 构造发送地址
    struct sockaddr_nl sa = {0};
    sa.nl_family = AF_NETLINK;

    struct iovec iov = {buf, nlh->nlmsg_len};
    struct msghdr msg = {&sa, sizeof(sa), &iov, 1, NULL, 0, 0};

    // 发送请求
    if (sendmsg(sockfd, &msg, 0) < 0) {
        std::cout << "\nassocNd6: 发送netlink请求失败\n";
        close(sockfd);
        return;
    }

    // 接收内核应答确认操作结果
    char recvBuf[4096];
    iov.iov_base = recvBuf;
    iov.iov_len = sizeof(recvBuf);
    ssize_t len = recvmsg(sockfd, &msg, 0);
    if (len < 0) {
        std::cout << "\nassocNd6: 接收内核应答失败\n";
        close(sockfd);
        return;
    }

    for (nlh = (struct nlmsghdr*)recvBuf; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) {
        if (nlh->nlmsg_type == NLMSG_ERROR) {
            struct nlmsgerr* err = (struct nlmsgerr*)NLMSG_DATA(nlh);
            if (err->error != 0) {
                std::cout << "\nassocNd6: 添加IPv6邻居条目失败,错误码:" << -err->error << "\n";
            }
        }
    }

    close(sockfd);
}

注意事项

  • 该实现需要进程拥有CAP_NET_ADMIN权限才能操作邻居表
  • 如果需要添加非永久的临时条目,可以修改ndm->ndm_state为NUD_REACHABLE等其他状态值

内容的提问来源于stack exchange,提问作者Kuldeep Singh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 08:06:04