jamulussoftware / jamulus

Jamulus enables musicians to perform real-time jam sessions over the internet.
https://jamulus.io
Other
997 stars 222 forks source link

The central server presents internal IP #888

Closed genesisproject2020 closed 3 years ago

genesisproject2020 commented 3 years ago

I'm trying to run my own central server together with a few local servers reporting to my central server to make it easy for a group to have "break out sessions". All servers exist locally with local ip addresses.

Problem... With the current configuration alternatives the central server presents its public IP correct the outside but the servers registered will be presented with its internal ip to the outside. In a standard senario a server will register it self to a central server elsewhere and the public will be correctly presented. When building everything in a local environment and then publish it to the world the current options will not work.

Request... Be able to send the IP (or url) that the server should present to be able to build local environments. Each server will require a unique port when the servers use the same external IP.

gene96817 commented 3 years ago

There is some information missing in this scenario. How are the internal servers addressed from the Internet? (Meaning how are you managing the router, NAT, and firewall functions?) How are the addresses and port mapping determined? This is a scenario where using domain names for each server can help. For the described scenario to be stable, you would have to manually manage your (internal) IP addresses. For long-term convenience, it is best to (find a solution) using DNS and DHCP.

genesisproject2020 commented 3 years ago

One basic example and use case:

Internet - Firewall with NAT - an internal server "real ip" - port mapping - internal server with a few Jamulus servers running on different ports 193.x.x.x - port 23000 - 10.0.0.10:23000 (jamulus central server) 193.x.x.x - port 23001 - 10.0.0.10:23001 (jamulus server registered --centalserver 10.0.0.10:23000) 193.x.x.x - port 23002 - 10.0.0.10:23002 (jamulus server registered --centalserver 10.0.0.10:23000) 193.x.x.x - port 23003 - 10.0.0.10:23003 (jamulus server registered --centalserver 10.0.0.10:23000)

The server in the example above the central server will present it self with ip 193.x.x.x and the rest will be presented with 10.0.0.10. There is no need for a dns name (but it makes it easier for the users). Everything is manually handled but I do not see that as a problem for most people.

gene96817 commented 3 years ago

1- what is your public IP address? 2- How do you want clients on the public network to address these internal addresses? You need to create a mapping between your public IP:port to internal IP:port for every portl in your internal network. 3- for clients on the local network, do you want your users to use those internal (numerical) addresses? You can use host names if you enter the host name/IP address mapping in each computer's host table.

DNS was invented (long ago) to eliminate the need of managing all those host tables.

genesisproject2020 commented 3 years ago

1) A public IPv4 address reachable from the world. And yes, DNS can be used to make it easier to remember. 2) As described above, with port mapping, as most private servers are configured. I do not see any problem to configure a few port mappings. 3) The internal network is irrelevant in this scenario. But, all servers can be addressed directly and an internal DNS/DHCP can make it easier.

I have a hard time to see the relevance in the questions above connected to the addresses the central server presents to the clients.

My request is to be able to configure the IP address the server presents to the clients.

gene96817 commented 3 years ago

You have more than one server in your internal network. They connect to the public network through a router/gateway using NAT. If you want an external client to contact an internal server, the messages have to be addressed to the gateway IP:port that is mapped to the internal server internal-IP:port. This is easy to do with one internal server and hard (impossible?) to do with multiple internal servers. you don't have a problem if all the users are on your internal network.

genesisproject2020 commented 3 years ago

There is no problem to address more servers on an internal network through just one public IP with port mapping. A server can use whatever port available and present it to the client through a central server. If you want more servers just use different ports to all servers and you will be able to address them all. As I wrote, internal addressing is irrelevant. Only external access where the clients connect from Internet is in focus. (If it was an only internal environment the current configuration options will work perfectly.)

gene96817 commented 3 years ago

I suggest you build your port mapping table. If I understand your scenario, the table will be very difficult to specify. I'd love to see a table that solves your needs.

genesisproject2020 commented 3 years ago

Port mapping is not the problem so please focus on the question to be able to choose the IP that's presented to the client from the central server list.

hoffie commented 3 years ago

@genesisproject2020: I quickly put something together, is this what you want?

https://github.com/hoffie/jamulus/commit/8e8ed739c02e8d6d9ffad42c2632a2dcc62bf59b

gene96817 commented 3 years ago

The behavior I have seen is the central server reports the gateway public IP address as the server address. If you have three servers on your private LAN, I expect the central server will report the same IP address for all three servers. The reported port numbers for each server will be different.

hoffie commented 3 years ago

A quick scan through the code looks to me as follows:

So, the current behavior is:

