即使在使用 IP_FREEBIND 之后 EADDRNOTAVAIL?
EADDRNOTAVAIL even after using IP_FREEBIND?
我的印象是,在 Linux 下,只要设置 IP_FREEBIND
套接字选项,就可以绑定到非本地地址,但这不是我看到的行为:
$ sudo strace -e 'trace=%network' ...
...
socket(AF_INET, SOCK_RAW, IPPROTO_UDP) = 5
setsockopt(5, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
setsockopt(5, SOL_SOCKET, SO_NO_CHECK, [1], 4) = 0
setsockopt(5, SOL_IP, IP_HDRINCL, [1], 4) = 0
setsockopt(5, SOL_IP, IP_FREEBIND, [1], 4) = 0
bind(5, {sa_family=AF_INET, sin_port=htons(abcd), sin_addr=inet_addr("w.x.y.z")}, 16) = -1 EADDRNOTAVAIL (Cannot assign requested address)
...
我也设置了 ip_nonlocal_bind
设置,只是为了确定,我得到了相同的结果。
$ sysctl net.ipv4.ip_nonlocal_bind
net.ipv4.ip_nonlocal_bind = 1
不幸的是,似乎无法将原始 IP 套接字绑定到非本地、非广播和非多播地址,无论 IP_FREEBIND
。因为我在你的 strace
输出中看到 inet_addr("w.x.y.z")
,我假设这正是你想要做的并且 w.x.y.z
是一个非本地单播地址,因此你的 bind
系统调用失败。
这似乎符合man 7 raw
:
A raw socket can be bound to a specific local address using the
bind(2)
call. If it isn't bound, all packets with the specified
IP protocol are received. In addition, a raw socket can be bound
to a specific network device using SO_BINDTODEVICE
; see socket(7)
.
确实,查看内核源代码,在raw_bind()
中我们可以看到the following check:
ret = -EADDRNOTAVAIL;
if (addr->sin_addr.s_addr && chk_addr_ret != RTN_LOCAL &&
chk_addr_ret != RTN_MULTICAST && chk_addr_ret != RTN_BROADCAST)
goto out;
另外,请注意 .sin_port
必须是 0
。原始套接字的 .sin_port
字段用于 select 一个 sending/receiving IP 协议(不是端口,因为我们处于 3 级并且端口不存在)。正如手册所述,从 Linux 2.2 开始,您不能再通过 select 通过 .sin_port
发送协议,发送协议是在创建套接字时设置的。
我的印象是,在 Linux 下,只要设置 IP_FREEBIND
套接字选项,就可以绑定到非本地地址,但这不是我看到的行为:
$ sudo strace -e 'trace=%network' ...
...
socket(AF_INET, SOCK_RAW, IPPROTO_UDP) = 5
setsockopt(5, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
setsockopt(5, SOL_SOCKET, SO_NO_CHECK, [1], 4) = 0
setsockopt(5, SOL_IP, IP_HDRINCL, [1], 4) = 0
setsockopt(5, SOL_IP, IP_FREEBIND, [1], 4) = 0
bind(5, {sa_family=AF_INET, sin_port=htons(abcd), sin_addr=inet_addr("w.x.y.z")}, 16) = -1 EADDRNOTAVAIL (Cannot assign requested address)
...
我也设置了 ip_nonlocal_bind
设置,只是为了确定,我得到了相同的结果。
$ sysctl net.ipv4.ip_nonlocal_bind
net.ipv4.ip_nonlocal_bind = 1
不幸的是,似乎无法将原始 IP 套接字绑定到非本地、非广播和非多播地址,无论 IP_FREEBIND
。因为我在你的 strace
输出中看到 inet_addr("w.x.y.z")
,我假设这正是你想要做的并且 w.x.y.z
是一个非本地单播地址,因此你的 bind
系统调用失败。
这似乎符合man 7 raw
:
A raw socket can be bound to a specific local address using the
bind(2)
call. If it isn't bound, all packets with the specified IP protocol are received. In addition, a raw socket can be bound to a specific network device usingSO_BINDTODEVICE
; seesocket(7)
.
确实,查看内核源代码,在raw_bind()
中我们可以看到the following check:
ret = -EADDRNOTAVAIL;
if (addr->sin_addr.s_addr && chk_addr_ret != RTN_LOCAL &&
chk_addr_ret != RTN_MULTICAST && chk_addr_ret != RTN_BROADCAST)
goto out;
另外,请注意 .sin_port
必须是 0
。原始套接字的 .sin_port
字段用于 select 一个 sending/receiving IP 协议(不是端口,因为我们处于 3 级并且端口不存在)。正如手册所述,从 Linux 2.2 开始,您不能再通过 select 通过 .sin_port
发送协议,发送协议是在创建套接字时设置的。