QuantumEntangledAndy / neolink

An RTSP bridge to Reolink IP cameras
GNU Affero General Public License v3.0
322 stars 44 forks source link

Enable local discovery across subnets when IP address known. #45

Closed dkerr64 closed 1 year ago

dkerr64 commented 1 year ago

Local discovery for cameras addressed by UID works by broadcasting to all networks that the neolink server device has direct connection to. I have isolated my cameras onto a separate VLAN / subnet, so when I run neolink from a server that is not connected to that VLAN then the camera must be discovered using remote Reolink servers... because broadcasts are not forwarded between subnets/VLANs by switch/router/bridges.

This can be optimized if the IP address of the camera is known. So, if the config file lists both a UID and a IP address then instead of broadcasting to all clients on the local network, just "broadcast" to the one known IP address (at the 2015/2018 ports if no port provided in the config file). This will forward through routers/bridges.

I tested this with a hardcoded hack in discovery.rs...

    let mut direct: Vec<(Ipv4Addr, u16)> = vec![(Ipv4Addr::new(192, 168, 21, 38), 2015), (Ipv4Addr::new(192, 168, 21, 38), 2018)];
    info!("Broadcasting to: {:?}", direct);
    Ok(direct
        .drain(..)
        .map(|(addr, port)| SocketAddr::new(addr.into(), port))
        .collect())

And ran it from a server that is NOT connected to 192.168.21.xx/24...

root@utils:/# ip -4 addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
28: eth0@if29: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link-netnsid 0
    inet 192.168.17.176/24 metric 100 brd 192.168.17.255 scope global dynamic eth0
       valid_lft 83474sec preferred_lft 83474sec
root@utils:/# ~/neolink2 image --config=/root/neolink2.toml --file-path=/mnt/tmp/cameras/temp6_hd.jpeg driveway
[2023-03-01T15:58:22Z INFO  neolink] Neolink 0.5.4 (unknown commit) debug
[2023-03-01T15:58:22Z INFO  neolink::utils] driveway: Connecting to camera at UID: <redacted>
[2023-03-01T15:58:22Z INFO  neolink_core::bc_protocol::connection::discovery] Broadcasting to: [(192.168.21.38, 2015), (192.168.21.38, 2018)]
[2023-03-01T15:58:22Z INFO  neolink_core::bc_protocol] Local discovery success <redacted> at 192.168.21.38:38438
[2023-03-01T15:58:22Z INFO  neolink::utils] driveway: Logging in
[2023-03-01T15:58:23Z INFO  neolink::utils] driveway: Connected and logged in

This is far more efficient than reaching out to a remote Reolink server, which is what is required when camera is not on the same subnet as the server..

Thanks David

QuantumEntangledAndy commented 1 year ago

Sounds like a good idea I'll try and work it up

QuantumEntangledAndy commented 1 year ago

Could you test #46 with this feature?

dkerr64 commented 1 year ago

What is the right way to set this in the config file? I am getting a validation error when I have both UID and address..

uid = "<redacted>"
address = "192.168.21.38"
root@utils:~# RUST_LOG=debug ~/neolink2 image --config=/root/neolink2.toml --file-path=/mnt/tmp/cameras/temp6_hd.jpeg driveway
[2023-03-03T03:00:44Z INFO  neolink] Neolink 0.5.5 (unknown commit) release
Error: Failed to validate the "/root/neolink2.toml" config file

Caused by:
    ValidationErrors({"cameras": List({0: ValidationErrors({"__all__": Field([ValidationError { code: "Must provide either camera address or uid not both", message: None, params: {} }])})})})
root@utils:~#

Did I configure it wrong?

Thanks David

QuantumEntangledAndy commented 1 year ago

Ah let me fix that

QuantumEntangledAndy commented 1 year ago

Ok should be pushed

dkerr64 commented 1 year ago

Yup, I did the same myself. That gets things sort-of working. If I do not specify a port number then I get this error...

root@utils:~# RUST_LOG=debug ~/neolink2 image --config=/root/neolink2.toml --file-path=/mnt/tmp/cameras/temp6_hd.jpeg driveway
[2023-03-03T03:11:32Z INFO  neolink] Neolink 0.5.5 (unknown commit) release
[2023-03-03T03:11:32Z INFO  neolink::utils] driveway: Connecting to camera at Address: 192.168.21.38, UID: <redacted>
Error: Failed to connect to the camera, check credentials and network

