如何使用Retrofit作为baseUrl的IPv6链路本地地址



我正在使用NsdManager解析本地网络(物联网设备)上的服务。resolveService返回给我一个带有主机和端口的NsdServiceInfo。主机是一个Inet6Address对象,具有链路本地地址(fe80::xxxx:xxxx:xxxx:9718),没有设置范围。它还为isLinkLocalAddress返回一个true。如何使用这个没有作用域的链路本地地址进行任何网络调用

?我可以用本地机器ping服务的唯一方法是指定一个网络接口,例如:ping6 fe80::xxxx:xxxx:xxxx:9718%en0.

感觉我在这里错过了一些重要的Android API或IPv6规范。就我个人而言,我宁愿只获得一个IPv4地址,但似乎也没有办法告诉NsdManager。

您可以尝试使用来自Java网络API的NetworkInterface类以编程方式检索接口的名称。例如:

val interfacename = NetworkInterface.getNetworkInterfaces()
.toList()
.find { interface ->
interface.inetAddresses.any { address ->
address is Inet6Address && address.isLinkLocalAddress
}
}
?.name
val baseUrl = "http://[fe80::xxxx:xxxx:xxxx:9718%$interfacename]:port/"
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.build()

首先,了解IPv6链路本地地址有特定的用途,通常不用于正常的网络流量。

源自RFC 4291, IP Version 6 Addressing Architecture:

Link-Local地址被设计用来在a上寻址用于自动地址配置等目的的单链路;发现邻居,或者当没有路由器存在时。

接下来,由于设备中的所有接口都使用相同的Link-Local网络,因此必须使用Zone ID来区分目的接口。请记住,即使主机(pc、打印机等)也有路由表,因此它们必须使用区域id来区分Link-Local地址。

来自RFC 6874,用地址字面值和统一资源标识符表示IPv6区域标识符:

因为相同的非全局地址可以在多个地址中使用相同作用域的区域(例如,使用链路本地地址fe80::1 in)两个独立的物理链路)和一个节点可能有附加的接口到同一作用域的不同区域(例如,一台路由器通常有多个接口连接到不同的链路),一个节点需要一个内部意味着确定非全局地址属于哪个区域。这是通过在节点内分配一个不同的"区域"来实现的index"到该节点所连接的同一作用域的每个区域,通过允许地址的所有内部使用都用带索引。

IPv6 Link-Local寻址需要使用Zone ID。事实上,Link-Local寻址是目前唯一允许使用的区域id。

来自RFC 6874,用地址字面值和统一资源标识符表示IPv6区域标识符:

为了限制这种风险,实现必须不允许使用这种格式除了定义良好的用法,例如发送到link-local前缀fe80::/10下的地址。在撰写本文时,这是只有明确定义的用法。

你可以使用一些其他唯一的寻址,例如ULA (RFC 4193,唯一本地IPv6单播地址),以避免该要求。对于ULA,您在fc00::/7范围内使用寻址,限制将您限制在fd00::/8范围内,其中下一个40位必须随机选择,给您一个/48前缀,从中获得65,536个标准/64IPv6网络。

最新更新