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

Bug - SAM: I2Pd, Stream and Datagram forwarding fails on hosts having multiple interfaces (C++, Boost ASIO related) #1778

Open
diva-exchange opened this issue Jul 26, 2022 · 3 comments
Labels

Comments

@diva-exchange
Copy link

How to reproduce (to have multiple interfaces, like virtual interfaces and bridges on a system):

a) Docker container setup, see below "docker compose", as an example to reproduce the bug
b) create some SAM sessions just using 1..n other containers on the same docker network (called "network.local.testnet.diva.i2p" in the reproduction example below).

Problem/Bug:
Sending a "SESSION CREATE ... STYLE=DATAGRAM PORT=17468 HOST=172.19.72.21 ..." to SAM might (depending on the number of interfaces on the system [and it's enumeration]) lead to a wrong forward IP (like: it takes the gateway ip where the traffic is coming from, like 172.19.72.1 in this specific example, instead of the "real" local ip, which is 172.19.72.21 in this example). This is fatal, since it breaks the SAM communication.

Please note: I do not know much about C++... just able to read and +/- understand the code.

I believe that this line here is the problem:

auto addr = boost::asio::ip::address::from_string(params[SAM_PARAM_HOST], e);

BTW: same is true for STREAM FORWARD - but I really wonder where in the code, the "HOST" param gets processed during the STREAM FORWARD command. Method, see:

void SAMSocket::ProcessStreamForward (char * buf, size_t len)

I also believe that this is a generic problem of boost/ASIO on systems with many interfaces. I am not totally sure, but it might be worth looking at how boost/ASIO gets the "real" local IP.

Fact: on some systems the same setup works, on other systems it does not. Therefore I see the bug related to boost/ASIO and the way the network interfaces are handled.

Docker compose file to reproduce the bug:

services:
  i2p.http.local.testnet.diva.i2p:
    container_name: i2p.http.local.testnet.diva.i2p
    image: divax/i2p:latest
    restart: unless-stopped
    environment:
      ENABLE_SOCKSPROXY: 1
      ENABLE_SAM: 1
      BANDWIDTH: P
    volumes:
      - i2p.http.local.testnet.diva.i2p:/home/i2pd/data
    ports:
      - 127.19.72.11:7070:7070
    networks:
      network.local.testnet.diva.i2p:
        ipv4_address: 172.19.72.11

  i2p.udp.local.testnet.diva.i2p:
    container_name: i2p.udp.local.testnet.diva.i2p
    image: divax/i2p:latest
    restart: unless-stopped
    environment:
      ENABLE_SAM: 1
      BANDWIDTH: P
    volumes:
      - i2p.udp.local.testnet.diva.i2p:/home/i2pd/data
    ports:
      - 127.19.72.12:7070:7070
    networks:
      network.local.testnet.diva.i2p:
        ipv4_address: 172.19.72.12

networks:
  network.local.testnet.diva.i2p:
    name: network.local.testnet.diva.i2p
    ipam:
      driver: default
      config:
        - subnet: 172.19.72.0/24

volumes:
  i2p.http.local.testnet.diva.i2p:
    name: i2p.http.local.testnet.diva.i2p
  i2p.udp.local.testnet.diva.i2p:
    name: i2p.udp.local.testnet.diva.i2p
@orignal
Copy link
Contributor

orignal commented Jul 26, 2022

So, you have multiple local interface and forwarding fails?
But it's not about host param. I need to check if SAM even allows to bind to a specified address.
Otherwiise we should take it from "address", "address4" and "address6" params.

@diva-exchange
Copy link
Author

Sorry took a bit - had to enable debug logging (can be reproduced using the docker compose file above and some additional efforts to create SAM sessions).

Now, more analysis data from the debug logs, i2p.http.-container:

[...]
2022-07-27T06:49:23.964423704Z [27/Jul/2022:06:49:23 +0000]@174/debug - SAM: New connection from 172.19.72.1:49808
2022-07-27T06:49:23.964706489Z [27/Jul/2022:06:49:23 +0000]@174/debug - SAM: Handshake HELLO VERSION
2022-07-27T06:49:23.965393014Z [27/Jul/2022:06:49:23 +0000]@174/debug - SAM: New connection from 172.19.72.1:49810
2022-07-27T06:49:23.965468418Z [27/Jul/2022:06:49:23 +0000]@174/debug - SAM: Handshake HELLO VERSION
2022-07-27T06:49:23.965779078Z [27/Jul/2022:06:49:23 +0000]@174/debug - SAM: Session create: ID=e9zM2VkMP1av-qyg DESTINATION=xl...9e STYLE=STREAM
[...]
2022-07-27T06:49:43.675394759Z [27/Jul/2022:06:49:43 +0000]@174/debug - SAM: Stream forward: SILENT=true ID=dbQS8GrcoAKfx79R PORT=17468 HOST=172.19.72.21
2022-07-27T06:49:43.675406304Z [27/Jul/2022:06:49:43 +0000]@174/debug - SAMSocket::SendMessageReply, close=false reason: STREAM STATUS RESULT=OK
2022-07-27T06:49:43.966232095Z [27/Jul/2022:06:49:43 +0000]@174/debug - SAMSocket::SendMessageReply, close=false reason: SESSION STATUS RESULT=OK DESTINATION=xl...9e
[...]

and from the i2p.udp.-container

[...]
2022-07-27T06:49:43.676307760Z [27/Jul/2022:06:49:43 +0000]@249/debug - SAM: New connection from 172.19.72.1:39542
2022-07-27T06:49:43.676444067Z [27/Jul/2022:06:49:43 +0000]@249/debug - SAM: Handshake HELLO VERSION
2022-07-27T06:49:43.677418139Z [27/Jul/2022:06:49:43 +0000]@249/debug - SAM: Session create: ID=xm7BviC--bl07PhC DESTINATION=jh...-Q STYLE=DATAGRAM PORT=17470 HOST=172.19.72.21
[...]

Now, the output from the web console (like: curl "http://127.19.72.12:7070/?page=sam_session&sam_id=xm7BviC--bl07PhC" (sam_id see above, udp debug log):

[...]
<div class="listitem"><a href="/?page=local_destination&b32=t...a.b32.i2p</a></div>
<br>
<b>Streams:</b><br>
<div class="list">
<div class="listitem">session [172.19.72.1:39542]</div>
[...]

Same kind of console output for the http stream forward (curl "http://127.19.72.11:7070/?page=sam_session&sam_id=e9zM2VkMP1av-qyg", see http debug log for sam_id)

[...]
div class="listitem"><a href="/?page=local_destination&b32=7...a">7...a.b32.i2p</a></div>
<br>
<b>Streams:</b><br>
<div class="list">
<div class="listitem">session [172.19.72.1:49808]</div>
<div class="listitem">forward [172.19.72.1:49810]</div>
[...]

Result: IP is wrong, forwarding is broken.

Conclusion

So, the connection handshake sees the gateway IP. This is reasonable from a networking point of view. Then the session seems to store this IP as a "forward" IP. This is IMHO wrong in the case where a HOST param gets passed to either the SESSION CREATE or the STREAM FORWARD command.

Potential Fix

Remark: untested (only true for docker containers, and AFAIK only possible on Linux)
This fix does not depend on any code changes of I2Pd. Set within containers: sysctl net.ipv4.conf.all.forwarding=1

Some Suggestions and thoughts

IMHO the recognition of the HOST param has to be fixed sooner or later (see specs at https://geti2p.net/en/docs/api/samv3, section "SAM Virtual Streams : FORWARD" for streams and same is true for DATAGRAM). The "automatic" derivation of a forward IP from the session handshake goes wrong on some systems with a more complex network interface setup.

@diva-exchange
Copy link
Author

Update 1: "potential fix" has no effect (it's not related to ip forwarding at all).

Update 2: it is related to iptables though. Assume a rule on any given linux host system (example of an extract of "/etc/iptables/rules.v4"):
-A POSTROUTING -s 172.19.72.0/24 ! -o some-bridge-interface -j MASQUERADE
Such a rule is per-se not unreasonable. But it breaks I2P/SAM forwarding, since the traffic gets masqueraded. IMHO, the proper fix - so that I2P SAM works also in such situations - is to properly respect the given HOST param as a forward address (see "Suggestions" above, #1778 (comment)).

@r4sas r4sas added the SAM label Sep 22, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants