Выбор исходного IPv6-адреса в Linux, когда интерфейс имеет> 1 IPv6-адреса: как его использовать? - PullRequest
0 голосов
/ 27 апреля 2018

В контексте выбора адреса источника IPv6 для исходящего трафика в Linux:

У меня есть несколько адресов IPv6 на интерфейсе. Я хочу, чтобы ядро ​​выбрало один из них в качестве исходного IPv6-адреса. Я не хочу, чтобы ядро ​​выбрало этот адрес, который я собираюсь отправить в качестве адреса источника для исходящих пакетов.

Более конкретно, в этом фрагменте я хотел бы, чтобы ядро ​​выбрало любой другой IPv6-адрес уже на этом интерфейсе, если dontUseAsSourceAddressForOutgoingPkts имеет значение true. Какие флаги дадут этот эффект? Если я использую неправильную структуру 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);
}

Ответы [ 2 ]

0 голосов
/ 01 мая 2018

RFC 3484 состояния:

  1. Выбор адреса источника

    <...>

    Правило 3: избегайте устаревших адресов. Адреса SA и SB имеют одинаковую область. Если один из двух адреса источника являются «предпочтительными», а один из них «устаревшим» (в смысл RFC 2462), затем выберите тот, который является «предпочтительным».

    <...>

Справочные страницы rtnetlink (7) кратко упоминают структуру с именем ifa_cacheinfo.

Эта структура содержит два флага значимого импорта: ifa_valid и ifa_prefered. Чтобы пометить адрес IPv6 как устаревший, задайте для его предпочтительного значения 0 значение. Кроме того, по-видимому, также обычно для valid_lft задается значение 0xffffffff (навсегда), чтобы подчеркнуть явно устаревшую природу этого адреса IPv6.

/* 
 * 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>
0 голосов
/ 27 апреля 2018

rtnetlink(7) справочные страницы просто сказать:

ifa_flags - это слово флага IFA_F_SECONDARY для вторичного адреса (старый интерфейс псевдонимов), IFA_F_PERMANENT для постоянного адреса, установленного пользователем, и других недокументированных флагов.

Действительно, исходники ядра , похоже, не документируют их:

/* ifa_flags */
#define IFA_F_SECONDARY      0x01
#define IFA_F_TEMPORARY      IFA_F_SECONDARY

#define IFA_F_NODAD          0x02
#define IFA_F_OPTIMISTIC     0x04
#define IFA_F_DADFAILED      0x08
#define IFA_F_HOMEADDRESS    0x10
#define IFA_F_DEPRECATED     0x20
#define IFA_F_TENTATIVE      0x40
#define IFA_F_PERMANENT      0x80
#define IFA_F_MANAGETEMPADDR 0x100
#define IFA_F_NOPREFIXROUTE  0x200
#define IFA_F_MCAUTOJOIN     0x400
#define IFA_F_STABLE_PRIVACY 0x800

Однако RFC 3549 "Linux Netlink как протокол IP-сервисов" разъясняет немного больше:

   Flags: 8 bits
   IFA_F_SECONDARY  For secondary address (alias interface).
   IFA_F_PERMANENT  For a permanent address set by the user.
                    When this is not set, it means the address
                    was dynamically created (e.g., by stateless
                    autoconfiguration).
   IFA_F_DEPRECATED Defines deprecated (IPV4) address.
   IFA_F_TENTATIVE  Defines tentative (IPV4) address (duplicate
                    address detection is still in progress).

Таким образом, кажется, что эти два флага не связаны: один помечает адрес интерфейса как вторичный (временный); в то время как другой определяет адрес IPv4 («устарел»).

Если вам необходимо точно понять, каковы последствия каждого флага, вы можете взглянуть на ссылки на символ в исходном коде, например, по IFA_F_SECONDARY и * 1029. *.

...