Caused by:
    0: Failed to connect to camera driveway at Address: 192.168.21.38, UID: <redacted> on channel 0
    1: IO Error: Error { kind: InvalidInput, message: "invalid socket address" }
    2: invalid socket address
root@utils:~#

But If I change the config file to this...

uid = "<redacted>"
address = "192.168.21.38:2015"

Then it works...

root@utils:~# RUST_LOG=debug ~/neolink2 image --config=/root/neolink2.toml --file-path=/mnt/tmp/cameras/temp6_hd.jpeg driveway
[2023-03-03T03:12:46Z INFO  neolink] Neolink 0.5.5 (unknown commit) release
[2023-03-03T03:12:46Z INFO  neolink::utils] driveway: Connecting to camera at Address: 192.168.21.38:2015, UID: <redacted>
[2023-03-03T03:12:46Z DEBUG neolink_core::bc_protocol::connection::discovery] Broadcasting to: [(255.255.255.255, 2015), (255.255.255.255, 2018), (192.168.17.255, 2015), (192.168.17.255, 2018)]
[2023-03-03T03:12:48Z DEBUG neolink_core::bcudp::xml] Struct: start to parse "P2P"
[2023-03-03T03:12:48Z DEBUG yaserde::de] Fetched StartElement(P2P, {"": "", "xml": .
.
. deleted bunch of debug
.
[2023-03-03T03:12:48Z DEBUG yaserde::de] Fetched EndElement(did)
[2023-03-03T03:12:48Z DEBUG yaserde::de] Fetched EndElement(D2C_C_R)
[2023-03-03T03:12:48Z INFO  neolink_core::bc_protocol] Local discovery success <redacted> at 192.168.21.38:54576
[2023-03-03T03:12:48Z INFO  neolink::utils] driveway: Logging in

I tried port 2018 and that failed. But I was not expecting to need to specify port number... neolink should append both like it does for broadcast. And I would expect to see the "broadcasting to" message to include the specified address.

Thanks David

dkerr64 commented 1 year ago

Do allow for a port to be specified, but if none specified then test with both 2015 and 2018 like it does now.

dkerr64 commented 1 year ago

Or maybe if an address is specified than it shouldn't even try broadcasting to 255.255.255.255 and x.y.z.255

QuantumEntangledAndy commented 1 year ago

I think I should do a refractor so it does:

Going to be alot of work to restructure the code to that though

dkerr64 commented 1 year ago

In that case I suggest leaving it as it is. Let's just document the known port numbers and advise user to try each until one [hopefully] works.

I think deploying neolink on a different subnet from the cameras is probably rare and a niche case. I'm grateful you got it working for me. But let's not go over the top. If an environment has multiple subnets, then the user should be technically competent enough to know about port numbers too.

Thanks David

QuantumEntangledAndy commented 1 year ago

Perhaps but I did it anyways. It now takes an address and a UID. Tests standard TCP in that port while also if a UID is given does discovery on the given ip/port while also trying the known common ports. I also made it so that it prioritises the more local connections rather than which ever comes first so, TCP, Local, Remote, Map, Relay are preferred in that order. Should be live on feature/UidAddr

dkerr64 commented 1 year ago

Okay, I tested and it works provided you do not specify a port number in the config. With a port number it fails.

root@utils:~#
root@utils:~# RUST_LOG=debug ~/neolink2 image --config=/root/neolink2.toml --file-path=/mnt/tmp/cameras/temp6_hd.jpeg driveway
[2023-03-07T08:07:14Z INFO  neolink] Neolink 0.5.5 (unknown commit) release
[2023-03-07T08:07:14Z INFO  neolink::utils] driveway: Connecting to camera at Address: 192.168.21.38:2015, UID: <redacted>
[2023-03-07T08:07:14Z DEBUG neolink_core::bc_protocol::connection::discovery] Broadcasting to: [(255.255.255.255, 2015), (255.255.255.255, 2018), (192.168.17.255, 2015), (192.168.17.255, 2018)]
Error: Failed to connect to the camera, check credentials and network

Caused by:
    0: Failed to connect to camera driveway at Address: 192.168.21.38:2015, UID: <redacted> on channel 0
    1: Cannot contact camera at given address
root@utils:~#

That is with discovery set to local. If I set it to "map" then there is a whole lot of communication back-and-forth with the camera but it still ends with above message.

QuantumEntangledAndy commented 1 year ago

Then something is not right since it is not supposed to matter if you specify the port or not... Hmm

QuantumEntangledAndy commented 1 year ago

Closing this as the feature has been added (and I fixed that port thing)