【问题标题】:Python 3 IPv6 MulticastPython 3 IPv6 多播
【发布时间】:2018-06-14 02:13:13
【问题描述】:

我正在尝试为 Python 中的 IPv6 多播通信设计一个简单的客户端/服务器对。到目前为止,我的代码似乎符合我在网上看到的示例,但我的服务器从未接收到数据。

我在ip maddr shownetstat -g 中看到了订阅。 tcpdump 显示:10:13:36.913546 IP6 (flowlabel 0x77fe8, hlim 5, next-header UDP (17) payload length: 20) **omitted** > ff16::fe.commplex-main: [udp sum ok] UDP, length 12

客户端和服务器连接到同一个交换机,并且 IPv6 单播地址在同一个子网中(它们可以相互 ping 通)。

服务器

#!/usr/bin/python3

import socket
import struct

local_addr = ::
mcast_addr = "ff16::fe"
mcast_port = 5000
ifn = "eno1"

# Create socket
sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)

sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)

# Set multicast interface
ifi = socket.if_nametoindex(ifn)
ifis = struct.pack("I", ifi)
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, ifis)

# Set multicast group to join
group = socket.inet_pton(socket.AF_INET6, mcast_addr) + ifis
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, group)

sock_addr = socket.getaddrinfo(local_addr, mcast_port, socket.AF_INET6, socket.SOCK_DGRAM)[0][4]
sock.bind(sock_addr)

cmd = ""
while True:
    data, src = sock.recvfrom(1024)
    print("From " + str(src) + ": " + data.decode())

客户

#!/usr/bin/python3

import socket
import struct

message = "Hello world!"
sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
sock_addr = socket.getaddrinfo("ff16::fe", 5000, socket.AF_INET6, socket.SOCK_DGRAM)[0][4]

ttl = struct.pack('i', 5)
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, ttl)

sock.sendto(message.encode(), sock_addr)

我们将不胜感激。

【问题讨论】:

  • 客户端和服务器是否在同一个网络上?组播数据包不能通过 Internet 路由。
  • 客户端和服务器在同一个网络。
  • "客户端和服务器在同一个网络上。" 那么你应该使用 Link-Local 范围:ff12::fe。您似乎仍然错误地计算了 UDP 校验和。 "UDP 与 IPv4 和 IPv6 的使用之间的主要区别在于,由于缺少 IPv6 标头校验和,RFC 2460 要求使用计算的 UDP 校验和,即非零值。包含伪校验和计算中的标头提供了数据报是否已传送到预期的 IPv6 目标节点的统计检查。[RFC1071] 中描述了校验和计算的算法。"
  • 在这种情况下,未分配的范围是否应该与链接本地范围一样工作?就 UDP 校验和而言,我的代码中还需要为此做什么?
  • 根据 RFC,您应该能够使用未分配的范围,但我不会,因为它可能会在某些时候被重新定义,例如,直到这个 RFC,Realm-Local 未被分配,现在您在尝试使用该范围时可能会遇到麻烦。 Admin-Local 非常适合您自己使用。

标签: python python-3.x ipv6 multicast


【解决方案1】:

除了所有其他好的建议:将服务器上的套接字绑定到链接本地地址。这将过滤传入的数据包,以便只有具有该目标地址的数据包才能到达您的代码。发送到多播地址的数据包将被丢弃。尝试绑定到 :: 并确保在继续进行更复杂的操作之前可以正常工作。

【讨论】:

  • 改成 ::,还是没有骰子。服务器机器上的跟踪什么也没看到,发送机器:10:13:36.913546 IP6 (flowlabel 0x77fe8, hlim 5, next-header UDP (17) payload length: 20) **omitted** > ff16::fe.commplex-main: [udp sum ok] UDP, length 12 由于某种原因,校验和现在似乎没问题...我将在帖子中添加有关网络设置的更多详细信息。
  • 这也可能是代码的问题。谢谢你的收获。
【解决方案2】:

