arriven / db1000n

MIT License
1.18k stars 208 forks source link

-local-address flag does not work #526

Closed deputinizer closed 2 years ago

deputinizer commented 2 years ago

Expected Behavior

I wanted to run program only on interface/device wlan1 from Raspberry Pi 2B. HTTP Requests should be sent just like in curl:

$ curl --interface wlan1 'https://api.ipify.org'
1.2.3.4

Similar problem fixed in Rust: https://github.com/hyperium/hyper/pull/2823

Actual Behavior

Running: ./db1000n -local-address wlan1 sends requests over eth0

$ vnstat -l -i eth0
Monitoring eth0...  

   rx:     1.26 Mbit/s   214 p/s          tx:   220.41 kbit/s   324 p/s

$ vnstat -l -i wlan1
Monitoring wlan1... 

   rx:         0 bit/s     0 p/s          tx:         0 bit/s     0 p/s

Also, choosing address given by DHCP on wlan1 does not solve the problem. Note: On raspberry pi 1 choosing IP of wlan1 completely disables the program [0 bit/s].

Steps to Reproduce the Problem

  1. Find interface IPs with ip a s
  2. Run ./db1000n -local-address 10.2.2.2
  3. Observe network interfaces with nload or vnstat -i wlan1

Specifications

deputinizer commented 2 years ago

Looks like Golang is just a lot more retarded than Rust... https://github.com/golang/go/issues/39293

I can't believe they didn't add this functionality for so many years.

There should be also a field for device/interface https://cs.opensource.google/go/go/+/refs/tags/go1.18.1:src/net/dial.go;l=52

arriven commented 2 years ago

LocalAddress is called that way because it expects an address, not an interface name. Golang has features that would allow working with interface names (it would just fetch interface address under the hood) but I feel like exposing address gives user more control (i.e. an interface can have multiple addresses attached, especially in case of ipv6)

arriven commented 2 years ago

Ah, wait, I see that you're using IP in repro steps, can you clarify that it's not working that way as your actual behavior showcases usage with interface name?

deputinizer commented 2 years ago

I feel like exposing address gives user more control (i.e. an interface can have multiple addresses attached, especially in case of ipv6)

Well, DHCP is a ducktape. Local IP changes on WiFi. Especially on public networks.

can you clarify [...]

[Spoiler] Raspberry pi 1B froze... Let's use raspberry pi 1B with USB wlan0 adapter. ```console pi@pi1:~/db1000n $ ip a s 105: eth0: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 inet 192.168.0.157/24 brd 192.168.0.255 scope global dynamic noprefixroute eth0 106: wlan0: mtu 1500 qdisc mq state UP group default qlen 1000 inet 10.2.2.3/16 brd 10.2.255.255 scope global dynamic noprefixroute wlan0 pi@pi1:~/db1000n $ ./db1000n -local-address 10.2.2.3 ... pi@pi1:~ $ vnstat -l Monitoring eth0... (press CTRL-C to stop) rx: 0 kbit/s 0 p/s tx: 209 kbit/s 303 p/s^C pi@pi1:~ $ vnstat -l -i wlan0 Monitoring wlan0... (press CTRL-C to stop) rx: 0 bit/s 0 p/s tx: 0 bit/s 0 p/s pi@pi1:~ $ then raspberry pi 1B froze because of 512 MB ram... ```
[Spoiler] Raspberry pi 2B Let's use raspberry pi 2B with USB wlan1 adapter. (wlan0 is built-in) ```console pi@pi2:~/db1000n $ ip a s 2: eth0: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 inet 192.168.0.66/24 brd 192.168.0.255 scope global dynamic noprefixroute eth0 4: wlan1: mtu 1500 qdisc mq state UP group default qlen 1000 inet 10.2.2.2/16 brd 10.2.255.255 scope global dynamic noprefixroute wlan1 pi@pi2:~/db1000n $ ./db1000n -local-address 10.2.2.2 ... pi@pi2:~ $ vnstat -l Monitoring eth0... (press CTRL-C to stop) rx: 560 bit/s 1 p/s tx: 59.92 kbit/s 87 p/s pi@pi2:~ $ vnstat -l -i wlan1 Monitoring wlan1... (press CTRL-C to stop) rx: 0 bit/s 0 p/s tx: 0 bit/s 0 p/s ```

Note: CURL does not work either [on both PI's] with local IP address. I had to specify --interface wlan0

deputinizer commented 2 years ago

Should work in go:


    dialer := &net.Dialer{
        Control: func(network, address string, conn syscall.RawConn) error {
            var operr error
            if err := conn.Control(func(fd uintptr) {
                operr = syscall.BindToDevice(fd, "wlan1")
            }); err != nil {
                return err
            }
            return operr
        },
    }
deputinizer commented 2 years ago

Temprorary solution

while true; do sudo route del default gw 192.168.0.1 eth0; sleep 5; done
deputinizer commented 2 years ago

Ok, another thing. How to limit resources on raspberry pi 1B ?

In stoppropaganda, i've already optimized fasthttp so it doesn't store response bytes in memory. https://github.com/deputinizer/fasthttp/commits/master https://github.com/erkexzcx/stoppropaganda/pull/103#issuecomment-1059579413

deputinizer commented 2 years ago

Woooow thanks! https://github.com/Arriven/db1000n/commit/a7edd0071ee129712fe3ebdf643a75e24c3f1a0a

deputinizer commented 2 years ago

Nice. It actually works.

pi@pi2:~ $ ./db1000n -interface wlan1
...
pi@pi2:~ $ vnstat -l -i wlan1
Monitoring wlan1...

   rx:     3.24 Mbit/s   296 p/s          tx:   165.98 kbit/s   221 p/s
arriven commented 2 years ago

I'm a bit sad that it only works for linux as other systems don't have that particular syscall. I feel like there should be a way to hack around that by dynamically querying for the ip address of an interface in that particular function but I don't have time to check that theory yet

As for limiting the resource usage you can try using --min-interval which would introduce artificial rate limiting to the app. It's a bit hacky way but for historical reasons it works that way