В контексте выбора адреса источника 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);
}