您不能使用"ff10:: 作为 IPv6 多播地址。您在该地址中使用的范围0 是保留的。 IPv6 多播需要考虑多个 RFC,它们使用标志和范围。例如,作用域1 是接口本地作用域,发送到具有该作用域的组的流量不会离开发送主机上的本地接口。

查看RFC 7346, IPv6 Multicast Address Scopes 了解最新范围:

  1. IPv6 多播地址范围的定义(更新 RFC 4291)

下表更新了 [RFC4291] 中的定义:

    +------+--------------------------+-------------------------+
    | scop | NAME                     | REFERENCE               |
    +------+--------------------------+-------------------------+
    |  0   | Reserved                 | [RFC4291], RFC 7346     |
    |  1   | Interface-Local scope    | [RFC4291], RFC 7346     |
    |  2   | Link-Local scope         | [RFC4291], RFC 7346     |
    |  3   | Realm-Local scope        | [RFC4291], RFC 7346     |
    |  4   | Admin-Local scope        | [RFC4291], RFC 7346     |
    |  5   | Site-Local scope         | [RFC4291], RFC 7346     |
    |  6   | Unassigned               |                         |
    |  7   | Unassigned               |                         |
    |  8   | Organization-Local scope | [RFC4291], RFC 7346     |
    |  9   | Unassigned               |                         |
    |  A   | Unassigned               |                         |
    |  B   | Unassigned               |                         |
    |  C   | Unassigned               |                         |
    |  D   | Unassigned               |                         |
    |  E   | Global scope             | [RFC4291], RFC 7346     |
    |  F   | Reserved                 | [RFC4291], RFC 7346     |
    +------+--------------------------+-------------------------+

编辑:

根据您对原始问题的更改,您没有正确计算 UDP 校验和,这对于 IPv4 是可选的,但对于 IPv6 是必需的。

此外,由于您正在构建自己的多播地址(不是 IANA 分配的地址),您还应该重新使用 T 标志作为 1

T = 0 表示永久分配的(“知名”)多播 地址,由 Internet 号码分配机构 (IANA) 分配。

T = 1 表示非永久分配的(“瞬态”或 “动态”分配的)多播地址。


编辑 2:

顺便说一句,在 IPv6 多播中不能使用零地址:

保留多播地址:
FF00:0:0:0:0:0:0:0
FF01:0:0:0:0:0:0:0
FF02:0:0:0:0:0:0:0
FF03:0:0:0:0:0:0:0
FF04:0:0:0:0:0:0:0
FF05:0:0:0:0:0:0:0
FF06:0:0:0:0:0:0:0FF07:0:0:0:0:0:0:0
FF08:0:0:0:0:0:0:0
FF09:0:0:0:0:0:0:0
FF0A:0:0:0:0:0:0:0
FF0B:0:0:0:0:0:0:0
FF0C:0:0:0:0:0:0:0
FF0D:0:0:0:0:0:0:0
FF0E:0:0:0:0:0:0:0
FF0F:0:0:0:0:0:0:0

听起来您真的非常需要在尝试使用 IPv6 多播之前研究 RFC。

【讨论】:

  • 这似乎允许程序开始发送数据包,但是,服务器仍然没有收到它。带有 -vv 的 tcpdump 显示:> ff06::.5000: [bad udp cksum 0xb590 -> 0xf470!] UDP, length 12,我已经更新了问题,感谢您的关注。
  • 再次感谢,我已经修改了我的代码以使用正确的多播地址。问题是 udp 校验和哪里出错了?
  • 我不确定,我看不到它是在哪里计算的。
  • 校验和似乎不再是问题
【解决方案3】:

我还必须在客户端中设置多播接口。客户端和服务器都有一个虚拟接口和一个可操作的 VLAN 感知接口(网络驱动程序的设计我无法进入。我将两者上的接口都设置为 VLAN 感知接口。

在客户端:

# Set multicast interface
ifi = socket.if_nametoindex(ifn)
ifis = struct.pack("I", ifi)
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, ifis)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-10-26
    • 2011-04-20
    • 2014-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-20
    • 1970-01-01
    相关资源
    最近更新 更多