本文介绍了标识适配器的首选IPv6源地址的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果您具有一个具有多个全局作用域地址的启用IPv6的主机,那么如何以编程方式标识bind()的首选地址?

If you have a IPv6 enabled host that has more than one global-scope address, how can you programmatically identify the preferred address for bind()?

示例地址列表:

eth0      Link encap:Ethernet  HWaddr 00:14:5e:bd:6d:da
          inet addr:10.6.28.31  Bcast:10.6.28.255  Mask:255.255.255.0
          inet6 addr: 2002:dce8:d28e:0:214:5eff:febd:6dda/64 Scope:Global
          inet6 addr: fe80::214:5eff:febd:6dda/64 Scope:Link
          inet6 addr: 2002:dce8:d28e::31/64 Scope:Global
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

在Solaris上,您可以使用接口标志来指示首选地址,并且可以通过SIOCGLIFCONF以编程方式使用它:

On Solaris you can indicate a preferred address with an interface flag and it is available programmatically via SIOCGLIFCONF:

/usr/include/net/if.h:
#define   IFF_PREFERRED   0x0400000000    /* Prefer as source address */

如界面列表中所列:

eri0: flags=2104841<UP,RUNNING,MULTICAST,DHCP,ROUTER,IPv6> mtu 1500 index 2
        inet6 fe80::203:baff:fe4e:6cc8/10
eri0:1: flags=402100841<UP,RUNNING,MULTICAST,ROUTER,IPv6,PREFERRED> mtu 1500 index 2
        inet6 2002:dce8:d28e::36/64

但这不能移植到OSX,Linux,FreeBSD或Windows.从管理员的角度来看,Windows变得毫无用处,因为它完全没有用,基于UUID的适配器名称(取决于Windows版本).

This is not portable to OSX, Linux, FreeBSD, or Windows though. Windows is let off easy though as it has completely useless, from an administrators perspective, UUID based adapter names (depending upon the Windows version).

对于Linux 本文详细介绍了参数,其中lft是生存期"的缩写,可以更改以加权内核的选择过程.不过,在SIOCGIFCONFgetifaddrs()的结果中,此设置似乎并不方便使用.

For Linux this article details how the parameter preferred_lft, where lft is short for "lifetime", can be altered to weight the selection process by the kernel. This setting doesn't appear conveniently available in the results of SIOCGIFCONF or getifaddrs() though.

所以我想绑定到eth0eri0或任何可用的接口名称.选择有些明显:

So I want to bind to eth0, eri0, or whatever available interface name. The choices are a bit stark:

  1. 解析为多个接口的适配器名称失败.我采用这种方法来处理多播传输( OpenPGM ),因为协议必须具有唯一发送功能地址.
  2. 绑定所有内容.这是一个解决方案,对用户来说是意外的.
  3. 使用SO_BINDTODEVICE绑定到适配器.这需要Linux上的CAP_NET_RAW系统功能,这对于管理员来说可能是相当麻烦的开销.
  4. 绑定到适配器上的第一个IPv6接口.排序往往是完全伪造的.
  5. 绑定到最后一个界面. David Croft 的文章暗示Linux可以做到这一点,但也是有点假.
  6. 枚举每个接口,并为每个接口显式创建一个新的套接字.
  1. Fail on adapter names resolving to multiple interfaces. I take this approach for handling multicast transports (OpenPGM) as the protocol MUST have one-only sending address.
  2. Bind to everything. This is a cop out and would be unexpected to users.
  3. Bind to the adapter with SO_BINDTODEVICE. This requires CAP_NET_RAW system capability on Linux which can be quite a cumbersome overhead for administrators.
  4. Bind to the first IPv6 interface on the adapter. The ordering tends to be completely bogus.
  5. Bind to the last interface. David Croft's article implies Linux does this, but is also a bit bogus.
  6. Enumerate over every interface and create a new socket explicitly for each.

使用选项#6,我希望您通常会更聪明,并采用以下方法:如果仅链接本地作用域地址可用,则将其绑定到该地址,否则仅绑定到可用的全局链接作用域地址.

With option #6 I would expect you could usually be smarter and take the approach that if only a link-local scope address is available bind to that, otherwise bind to just the available global-link scope addresses.

