Reputation: 91
In the context of IPv6 source address selection for outgoing traffic in Linux:
I have some IPv6 address(es) on the interface. I want the kernel to pick one of those as the source IPv6 addr. I don't want the kernel to pick this address I'm about to send it as the source address for outgoing packets.
More concretely, in this snippet I would like for the kernel to select any other IPv6 address already on this interface when dontUseAsSourceAddressForOutgoingPkts is true. What flags will yield that effect? If I'm using the wrong ifaddrmsg struct for IPv6 addressing, which one should I be using?
Snippet containing further context:
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);
}
Upvotes: 2
Views: 2550
Reputation: 91
RFC 3484 states:
Source Address Selection
<...>
Rule 3: Avoid deprecated addresses. The addresses SA and SB have the same scope. If one of the two source addresses is "preferred" and one of them is "deprecated" (in the RFC 2462 sense), then prefer the one that is "preferred."
<...>
The rtnetlink(7) man pages briefly mention a struct called ifa_cacheinfo.
This struct contains two flags of notable import: ifa_valid and ifa_prefered. In order to mark an IPv6 address as Deprecated, set its prefered_lft to zero. Additionally, it seems customary to also set valid_lft to 0xffffffff (forever) to emphasize the explicitly deprecated nature of this IPv6 address.
/*
* 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>
Upvotes: 2
Reputation: 26066
The rtnetlink(7)
man pages just say:
ifa_flags
is a flag word ofIFA_F_SECONDARY
for secondary address (old alias interface),IFA_F_PERMANENT
for a permanent address set by the user and other undocumented flags.
Indeed, the kernel sources do not seem to document them:
/* 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
However, the RFC 3549 "Linux Netlink as an IP Services Protocol" clarifies a bit more:
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).
So it seems the two flags are not related: one marks an interface address as being secondary (temporary); while the other defines a IPv4 address ("deprecated").
If you need to see exactly what are the implications of each flag, you can take a look at references for the symbol in the source code, for instance at IFA_F_SECONDARY
and IFA_F_DEPRECATED
.
Upvotes: 0