[erlang-questions] Socket option FREEBIND
Andreas Schultz
aschultz@REDACTED
Mon Oct 23 09:44:32 CEST 2017
----- On Oct 20, 2017, at 8:22 PM, Michael L Martin <mmartin4242@REDACTED> wrote:
> Posting again to all...
> iex(4)> {:ok, s} = :gen_tcp.connect('localhost', 80, [{:raw, 0, 15, <<1>>},
> {:ip, {10, 138, 69, 63}}])
Very small nitpick (does not impact the function), setsockopt boolean and integer options usually use a 32bit integer. Debugging tools like strace are much easier to read then.
So I would recommend to write the options as {:raw, 0, 15, <<1:32/native>>}
> {:ok, #Port<0.1310>}
> iex(5)> :inet.getopts(s, [{:raw, 0, 15, 4}])
> {:ok, [{:raw, 0, 15, <<1, 0, 0, 0>>}]}
> iex(6)> :gen_tcp.close(s) :ok
> iex(7)> {:ok, s} = :gen_tcp.connect('localhost', 80, [{:raw, 0, 15, <<1>>},
> {:ip, {10, 138, 69, 64}}])
10.138.69.64 does not exist on you system, so attempting to use it will fail (as you just discovered).
> ** (exit) :badarg
> (kernel) gen_tcp.erl:149: :gen_tcp.connect/4
> iex(7)>
> I see badarg when trying to connect using a bogus IP address. But, the option is
> being set now.
> Using IP_TRANSPARENT instead of IP_FREEBIND:
> iex(8)> {:ok, s} = :gen_tcp.connect('localhost', 80, [{:raw, 0, 19, <<1>>},
> {:ip, {10, 138, 69, 64}}])
IP_TRANSPARENT is very different from IP_FREEBIND. You need iptables rules to make it work (and a lot more about what it is supposed to to before playing with it).
> At this point, :gen_tcp.connect/3 blocks. The ip address does not exist, so now
> I add it:
> sudo ip addr add 10.138.69.64 dev eno1
> ip addr list dev eno12: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc
> pfifo_fast state UP group default qlen 1000
> link/ether 8c:89:a5:c6:e1:7f brd ff:ff:ff:ff:ff:ff
> inet 10.138.69.63/16 brd 10.138.255.255 scope global eno1
> valid_lft forever preferred_lft forever
> inet 10.138.69.64/32 scope global eno1
> valid_lft forever preferred_lft forever
> inet6 fe80::8e89:a5ff:fec6:e17f/64 scope link
> valid_lft forever preferred_lft forever
> I would think that :gen_tcp.connect would then connect, but it's still blocked,
> and eventually times out.
> After the timeout:
> iex(8)> {:ok, s} = :gen_tcp.connect('localhost', 80, [{:raw, 0, 19, <<1>>},
> {:ip, {10, 138, 69, 64}}])
> {:ok, #Port<0.1300>}
> iex(9)> :inet.getopts(s, [{:raw, 0, 19, 4}]) {:ok, [{:raw, 0, 19, <<1, 0, 0,
> 0>>}]}
> iex(10)>
As I have said before, using FREEBIND for connect makes no sense.
What you probably want it to bind a listening socket to an IP address that will later be added by systemd. For that include the raw option in the listening call.
Regards
Andreas
>> On 2017-10-20 11:10 AM, Michael L Martin wrote:
>>> 6, 15, <<1>>}, {:ip, {10, 138, 69, 64}}])
>>> ** (MatchError) no match of right hand side value: {:error, :eaddrnotavail}
> On 2017-10-20 12:33 PM, Richard Jones wrote:
>> I'm able to make outbound connections bound to an arbitrary IP from my
>> range by specifying a socket option like: {raw, 0, 15, <<1>>}
>> note that that protocol part of {raw, ???, _, _} is 0, not 6, since
>> it's an IP level socket option (not tcp-level).
>> btw there's also a system-wide option so you don't need to specify
>> ip_freebind per socket:
>> echo 1 > /proc/sys/net/ipv6/ip_nonlocal_bind
>> RJ
>> On 20 October 2017 at 17:17, Michael L Martin [ mailto:mmartin4242@REDACTED |
>> <mmartin4242@REDACTED> ] wrote:
>>> Thanks for the quick response. As for what I'm attempting to achieve, well,
>>> I'm not really sure.
>>> I have a customer that is adamant that we use IP_FREEBIND, so I've been
>>> poking around in :gen_tcp and :inet trying to see how to do that.
>>> Here's what happens when I try to open a listen socket:
>>> iex(5)> {:ok, s} = :gen_tcp.listen(8000, [{:raw, 6, 15, <<1 :: size(32)>>},
>>> {:ip, {10, 138, 69, 63}}])
>>> {:ok, #Port<0.1305>}
>>> iex(6)> :inet.getopts(s, [{:raw, 6, 15, 4}])
>>> {:ok, []}
>>> iex(7)> :gen_tcp.close(s)
>>> :ok
>>> iex(8)> {:ok, s} = :gen_tcp.listen(8000, [{:raw, 6, 15, <<1 :: size(32)>>},
>>> {:ip, {10, 138, 69, 64}}])
>>> ** (MatchError) no match of right hand side value: {:error, :eaddrnotavail}
>>> iex(8)>
>>> So it looks like it's not working for listen sockets, either. Could very
>>> well be that I simply don't know what the heck I'm doing...
>>> On 2017-10-20 12:04 PM, Andreas Schultz wrote:
>>> Hi,
>>> ----- On Oct 20, 2017, at 5:10 PM, Michael L Martin [
>>> mailto:mmartin4242@REDACTED | <mmartin4242@REDACTED> ] wrote:
>>> Raw socket options looks like it should be the answer, but I'm not getting
>>> good results:
>>> iex( [ mailto:worker@REDACTED | worker@REDACTED ] )28> {:ok, s} =
>>> :gen_tcp.connect('localhost', 80,
>>> [{:raw, 6, 15, <<1>>}, {:ip, {10, 138, 69, 63}}])
>>> {:ok, #Port<0.12110>}
>>> iex( [ mailto:worker@REDACTED | worker@REDACTED ] )29> :inet.getopts(s,
>>> [{:raw, 6, 15, 32}])
>>> {:ok, []} <== option not set?
>>> iex( [ mailto:worker@REDACTED | worker@REDACTED ] )29> {:ok, s} =
>>> :gen_tcp.connect('localhost', 80,
>>> [{:raw, 6, 15, <<1>>}, {:ip, {10, 138, 69, 64}}])
>>> ** (MatchError) no match of right hand side value: {:error, :eaddrnotavail}
>>> IP_FREEBIND on connect doesn't make sense, it's only really useful on listen
>>> sockets.
>>> The option lets you bind to an IP address that does not yet exist on the
>>> host. That is ok for listen, but when you attempt to build an outgoing
>>> connection from that IP, then it has to be configured.
>>> It appears that the IP_FREEBIND option is simply not set. If I use
>>> IP_TRANSPARENT instead:
>>> iex( [ mailto:worker@REDACTED | worker@REDACTED ] )35> {:ok, s} =
>>> :gen_tcp.connect('localhost', 80,
>>> [{:raw, 6, 19, <<1>>}, {:ip, {10, 138, 69, 63}}])
>>> {:ok, #Port<0.12113>}
>>> iex( [ mailto:worker@REDACTED | worker@REDACTED ] )34> :inet.getopts(s,
>>> [{:raw, 6, 19, 32}])
>>> {:ok, [{:raw, 6, 19, <<0, 0, 0, 0>>}]}
>>> IP_TRANSPARENT needs special iptables rules to work correctly and is for a
>>> very specific use case. I don't think it would be very useful for an Erlang
>>> application to use that.
>>> In this case, the raw option 19 (IP_TRANSPARENT) is set, but to false rather
>>> than true. I believe this is because the beam file needs root or
>>> CAP_NET_ADMIN.
>>> You need root and a specific iptables setup for it to work.
>>> Any thoughts?
>>> What are you attempting to achieve anyway?
>>> Regards
>>> Andreas
>>> On 2017-10-16 01:18 PM, Guilherme Andrade wrote:
>>> I believe you can use raw socket options[1] for that, but it won't be
>>> portable.
>>> [1]: [ http://erlang.org/doc/man/inet.html#setopts-2 |
>>> http://erlang.org/doc/man/inet.html#setopts-2 ] - {raw, Protocol,
>>> OptionNum, ValueBin}
>>> On 16 October 2017 at 15:09, Michael L Martin [ mailto:mmartin4242@REDACTED |
>>> <mmartin4242@REDACTED> ] wrote:
>>>> Hi all,
>>>> Is there a way to specify the FREEBIND option when opening a socket? I
>>>> don't see any reference to it in the documentation.
>>>> Thanks,
>>>> _______________________________________________
>>>> erlang-questions mailing list [ mailto:erlang-questions@REDACTED |
>>>> erlang-questions@REDACTED ] [
>>>> http://erlang.org/mailman/listinfo/erlang-questions |
>>>> http://erlang.org/mailman/listinfo/erlang-questions ]
>>> --
>>> Guilherme
>>> _______________________________________________
>>> erlang-questions mailing list [ mailto:erlang-questions@REDACTED |
>>> erlang-questions@REDACTED ] [
>>> http://erlang.org/mailman/listinfo/erlang-questions |
>>> http://erlang.org/mailman/listinfo/erlang-questions ]
>>> _______________________________________________
>>> erlang-questions mailing list [ mailto:erlang-questions@REDACTED |
>>> erlang-questions@REDACTED ] [
>>> http://erlang.org/mailman/listinfo/erlang-questions |
>>> http://erlang.org/mailman/listinfo/erlang-questions ]
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20171023/79afc9f9/attachment.htm>
More information about the erlang-questions
mailing list