xrdp: xrdp fails without IPv6
History The xrdp service don’t start on one of my machines, if nic uses static ip address. I have seen the fault on another machine (different hardware) also, however it depends on how Ubuntu Server 16.10 is installed. I skip all details but I can say that I found out that IPv6 isn’t always available during start of services at boot. This let me find out that xrdp can only work if an IPv6 network (IPv6 address on the nic) is available. For me right now I have solved by start the xrdp a second time a few seconds later. However, if a user want to run without IPv6 it is problem.
Platform Ubuntu Server 16.10 amd64 (also Xubuntu 16.10 and Kubuntu 16.10)
Version xrdp 0.9.0~20160601+git703fedd-3, included in Ubuntu Server 16.10 (the proposal is done for the latest github code)
Fault The error messages at boot of system: xrdp-sesman [ERROR] bind error on port ‘3350’: 22 (Invalid argument) xrdp [ERROR] xrdp_listen_main_loop: listen error, possible port already in use
How to reproduce Boot the machine and check that xrdp-sesman and xrdp services is runnig (sudo lsof -i). Also look in the syslog. sudo systemctl stop xrdp Disable IPv6 on the machine. sudo systemctl start xrdp-sesman -> xrdp service will not start.
Details In function g_tcp_bind_flags, getaddrinfo() will find two addresses. An IPv4 and an IPv6. The IPv4 will failed to bind and the IPv6 will be ok to bind. However if the IPv6 isn’t available only IPv4 will be found and it will fail to bind. Source code file: common/os_calls.c
Disable IPv6 It is possible to disable IPv6 in several ways. Here are the two ways I have used: d6A) /etc/sysctl.conf: net.ipv6.conf.all.disable_ipv6 = 1 net.ipv6.conf.default.disable_ipv6 = 1 net.ipv6.conf.lo.disable_ipv6 = 1 sudo sysctl -p d6B) grub: Ubuntu: /etc/default/grub: GRUB_CMDLINE_LINUX_DEFAULT “ipv6.disable=1” ; sudo update-grub CentOS: /etc/default/grub: GRUB_CMDLINE_LINUX “ipv6.disable=1” ; sudo grub2-mkconfig -o /boot/grub2/grub.cfg
Progress I believe both IPv4 and IPv6 should be supported, It is an automatic mapping available. The socket should be AF_INET6 and use flag AI_V4MAPPED. Need to remove flag AI_ADDRCONFIG. This work, however if the IPv6 loopback interface are disabled, it fails on the loopback port. I needed to make a special handling of this. By making a bind to “::FFFF:127.0.0.1”. I had a proposal, but… Now I discover something in the latest source code (github); Fixes #432, commit 849a807 “If IPv6 not supported, fall back to IPv4”. This doesn’t solve the issue I have seen. After reading more, I found that this was for CentOS. I install a virtual machine and tested. Yes - difference. However this was because IPv6 was disabled in difference ways. If I disable IPv6 in the same way (via grub) in Ubuntu, I got the same behaviour. I have done a lot of testing with getaddrinfo and bind. This with small test applications. I’m not satisfied with how getaddrinfo works. For example using AI_V4MAPPED | AI_ADDRCONFIG | AI_PASSIVE and AF_UNSPEC, will return IPv4 before IPv6 net. I realise that it seems to be best to have special handling of loopback and any. This without getaddrinfo. Check the string and if loopback, try bind in6addr_loopback (AF_INET6) and if fail bind to INADDR_LOOPBACK (AF_INET). When the same for any. Check the string and if any, try bind in6addr_any (AF_INET6) and if fail bind to INADDR_ANY (AF_INET). Now we need to take care of other addresses. I believe the purpose of this is to be able to let xrdp only listen on a specific interface/network (nic). Today this is handle by inet_pton (AF_INET) and inet_pton (AF_INET6). However it cannot handle IPv6 addresses correctly. Nowadays the scope id needs to be specified in an IPv6 address, inet_pton cannot handle it (so bind will fail). Here I see little strange in the code, getaddrinfo is used to find networks and when the address is created in different ways. I realize that this functionality is what getaddrinfo is aimed to do. It can also handle scope id (interface name), added to the end of an IPv6 address with a percent sign. It is actually possible to use getaddrinfo for both IPv4 and IPv6 address strings. It works as supposed to, at least the combinations I haved tested. However I haven’t found a nice way to implement it, because it is a problem with the today design. The call to socket is done in g_tcp_socket, which is called before call to g_tcp_bind and g_tcp_bind_address (which uses getaddrinfo). However getaddrinfo needs to be called first and when socket, since it’s only after getaddrinfo we know for which family the socket should be created. I see no other nice option than to force a change outside of this source file. Now I realize these functions are used from many places. Okay, so here I start to think more about if it would be possible to solve without making changes outside the os_calls.c file. I come up with a proposal! So here I present a proposal that keep the interface to the os_calls intact. During first verification I found out that g_tcp_connect also needed to be modified. First I did a small change, if failed a call to getaddrinfo(“::FFFF:127.0.0.1”). This was ok for d6A, however not for d6B. Here it goes strange, well I was confused and tested a lot. Let short it to my end result. We need be able to do 3 different connect calls. The connect is done non-blocking, so connect can return EINPROGRESS. It isn’t specified how this function shall do, however at error it is called again and again. So it probably shouldn’t wait. After an in-progress the next call to connect will be ok, this no matter of arguments to the connect call. Now I also guess that the reason for the EISCONN thing in the code, is for this second call (on some OS). At final verification it shows that Xubuntu 16.04 behave differently, so I needed to change the connect call and have all 3 alternatives in the new function.
Proposal When bind to loopback or any, try IPv6, IPv4 and IPv4 mapped (this also apply to the connect call). For other addresses, use getaddrinfo to create the socket address and bind to it. API functions changed: g_tcp_bind g_tcp_bind_address g_tcp_connect Local functions removed: address_match g_tcp_bind_flags Local functions added: bind_loopback getaddrinfo_bind connect_loopback
Extra comments
a) The list from getaddrinfo wasn’t freed.
b) In function g_tcp_socket I think it is some strange code. The IPV6_V6ONLY option is read out and only if it isn’t zero, it is written. So for example if define XRDP_ENABLE_IPV6ONLY the option is only written to 1 if it is already 1. I think this isn’t the wanted behavior.
c) It was a bug in g_tcp_connect that could result in infinite loop, the row “rp = h” within the for loop.
d) Please remove trailing white space in source code files.
e) g_tcp_connect is only fixed for the loopback address.
f) While do IPv6 connection from rdesktop, the local interface (nic name) needs to be given in the address, i.e. x::x:x:x:x%<interface>.
My build-A $ sudo apt install devscripts dpkg-dev $ sudo apt build-dep xrdp $ apt source xrdp $ cd <PackageFolder> Edit os_calls.c (merged my new stuff and fix432 into the old file) $ debuild -us -uc -b
My build-B $ sudo apt install gcc make autoconf automake libtool pkgconf nasm git $ sudo apt install xserver-xorg-dev libssl-dev libpam0g-dev libxfixes-dev libxrandr-dev $ git clone --recursive https://github.com/neutrinolabs/xrdp $ cd xrdp $ ./bootstrap $ ./configure --enable-ipv6 $ patch -p1 -i MyPatch.patch $ make $ sudo make install $ cd … $ git clone --recursive https://github.com/neutrinolabs/xorgxrdp $ cd xorgxrdp $ ./bootstrap $ ./configure $ make $ sudo make install $ sudo ln -s /usr/local/sbin/xrdp /usr/sbin $ sudo ln -s /usr/local/sbin/xrdp-sesman /usr/sbin $ sudo systemctl enable xrdp $ reboot
Test plan Check points for each configuration:
- Execute “sudo lsof -i” and check that ports are active (3389 and 3350).
- Check log file (/var/log/syslog)
- Do a rdp connection from a client computer and login.
Configurations to test:
- Normal (default) installation of Ubuntu 16.10 (please check that you also can connect with the IPv6 address)
- Disable IPv6 the d6A way
- Disable IPv6 the d6B way
- Back to normal (with IPv6). Specify an “address” in the xrdp.ini file. Use the nic’s IPv4 address. Check so connect with IPv4 address works, but not with IPv6.
- Check so connection with IPv4 address works with disabled IPv6, both d6A and d6B. After this enable IPv6.
- Change the “address” to the nic’s IPv6 address (don’t forget to end with
%<interface>). - Remove “address” value. Add a second nic (in my case, an USB connected network card). Check that it is possible to connect via both networks.
- Specify the IPv4 address to the second nic in xrdp.ini. Check that it is possible to connect via that network, but not via the first nic’s network.
Verification
- I have tested on a Ubuntu Server 16.10 (with xfce4) machine. I installed xrdp via apt, i.e. the version via Ubuntu. Then I replaced the libcommon.so.0.0.0 file with the one I have built (see build-A). I execute according to Test plan above. All 8+ configurations worked OK. During this I had extra debug log messages in the code, to track my new stuff.
- I also made a new installation of Xubuntu 16.04 (without xrdp). Build and install xrdp, see my build-B above. Executed the check points in the test plan. This also for d6A and d6B. Result OK
- Fresh Xubuntu and build-B, but without “–enable-ipv6”, instead “–disable-ipv6”. This I tested with normal network configuration in Xubuntu, i.e. IPv6 available and also with d6A and d6B. Result OK
Disclaimer I haven’t tested other build configurations or operating systems.
Patch file xrdp20170327proposal.patch.txt
Comments are welcome!
PS. If this is to satisfaction, I can push to git. However I’m new here on github, so please bare with me if I ask details on how I should do.
Best regards Michael
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Comments: 18 (13 by maintainers)
Commits related to this issue
- Fix to handle OS disabled IPv6, issue #714. - Changes made only in the os_calls.c file. - Exported functions changed: g_tcp_bind g_tcp_bind_address g_tcp_connect - Support three network configurations... — committed to MichaelSweden/xrdp by MichaelSweden 7 years ago
- Removed error message while falling back to IPv4 (issue #714) — committed to MichaelSweden/xrdp by MichaelSweden 7 years ago
- Fix to handle OS disabled IPv6, issue #714. - Changes made only in the os_calls.c file. - Exported functions changed: g_tcp_bind g_tcp_bind_address g_tcp_connect - Support three network configurations... — committed to neutrinolabs/xrdp by MichaelSweden 7 years ago
- Removed error message while falling back to IPv4 (issue #714) — committed to neutrinolabs/xrdp by MichaelSweden 7 years ago
This issue is not fixed in debian 9.1 (stretch) using the repo apt-get package for xrdp. I disabled ipv6 via the grub command line method as noted above, and xrdp will never accept connection. I enable ipv6 again, and xrdp accepts connection.
Basically what I encountered, now, is pretty much identical to the debian bug reported in the past… Debian Bug report logs - #864230
Guys, please fix this, this is a very visible and frustrating issue to keep bouncing around.
Okay, now it is visible above that I managed to create a pull request. My first!
I would like to point out that my proposal keeps the libcommon interface intact. Another solution would be to change the interface and use two functions like: g_tcp_socket_bind (if argument address is null, bind to any) and g_tcp_socket_connect (if argument address is null, bind to loopback). However that will be a bigger change.