Possible solutions:

  1. Try forcing the central server registration via your public IP. I don't know if this commonly works or can even work as this would require SNAT + DNAT for the same packet. That would be up to you to try @genesisproject2020.
  2. Try local firewall tricks. On Linux, you could SNAT the traffic flowing from your internal slave IP/port to the internal central server by forcing the source address to be your public IP. You would then also have to DNAT traffic flowing from your central server to your slave server. That would be up to you to try @genesisproject2020.
  3. Adjust Jamulus to support such behavior. You can try the diff I linked to, but I suspect that it will not work because it triggers the wrong logic in the central server. A solution which would fix this Jamulus-side would be a modification of the central server code so that it allows handing out public IPs in this case. That would be a decision to be made by @corrados if this use case should be supported. If it should be, I can try my luck for a PR again.

All in all, this sounds like a legitimate issue/request (although maybe a bit niche until now).

hoffie commented 3 years ago

@genesisproject2020 In case you want to try, I have updated my branch with the necessary change to the server list logic (i.e. both your central server and your slaves would need to run with this code base; clients are unchanged).

Note: This is completely untested, I only know that it compiles.

genesisproject2020 commented 3 years ago

@hoffie Compilation worked but the server module do not recognize the option (--serverexternalip).

hoffie commented 3 years ago

@hoffie Compilation worked but the server module do not recognize the option (--serverexternalip).

Hrm, that's strange as that works for me. Can you share how you start your test server (full command line)? Can you also share the output of Jamulus --version? I suspect that the built may have been done without the new code somehow? The Github UI may be a bit misleading. I linked to my branch, but when you select Code > Clone, you will be given the URL for the whole repo. This means, after cloning you will still have to switch branches (git checkout override-external-ip) and run the build afterwards.

genesisproject2020 commented 3 years ago

@hoffie Thanks! I did a misstake with the config.

The version I use now is: Version 3.6.2dev-5b7a8daa-dirty Command server 1 (ip 10.1.1.10): Jamulus -s -n -p 10000 -e localhost Command server 2 (ip 10.1.1.11): Jamulus --serverexternalip xx.xx.xx.xx -s -n -p 10001 -e 10.1.1.10:10000

Server 1 message: jamulus[2015417]: Requested to register entry for 10.1.1.11:10001 (xx.xx.xx.xx:10001): Server 2

But.. the central server still presents the internal ip. I use https://explorer.jamulus.io to check what's presented to the world. If I use a client and connect to the central server I only see the central server and no other server. I think the reason is the presentation of a server with internal ip is not reachable from Internet.

There is no difference if I add --serverexternalip to the central server. The presentation is correct from the beginning so it should not be of any use.

Have I missed anything?

hoffie commented 3 years ago

Have I missed anything?

Hrm, maybe I have missed adding an important detail: The central server modification currently depends on a Qt function which is only available in rather recent versions (5.11 or newer). Which version of Qt are you using (check with rpm -qa | grep, dpkg -l or something)?

If this is the problem, I can try modifying the code to work without that function.

genesisproject2020 commented 3 years ago

The Ubuntu 20.04 LTS that I use for testing has Qt 5.12.8 installed. Maybe I can downgrade to 5.11.

hoffie commented 3 years ago

The Ubuntu 20.04 LTS that I use for testing has Qt 5.12.8 installed. Maybe I can downgrade to 5.11.

This should not be necessary, anything newer than 5.11 should be fine. I'll look into the issue again with your Jamulus Explorer example. Maybe I still have a logic mistake somewhere.

hoffie commented 3 years ago

@genesisproject2020 I just tried again:

$ Jamulus -s -n -p 44124 -e localhost
$ Jamulus -s -n -p 10001 -e 127.0.0.1:44124 --serverexternalip 1.2.3.4

Pointing Jamulus Explorer's central server address to myhost:44124 shows the slave server as 1.2.3.4.

My only idea would be to confirm once again that also your central server runs the new build (Jamulus Explorer should say: 3.6.2dev-5b7a8daa) and that it was built on a system with Qt 5.11 or newer.

I wrote you on Telegram, maybe we can figure it out there and add the results here.

hoffie commented 3 years ago

After some back and forth with debugging code, I think I found the issue. I assumed QHostAddr::isGlobal() would return false for private networks such as 10.1.2.0/24. It doesn't. It worked in my test because I tested with both the central server and the slave server on the same machine. Therefore I used 127.0.0.1 which did behave as expected in isGlobal().

I've now replaced the isGlobal() usage by a custom helper function which explicitly lists the RFC-defined private networks. This should be a better fit for this use case and also helps with Qt compatability.

@genesisproject2020 Can you try with the latest code again? Version should be 3.6.2dev-e2b43e55 after make clean && qmake ... && make

genesisproject2020 commented 3 years ago

It seams to work perfectly. I have to do some more testing and connect clients to be sure.

genesisproject2020 commented 3 years ago

Works great!!

sgraf812 commented 3 years ago

Thank you, I was extremely happy to find that this has already been solved. It landed as --serverpublicip in 3.7.