Skip to content
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

IPv6 server vs. IPv4 client mismatch on FreeBSD #587

Open
yurivict opened this issue Oct 8, 2016 · 5 comments
Open

IPv6 server vs. IPv4 client mismatch on FreeBSD #587

yurivict opened this issue Oct 8, 2016 · 5 comments

Comments

@yurivict
Copy link

yurivict commented Oct 8, 2016

I am looking at cadabra2 project that uses websocketpp.

On the server these functions call each other:
wserver.listen(0); -> listen(lib::asio::ip::tcp::v6(), port); -> ... -> bind(INET6)

The comment says The IPv6 protocol with mapped IPv4 for dual stack hosts will be used. But the actual bind is only done using IPv6. Then client tries to connect to 127.0.0.1:{same port} and gets 'connection refused'.

FreeBSD has dual stack, but this doesn't mean that you can connect from IPv4 to IPv6. How is this supposed to work?

PS: This is separate from the other bug related to cadabra2 on FreeBSD that is about EINPROGRESS mishandling.

@yurivict
Copy link
Author

yurivict commented Oct 8, 2016

I replaced v6() with v4() and the problem is gone. You should probably make v6()/v4() passed as an argument to "listen".

@zaphoyd
Copy link
Owner

zaphoyd commented Oct 14, 2016

On most systems I've tested on this usage of Asio listen allows connections from both v4 and v6. The endpoint will appear as a v6 only one from the point of view of your code. For example, if you ask for the remote address it will be in IPv6 format and in the case of a v4 connection the address listed will be a mapped v4 address (::ffff:1.2.3.4).

I'll do some more testing with Asio on freebsd, but in the meantime, if your systems doesn't behave this way you can explicitly listen on v4 via a listen overload already:

endpoint::listen(websocketpp::lib::asio::ip::tcp::v4(), 9002)

https://docs.websocketpp.org/singletonwebsocketpp_1_1transport_1_1asio_1_1endpoint.html#ae6813b9ecda67a13a78967f72e77b061

@yurivict
Copy link
Author

This mapping might be a linuxism.

I think you should just delete the functions that default to IPv6 or IPv4. This should be the decision made on the application level, not on the library level IMO.

@zaphoyd
Copy link
Owner

zaphoyd commented Oct 14, 2016

After a bit of research, it looks like this is standard RFC documented behavior for dual stack IPv6 implementations and is controlled by the IPV6_V6ONLY socket option. I know from my own testing that virtually every popular Linux distribution as well as macOS and Windows have this off by default. Wikipedia claims that some security oriented BSDs have different defaults that require you to open separate v4 and v6 sockets.

I'll think a bit about whether it makes sense to have a default address family. Based on the usage that I have seen the vast majority of WebSocket++ library users will want the current default and those that don't do currently have an easy way to specify something else. I also haven't seen many libraries at this level of abstraction that require the user to explicitly specify an address family.

3.7 Compatibility with IPv4 Nodes

   The API also provides a different type of compatibility: the ability
   for IPv6 applications to interoperate with IPv4 applications.  This
   feature uses the IPv4-mapped IPv6 address format defined in the IPv6
   addressing architecture specification [2].  This address format
   allows the IPv4 address of an IPv4 node to be represented as an IPv6
   address.  The IPv4 address is encoded into the low-order 32 bits of
   the IPv6 address, and the high-order 96 bits hold the fixed prefix
   0:0:0:0:0:FFFF.  IPv4-mapped addresses are written as follows:

      ::FFFF:<IPv4-address>

   These addresses can be generated automatically by the getaddrinfo()
   function, as described in Section 6.1.

   Applications may use AF_INET6 sockets to open TCP connections to IPv4
   nodes, or send UDP packets to IPv4 nodes, by simply encoding the
   destination's IPv4 address as an IPv4-mapped IPv6 address, and
   passing that address, within a sockaddr_in6 structure, in the
   connect() or sendto() call.  When applications use AF_INET6 sockets
   to accept TCP connections from IPv4 nodes, or receive UDP packets
   from IPv4 nodes, the system returns the peer's address to the
   application in the accept(), recvfrom(), or getpeername() call using
   a sockaddr_in6 structure encoded this way.

   Few applications will likely need to know which type of node they are
   interoperating with.  However, for those applications that do need to
   know, the IN6_IS_ADDR_V4MAPPED() macro, defined in Section 6.4, is
   provided.

...

5.3 IPV6_V6ONLY option for AF_INET6 Sockets
   This socket option restricts AF_INET6 sockets to IPv6 communications
   only.  As stated in section <3.7 Compatibility with IPv4 Nodes>,
   AF_INET6 sockets may be used for both IPv4 and IPv6 communications.
   Some applications may want to restrict their use of an AF_INET6
   socket to IPv6 communications only.  For these applications the
   IPV6_V6ONLY socket option is defined.  When this option is turned on,
   the socket can be used to send and receive IPv6 packets only.  This
   is an IPPROTO_IPV6 level option.  This option takes an int value.
   This is a boolean option.  By default this option is turned off.```

(https://tools.ietf.org/html/rfc3493#section-5.3)

@yurivict
Copy link
Author

yurivict commented Oct 14, 2016

I see, I didn't know about IPV6_V6ONLY.

Now I found that on FreeBSD there is a relevant sysctl option net.inet6.ip6.v6only which is set during the system boot based on the ipv6_ipv4mapping rc.conf variable which defaults to "NO". So, unless the user has set ipv6_ipv4mapping=YES in /etc/rc.conf, IPv6 sockets act as IPv6-only on FreeBSD.

Of course, once you provided the default functions most users just use them without even being aware of this. But I still think that you shouldn't make websocketpp implicitly depend on net.inet6.ip6.v6only=1. I am sure there are other OSes that will have this issue too, and setting such defaults directly conflicts with this. And you can't just ask users to change this OS setting either. I don't see how you can fix these default functions to get rid of this problem in general.

The difference between IPv4 and IPv6 is a big deal, apps should be aware which protocol they use.

uqs pushed a commit to freebsd/freebsd-ports that referenced this issue Oct 21, 2016
- Unbreaks GUI by applying two patches:
- Patch in boost headers to prevent EINPROGRESS mishandling
  zaphoyd/websocketpp#563
- Patch in websocketpp fixing IPv4 vs. IPv6 mismatch
  zaphoyd/websocketpp#587
- GUI option is made default
- Clones icons for the GUI app cadabra2-gui
- Adds "USE_TEX=texmf texhash" to allow for unincluded latex macros,
  and to make tex hash local packages installed by cadabra2
- Verified that cadabra2 GUI now works fine - it is able to run
  many examples from their website.

PR:		213329
Submitted by:	Yuri Victorovich <yuri@rawbw.com> (maintainer)


git-svn-id: svn+ssh://svn.freebsd.org/ports/head@424395 35697150-7ecd-e111-bb59-0022644237b5
uqs pushed a commit to freebsd/freebsd-ports that referenced this issue Oct 21, 2016
- Unbreaks GUI by applying two patches:
- Patch in boost headers to prevent EINPROGRESS mishandling
  zaphoyd/websocketpp#563
- Patch in websocketpp fixing IPv4 vs. IPv6 mismatch
  zaphoyd/websocketpp#587
- GUI option is made default
- Clones icons for the GUI app cadabra2-gui
- Adds "USE_TEX=texmf texhash" to allow for unincluded latex macros,
  and to make tex hash local packages installed by cadabra2
- Verified that cadabra2 GUI now works fine - it is able to run
  many examples from their website.

PR:		213329
Submitted by:	Yuri Victorovich <yuri@rawbw.com> (maintainer)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants