Connecting to Multiple cameras which each acts as a WiFi Access Point [closed]

Presentation

This problem could be solved by using network namespaces, thus splitting the single Pi system into X routers doing NAT. That's what should be done. Alas I don't know how to write an answer that would not only have to include how to move the Wifi interfaces to a new network namespace (requires compatible drivers and iw phy phyX set netns ... instead of ip link set wlanX ... netns ...) but also especially their associated wpa_supplicant and DHCP client daemons with corresponding system integration tweaks. This requires good knowledge on how the specific system is configured with wireless and DHCP.

Instead this answer avoids using network namespaces and avoids having to reconfigure the handling of wpa_supplicant and the DHCP client: it uses policy routing.

Involving marks in policy routing is unavoidable for this case where the routing stack will only see destination port 443 rather than 4431/4432 (DNAT already changed it before). Marks will also be user to set conntrack (reply) zones to be sure to handle the case where multiple cameras assign the same IP address to their matching host interface.

Strict Reverse Path Forwarding (SRPF) will have to be relaxed to Loose RPF in case Strict was set by default, because ARP handling wont't receive the marks and can stay blocked.

As the cameras might not have set their default route to the client but might only have a LAN route, double NAT (source and destination) will also be done.

Setup

As OP didn't provide any eth0 setting, there's none here. The answer tries to not depend too much on this, but OP would have to adapt it if needed (especially if the 7 wireless interfaces won't all have their name starting with wlx).

Let's adjust the goal:

192.168.42.0/24 represents multiple overlapping IP networks that must not be reached directly to protect the outside clients from all possible routing complexity.

So the address 192.168.42.250 won't be used. Pi's visible address 192.168.0.205 will be used from the remote clients.

Any HTTPS access will get a bad certificate. Deal with it: I'm providing an answer based on tweaking network settings, but it won't include the setup of a reverse proxy to hide the certificate issue. Adding such reverse proxy also requires more network tweaks (added as an option at the end). Tests can be done from a client (but not the RPi) with:

curl --insecure https://192.168.0.205:4431/
curl --insecure https://192.168.0.205:4432/

Also to show some examples below, I took the case with both cameras having assigned the same IP address to the host so it appears on two interfaces, to raise the bar. It doesn't matter. Most of these settings must be done after all wireless connections are done rather than before. I won't deal on how to integrate this with the specific Linux OS handling it.

All commands should be run as root.

Examples are based on:

# ip route
default via 192.168.0.1 dev wlan0 
192.168.0.0/24 dev wlan0 proto kernel scope link src 192.168.0.205 
192.168.42.0/24 dev wlxb8b7f16a0602 proto kernel scope link src 192.168.42.10 metric 600 
192.168.42.0/24 dev wlxb8b7f16a04cd proto kernel scope link src 192.168.42.10 metric 600 

Routing rules to select additional routing tables that will isolate duplicate each camera LAN from the other camera LAN:

ip rule add fwmark 1 lookup 4431
ip rule add fwmark 2 lookup 4432

ip route add 192.168.42.0/24 dev wlxb8b7f16a0602 table 4431
ip route add 192.168.42.0/24 dev wlxb8b7f16a04cd table 4432

This makes routing work from client to cameras (if the RPi doesn't have a default route, examples below using 192.0.2.2 to test routes could use 192.168.0.101 instead):

# ip route get mark 2 from 192.0.2.2 iif wlan0 192.168.42.1
192.168.42.1 from 192.0.2.2 dev wlxb8b7f16a04cd table 4432 mark 2 
    cache iif wlan0 

but still not for replies if SRPF is enabled:

# ip route get mark 2 from 192.168.42.1 iif wlxb8b7f16a04cd 192.0.2.2
RTNETLINK answers: Invalid cross-device link

So an almost undocumented flag would have to be set on the camera interfaces:

sysctl -w net.ipv4.conf.wlxb8b7f16a0602.src_valid_mark=1
sysctl -w net.ipv4.conf.wlxb8b7f16a04cd.src_valid_mark=1

to get now:

# ip route get mark 2 from 192.168.42.1 iif wlxb8b7f16a04cd 192.0.2.2
192.0.2.2 from 192.168.42.1 via 192.168.0.1 dev wlan0 mark 2 
    cache iif wlxb8b7f16a04cd 

But actually, anyway ARP (from camera to host) is still disrupted when SRPF is set because ARP doesn't get iptables' marks.

So just use Loose RPF (rp_filter=2) instead (and then the setting for src_valid_mark above is not needed anymore) to solve it. This works whether RPF was disabled or set strict before:

sysctl -w net.ipv4.conf.wlxb8b7f16a0602.rp_filter=2
sysctl -w net.ipv4.conf.wlxb8b7f16a04cd.rp_filter=2

Add iptables rules setting marks to complete the routing part as well as deal with conflicting addresses in NAT later by setting the reply zone selector.

iptables -t raw -A PREROUTING ! -i wlx+ -p tcp --dport 4431 -j MARK --set-mark 1
iptables -t raw -A PREROUTING -i wlxb8b7f16a0602 -j MARK --set-mark 1
iptables -t raw -A PREROUTING -m mark --mark 1 -j CT --zone-reply 1

iptables -t raw -A PREROUTING ! -i wlx+ -p tcp --dport 4432 -j MARK --set-mark 2
iptables -t raw -A PREROUTING -i wlxb8b7f16a04cd -j MARK --set-mark 2
iptables -t raw -A PREROUTING -m mark --mark 2 -j CT --zone-reply 2

Add those rules to NAT to the target IP and port (which are always the same). The correct interface will then be chosen by the routing stack thanks to previous settings:

iptables -t nat -A PREROUTING ! -i wlx+ -p tcp -m mark ! --mark 0 -j DNAT --to-destination 192.168.42.1:443
iptables -t nat -A POSTROUTING -o wlx+ -j MASQUERADE

Should a 3rd interface called wlx3 be added, here are the steps. It can be generalized the same way for more:

Add a new ip rule selected with a new mark (3) that will use a new routing table (4433):

ip rule add fwmark 3 lookup 4433

Add the matching new routing table with its entry more or less duplicated from the main table LAN's route for the new interface:

ip route add 192.168.42.0/24 dev wlx3 table 4433

Loosen RPF on this interface if OS' defaults were SRPF (as told src_valid_mark isn't needed in the end):

sysctl -w net.ipv4.conf.wlx3.rp_filter=2

Choose a new port (4433) and add 3 new raw/PREROUTING iptables rules that include the new port, the new mark and the new interface:

iptables -t raw -A PREROUTING ! -i wlx+ -p tcp --dport 4433 -j MARK --set-mark 3
iptables -t raw -A PREROUTING -i wlx3 -j MARK --set-mark 3
iptables -t raw -A PREROUTING -m mark --mark 3 -j CT --zone-reply 3

(If the new interface name doesn't start with wlx add new nat rules accordingly.)

Here's an example of conntrack handling when a client connects twice, even using the same source port, to the two ports while the RPi got the very same IP address assigned to the two wlx wireless interfaces. The conntrack zone is included in flow selection and allows NAT to be handled correctly even if one side of the flows sees exactly the same addresses and ports:

# conntrack -E
    [NEW] tcp      6 120 SYN_SENT src=192.168.0.101 dst=192.168.0.205 sport=6666 dport=4431 [UNREPLIED] src=192.168.42.1 dst=192.168.42.10 sport=443 dport=6666 zone-reply=1
 [UPDATE] tcp      6 60 SYN_RECV src=192.168.0.101 dst=192.168.0.205 sport=6666 dport=4431 src=192.168.42.1 dst=192.168.42.10 sport=443 dport=6666 zone-reply=1
 [UPDATE] tcp      6 432000 ESTABLISHED src=192.168.0.101 dst=192.168.0.205 sport=6666 dport=4431 src=192.168.42.1 dst=192.168.42.10 sport=443 dport=6666 zone-reply=1 [ASSURED]
    [NEW] tcp      6 120 SYN_SENT src=192.168.0.101 dst=192.168.0.205 sport=6666 dport=4432 [UNREPLIED] src=192.168.42.1 dst=192.168.42.10 sport=443 dport=6666 zone-reply=2
 [UPDATE] tcp      6 60 SYN_RECV src=192.168.0.101 dst=192.168.0.205 sport=6666 dport=4432 src=192.168.42.1 dst=192.168.42.10 sport=443 dport=6666 zone-reply=2
 [UPDATE] tcp      6 432000 ESTABLISHED src=192.168.0.101 dst=192.168.0.205 sport=6666 dport=4432 src=192.168.42.1 dst=192.168.42.10 sport=443 dport=6666 zone-reply=2 [ASSURED]

Miscellaneous

  • misc 1

    For a camera to be able to ping the host or receive ICMP errors from the host, this (global) setting must be added:

    sysctl -w net.ipv4.fwmark_reflect=1
    

    else the ICMP answer doesn't follow policy routing. An other better way is to use CONNMARK/connmark, but it would make the answer unnecessarily more complex.

  • Misc 2

    The working result can't be tested correctly from the RPi itself but only from a client on the LAN (or on Internet with support from RPi's router). Settings are specific to the routing case.

    Optionally for the host to be able to work (and allowing a reverse proxy to be put in place), these additional settings can be added:

    Select the right interface even before NAT happens (requires kernel >= 4.17), else the socket will choose the wrong source address (of an other interface) later:

    ip rule add iif lo ipproto tcp dport 4431 lookup 4431
    ip rule add iif lo ipproto tcp dport 4432 lookup 4432
    

    The destination has to be DNATed in nat/OUTPUT. No need for the exact wlx name here, the correct outgoing route was already selected by the routing stack with the new routing rules (reply still requires part of iptables raw/PREROUTING rules from main answer). And a conntrack reply zone is also needed in raw/OUTPUT to handle the rare clash cases.

    iptables -t raw -A OUTPUT -o wlx+ -p tcp --dport 4431 -j MARK --set-mark 1
    iptables -t raw -A OUTPUT -m mark --mark 1 -j CT --zone-reply 1
    
    iptables -t raw -A OUTPUT -o wlx+ -p tcp --dport 4432 -j MARK --set-mark 2
    iptables -t raw -A OUTPUT -m mark --mark 2 -j CT --zone-reply 2
    
    iptables -t nat -A OUTPUT -p tcp -m mark ! --mark 0 -j DNAT --to-destination :443
    

    The tests in this case should be done from the RPi with:

    curl --insecure https://192.168.42.1:4431/
    curl --insecure https://192.168.42.1:4432/
    
  • misc 3

    The settings in misc 2 if adapted for local handling of UDP, for a different case than OP, would probably not be enough for some corner cases: UDP always needs support from the local application when in a multi-homed environment.


I couldn't figure out how to do it with iptables but I solved the problem using socat worked nicely for me:

sudo socat TCP-LISTEN:443,interface=eth0,FORK TCP:192.168.42.1:443,interface=wlxb8b7f16a0602
sudo socat TCP-LISTEN:444,interface=eth0,FORK TCP:192.168.42.1:443,interface=wlxb8b7f16a04cd

All requests come to port 443 at eth0 interface will be redirected to 192.168.42.1:443 at wlxb8b7f16a0602 interface.

And similarly, requests come to port 444 at eth0 interface will be redirected to 192.168.42.1:443 at wlxb8b7f16a04cd interface.