-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gen_tcp cannot connect to IPv6 server through link-local address #4852
Comments
One Linux, when using link local IPv6 addresses, one needs to set With Erlang there are two problems with that:
|
How can we establish a link local TCP connection using Erlang? |
Use FreeBSD ;-) I think it actually works there because they have Scope ED 0 for link local. Not entirely sure, though. Or use IPv4 for link local. Now and then I have tried to make this work on Linux, with the new socket API, where it is possible to specify an address with Scope ID. But I have still not found a reliable way, since I have not yet found out which Scope ID to use. It would be possible, in the gen_tcp API, to pass the Scope ID buried in the link local address as FreeBSD does internally in the kernel. This restricts the Scope ID to 16 bit, so it is not a complete solution. `inet:parse_address/1 would then need to be augmented to interpret the %ScopeID suffix, and we still lack a way to translate Interface to ScopeID, and there seems to be a new concept in which the Scope ID 0x16 means link-local, bit I can not find any documentation for this. So, anybody than can educate me / us on how to handle IPv6 ScopeID:s on Linux, please speak up! |
easy, on Linux 5.11, OTP-24, without error handling -module(test).
-export([get/2]).
get(LL, Port) ->
[Host, If] = string:lexemes(LL, "%"),
{ok, Idx} = net:if_name2index(If),
{ok, IP6} = inet:parse_ipv6_address(Host),
Addr = #{family => inet6,
port => Port,
addr => IP6,
scope_id => Idx},
io:format("Host: ~s~nInterface: ~s~nScope: ~p~n",
[Host, If, Idx]),
{ok, Socket} = socket:open(inet6, stream, tcp),
ok = socket:connect(Socket, Addr, infinity),
socket:send(Socket, "GET / HTTP/1.0\r\n\r\n"),
{ok, Data} = socket:recv(Socket),
io:format("Data:~n~p~n", [Data]),
ok. Erlang/OTP 24 [erts-12.0] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [jit]
Eshell V12.0 (abort with ^G)
1> c(test).
{ok,test}
2> test:get("fe80::fb43:f093:7df5:dfdc%wlan0", 8000).
Host: fe80::fb43:f093:7df5:dfdc
Interface: wlan0
Scope: 5
Data:
<<"HTTP/1.1 404 Not Found\r\nServer: webfs/1.21\r\nConnection: Close\r\nAccept-Ranges: bytes\r\nContent-Type: text/plain\r\nContent-Length: 28\r\nDate: Thu, 20 May 2021 10:28:42 GMT\r\n\r\nFile or directory not found\n">>
ok
3> |
Thank you for your suggestion. It works for me on Erlang 23. Sadly, |
|
@RoadRunnr: That was enlightening. Can this be made to work for the loopback interface? |
@RaimoNiskanen getaddrinfo works nicely. I didn't even knew that function was there I have no idea how to assign link local address to loopback. However, it works nicely with a dummy interface:
2> net:getaddrinfo("fe80::c8f8:70ff:fe2f:ae01%dummy0").
{ok,[#{addr =>
#{addr => {65152,0,0,0,51448,28927,65071,44545},
family => inet6,flowinfo => 0,port => 0,scope_id => 9},
family => inet6,protocol => tcp,type => stream},
#{addr =>
#{addr => {65152,0,0,0,51448,28927,65071,44545},
family => inet6,flowinfo => 0,port => 0,scope_id => 9},
family => inet6,protocol => udp,type => dgram},
#{addr =>
#{addr => {65152,0,0,0,51448,28927,65071,44545},
family => inet6,flowinfo => 0,port => 0,scope_id => 9},
family => inet6,protocol => ip,type => raw}]} |
I thought the idea with a loopback interface was to have an always present local interface. 20> net:getaddrinfo("::1").
{ok,[#{addr =>
#{addr => {0,0,0,0,0,0,0,1},
family => inet6,flowinfo => 0,port => 0,scope_id => 0},
family => inet6,
protocol => [tcp,'TCP'],
type => stream},
#{addr =>
#{addr => {0,0,0,0,0,0,0,1},
family => inet6,flowinfo => 0,port => 0,scope_id => 0},
family => inet6,
protocol => [udp,'UDP'],
type => dgram},
#{addr =>
#{addr => {0,0,0,0,0,0,0,1},
family => inet6,flowinfo => 0,port => 0,scope_id => 0},
family => inet6,
protocol => [ip,'IP'],
type => raw}]}
21> net:getaddrinfo("::1%lo").
{error,enoname} But I can not get it to work. getaddrinfo says scope ID is 0, the interface index is 1, and ifconfig says: |
36> net:getaddrinfo("::1", "https").
{ok,[#{addr =>
#{addr => {0,0,0,0,0,0,0,1},
family => inet6,flowinfo => 0,port => 443,scope_id => 0},
family => inet6,
protocol => [tcp,'TCP'],
type => stream}]} But it seems we have introduced a bug in OTP-24.0 |
That bug in |
A fix/update for this (link-local) has been merged into maint, and will be part of OTP 24.3. |
Describe the bug
gen_tcp
get badarg when connecting to IPv6 server through link-local address. However, it can connect to a publish domain through IPv6.To Reproduce
I have two machines, the first machine opens a TCP v6 server by using
netcat
.The second machine open erlang shell to connect to the opened server
Expected behavior
The erlang code can connect to the server and can transmit/receive data.
Affected versions
I tried Erlang 23 on Fedora 34, Erlang 21 on raspberry pi and Erlang 24 build from source.
The text was updated successfully, but these errors were encountered: