c - 当接口(interface)有 >1 个 IPv6 地址时 Linux IPv6 源地址选择 : How to deprecate one?

标签 c linux linux-kernel netlink source-address

在 Linux 中为传出流量选择 IPv6 源地址的上下文中:

我的接口(interface)上有一些 IPv6 地址。 我希望内核选择其中之一作为源 IPv6 地址。 我不希望内核选择我将要发送的这个地址作为传出数据包的源地址。

更具体地说,在这段代码中,当 dontUseAsSourceAddressForOutgoingPkts 为 true 时,我希望内核选择此接口(interface)上已有的任何其他 IPv6 地址。 什么标志会产生这种效果? 如果我使用错误的 ifaddrmsg 结构进行 IPv6 寻址,我应该使用哪一个?

包含更多上下文的片段:

int
NetLnkSock::IpAdd(const std::string &ifname,
                  const IpAddr &ipaddr,
                  int prefixlen,
                  bool dontUseAsSourceAddressForOutgoingPkts)
    ifreq ifr;
    nlmsghdr *nlh;
    ifaddrmsg *ifa;
    nlmsgerr *nlerr;
    static uint32_t msg_seq = 0;
    NlSock nlsock;
    LogDev::Ostream logostr;

    nlsock.bind();
    memset(&ifr, 0, sizeof(ifr));

    if (ifname.size() > IFNAMSIZ)
        throw NetLnkNameErr();

    copy(ifname.begin(), ifname.end(), ifr.ifr_name);
    ifr.ifr_name[ifname.end() - ifname.begin()] = '\0';

    nlh = (nlmsghdr *)rcvbuf;

    nlh->nlmsg_len = sizeof(nlmsghdr);

    nlh->nlmsg_type = RTM_NEWADDR;
    nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;

    nlh->nlmsg_seq = ++msg_seq;
    nlh->nlmsg_pid = 0;

    ifa = (ifaddrmsg *)&nlh[1];
    ifa->ifa_family = (ipaddr.is_v4()) ? AF_INET : AF_INET6;
    ifa->ifa_prefixlen = prefixlen;
    /*
     * My question is about the behavior of the kernel
     * vis a vis source address selection for outgoing traffic
     * where there are multiple IP's on this interface.
     * How do the flags below impact the kernel's choice
     * for source address selection?
     */
    ifa->ifa_flags = 
    (dontUseAsSourceAddressForOutgoingPkts && ipaddr.is_v6()) ?
        (IFA_F_SECONDARY | IFA_F_DEPRECATED) : 0;
    /*
     * I would like for the kernel to select any other IPv6
     * address already on this interface when
     * dontUseAsSourceAddressForOutgoingPkts is true.
     * Will these flags yield that effect?
     */
    ifa->ifa_scope = RT_SCOPE_UNIVERSE;
    ifa->ifa_index = ifr.ifr_ifindex;
    nlh->nlmsg_len += sizeof(ifaddrmsg);
    if (ipaddr.is_v4()) {
        IpAddr ip4_bcast;
        char *buf = rcvbuf + nlh->nlmsg_len;

        ip4_bcast.create_netmask(prefixlen, ipaddr);
        ip4_bcast.from_v4(~ip4_bcast.get_v4() | ipaddr.get_v4());

        nlh->nlmsg_len += NLMSG_ALIGN(setRtAttr(buf, IFA_LOCAL,
                                  &ipaddr.get_v4(), sizeof(in_addr_t)));

        /*
         * Always send the netmask and broadcast even on delete.
         * Linux seems to ignore the prefixlen set in the original
         * message and simply matches by ip address on deletes.
         */
        buf = rcvbuf + nlh->nlmsg_len;
        nlh->nlmsg_len += NLMSG_ALIGN(setRtAttr(buf, IFA_ADDRESS,
                                  &ipaddr.get_v4(), sizeof(in_addr_t)));

        buf = rcvbuf + nlh->nlmsg_len;
        nlh->nlmsg_len += NLMSG_ALIGN(setRtAttr(buf, IFA_BROADCAST,
                                  &ip4_bcast.get_v4(), sizeof(in_addr_t)));


    } else { /* AF_INET6 */
        char *buf = rcvbuf + nlh->nlmsg_len;

        buf = rcvbuf + nlh->nlmsg_len;
        if (ipaddr.domain() != RD_DEFAULT_ID) {       // Hal doesn't support route domains
            throw NetLnkIpAddrErr();
        }
        nlh->nlmsg_len += NLMSG_ALIGN(setRtAttr(buf, IFA_LOCAL,
                                      &ipaddr.get_v6(), sizeof(in6_addr)));
        buf = rcvbuf + nlh->nlmsg_len;
        nlh->nlmsg_len += NLMSG_ALIGN(setRtAttr(buf, IFA_ADDRESS,
                                      &ipaddr.get_v6(), sizeof(in6_addr)));

    }
    nlsock.sendNlReq(rcvbuf);
}

最佳答案

RFC 3484状态:

  • 源地址选择

    <...>

    规则 3:避免使用已弃用的地址。 地址 SA 和 SB 具有相同的范围。如果两者之一 源地址是“首选”,其中之一是“已弃用”(在 RFC 2462 含义),然后选择“首选”的那个。

    <...>

  • rtnetlink(7) man pages简单提一下一个名为 ifa_cacheinfo 的结构。

    This struct包含两个值得注意的重要标志:ifa_valid 和 ifa_prefered。 为了将 IPv6 地址标记为“已弃用”,请将其 Preferred_lft 设置为零。此外,似乎习惯上还将 valid_lft 设置为 0xffffffff(永远),以强调此 IPv6 地址的明确弃用性质。 p>

    /* 
     * You have just put a new IPv6 address on the kernel with
     * net link. You don't want it chosen as the source address
     * of packets leaving this interface if there's at least one
     * other IPv6 address already on this interface.
     *
     * Mark this IPv6 address as Deprecated on this interface,
     * Causing LINUX not to choose it for source address of
     * packets outgoing from this interface when there exists
     * another, non-deprecated IPv6 address on this interface
     */
    struct ifa_cacheinfo ci;
    // This address is valid forever
    ci.ifa_valid = 0xffffffff;
    // A prefered ttl of 0 immediately deprecates this IPv6
    ci.ifa_preferred = 0;
    // <Send this cacheinfo to the kernel using net link>
    

    关于c - 当接口(interface)有 >1 个 IPv6 地址时 Linux IPv6 源地址选择 : How to deprecate one?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50051836/

    相关文章:

    编译旧的c代码

    xml - Shell 脚本 - 将 xml 拆分为多个文件

    c - 指向共享内存中数组的共享指针,指针似乎不共享?

    linux - 在 Linux 中使用 find 命令尝试匹配文件名时忽略大小写

    c - 在 VMX 中启用 EPT 会因访客状态而导致登录失败

    c++ - 我在探查器输出中看到的 __nss_passwd_lookup() 调用是关于什么的?

    c - C语言中如何正确使用指针?

    c - BST 空指针赋值错误

    C 程序正在执行一个分支,即使它不应该

    c - 通过/proc/$PID/smaps 在 Linux 内核中进行脏页统计