连接到另一台主机时,可以使用 RFC 3484 ,但是您会看到所有选择都取决于匹配的目标地址:

When connecting to another host then RFC 3484 can be used, but as you can see all the choices are dependent upon matching the destination address:

  1. 首选相同的地址. (即目的地是本地计算机)
  2. 请选择适当的范围. (即与目标共享的最小范围)
  3. 避免使用不建议使用的地址.
  4. 首选家庭住址.偏好传出界面. (即,在我们要发送的界面上选择一个地址出)
  5. 首选匹配标签.
  6. 首选公共地址.
  7. 使用最长匹配前缀.
  1. Prefer same address. (i.e. destination is local machine)
  2. Prefer appropriate scope. (i.e. smallest scope shared with the destination)
  3. Avoid deprecated addresses.
  4. Prefer home addresses. Prefer outgoinginterface. (i.e. prefer an address on the interface we're sendingout of)
  5. Prefer matching label.
  6. Prefer public addresses.
  7. Use longest matching prefix.

在某些情况下,我们可以在此处使用#7,但是在上面的接口示例中,两个全局作用域的接口都具有64位前缀长度.

In some circumstances we can use #7 here, but in the interface example above both global-scope interfaces have a 64-bit prefix length.

RFC 3484 具有以下相关行:

指向 RFC 2462 的链接,同样进行了扩展:

The link being to RFC 2462, similarly expanded:

但是没有方法以编程方式获取此细节.

But no methods to programmatically acquire this detail.

支持公开ioctl的Win32 API的道具 SIO_ADDRESS_LIST_SORT ,使开发人员不仅可以使用RFC 3484排序,还可以考虑任何系统管理员的替代. Linux在getaddrinfo()中具有用于RFC 3484排序的/etc/gai.conf,但是没有可直接访问该排序的API. Solaris具有ipaddrsel命令. OSX通过在10.7中添加ip6addrctl来遵循FreeBSD.

Props to Win32 API that exposes an ioctl SIO_ADDRESS_LIST_SORT that allows a developer to use not only RFC 3484 sorting but to take into consideration any system administrator overrides. Linux has /etc/gai.conf as used for RFC 3484 sorting in getaddrinfo() but no API for directly accessing the sorting. Solaris has the ipaddrsel command. OSX is following FreeBSD by adding ip6addrctl in 10.7.

编辑:此附加的IETF草案文档中列出并提到了RFC 3484排序的一些问题:

edit: Some concerns with RFC 3484 sorting are listed and referred to in this additional IETF draft document:

http://tools.ietf.org/html/draft- axu-addr-sel-01

作者喜欢Solaris为每个其他IPv6接口赋予新别名的方法,这样eri0将成为链接本地作用域地址,并且必须指定eri0:1eri0:2等以使用全局别名. -作用域地址.

The author likes Solaris's approach of giving each additional IPv6 interface a new alias, so that eri0 would become the link-local scope address, and eri0:1 or eri0:2, etc, must be specified to use a global-scope address.

很明显,虽然一个好主意在很长一段时间内都不会看到其他操作系统的变化.

Clearly whilst a nice idea one couldn't expect to see other OS change for quite some time.

推荐答案

我不确定这是否符合您的要求,但是...

I'm not sure this is in the direction you're seeking, but...

在linux下查看iproute bundle的ip代码(ip/ipaddress.c)显示,ip命令从成员ifa_flagsstruct ifaddrmsg中挖掘出primarysecondary之类的接口标志. ifaddmsg似乎是通过man 7 netlink中记录的struct nlmsghdr获取的,并通过sendmsgrecvmsg与内核的交互来使用,总体上听起来像是皇室之痛,但至少是程序性的.小学和中学是否足够有用是一个单独的问题.

Poking around in the iproute bundle's ip code (ip/ipaddress.c) under linux shows that the ip command digs up interface flags like primary and secondary from a struct ifaddrmsg, member ifa_flags. The ifaddmsg seems to be acquired through a struct nlmsghdr which is documented in man 7 netlink, and used via sendmsg and recvmsg interaction with the kernel, which overall sounds like a royal pain but it's at least programmatic. Whether primary and secondary would be enough to be useful is a separate question.

这篇关于标识适配器的首选IPv6源地址的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

06-30 12:36