我有一個 Linux 網路介面,IPv6 多播流量正在到達。有 ICMPv6 資料包和 UDP 資料包到達同一個多播組。
我正在嘗試接收 UDP 流量。下面的代碼是用 Python 撰寫的,但我認為這并不重要;這里的 Python 庫是 BSD 套接字 API 的一個非常薄的包裝器。這就是我正在做的事情:
import socket
import struct
import select
import ipaddress
# .packet is a byte array
mcast_group = ipaddress.IPv6Address("ff22:5eea:675c:d1fc:17c::1").packed
address = ipaddress.IPv6Address("...ipv6 global scope address of interface...")
sock = socket.socket(socket.AF_INET6, socket.SOCK_RAW, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
group = struct.pack("16s i",
mcast_group,
3 #Interface ID
)
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, group)
sock.bind((address.compressed, 0, 0, 0))
recv_socks, _, _ = select.select([sock], [], [], 10)
if recv_socks:
print("Received packet")
使用上面的代碼,我似乎同時接收到了 UDP 和 ICMPv6 資料包。如果我將第二個引數更改為socket.socket()from socket.SOCK_RAWtosocket.SOCK_DGRAM那么我根本不會收到任何資料包。
我已經確認兩種資料包型別都到達該介面,使用wireshark 發送到該多播組。
這里有一些我根本不了解的東西socket()。不應該指定IPPROTO_UDP意味著我只得到 UDP 資料包嗎?不應該指定SOCK_DGRAM仍然允許UDP資料包通過嗎?
Linux 內核在 aarch64 上是 4.9,以防萬一。
uj5u.com熱心網友回復:
最終想通了這一點。這是這樣做的正確方法:
import socket
import struct
import select
import ipaddress
# .packet is a byte array
mcast_group = ipaddress.IPv6Address("ff22:5eea:675c:d1fc:17c::1")
address = ipaddress.IPv6Address("...ipv6 global scope address of interface...")
port_no = 5555
interface_idx = 3
sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
group = struct.pack("16s i",
mcast_group.packed,
3 #Interface ID
)
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, group)
sock.bind((str(mcast_group), port_no, 0, interface_idx))
recv_socks, _, _ = select.select([sock], [], [], 10)
if recv_socks:
print("Received packet")
與問題中的代碼的重要區別:
- UDP (
SOCK_DGRAM) 套接字需要系結到一個埠號。AFAICT 這就是為什么我得到 ICMP 流量以及 UDPSOCK_RAW和SOCK_DGRAM. - 不要將套接字系結到感興趣的介面上的單播地址,而是將其系結到多播組,但使用介面索引作為
scope_id(傳遞給的地址元組中的第四個socket.bind())。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/488801.html
