pgj / freebsd-wifibox

wifibox: Use Linux to drive your wireless card on FreeBSD
BSD 2-Clause "Simplified" License
159 stars 12 forks source link

Suggestion: Bridging interfaces or 1:1 NAT #18

Closed ltning closed 1 year ago

ltning commented 2 years ago

Hi,

wifibox works quite well for me; ThinkPad Carbon X1 Gen6, Intel 8265 / 8275 (wouldn't know which). Thank you very much - I did something similar (but with much less style) with OpenBSD a while back but once it broke I never bothered fixing it again :)

The one thing I would've liked to see is if there was a way to effectively bridge the interfaces in the VM with the host, in order to pass the DHCP-assigned IP through. Like this one would avoid double NAT and things that actually require the newfound bandwidth (video conferencing, uPnP stuff, services running on the host) would work better (or at all).

If briding like this isn't feasible, perhaps using two-way 1:1 NAT would be? Then at least other hosts on the local network could reach it without adding additional plumbing in the VM.

I have avoided Linux since I left Slackware around 2000-ish, so I haven't played much around in the VM and wouldn't quite know where to begin.

/Eirik

pgj commented 2 years ago

Hi Eirik, that is great that I was able to help you out somehow! I have also been pondering about how to implement a direct bridge between the VM's Ethernet and wireless interfaces. That was the page I have checked on the topic: https://wiki.debian.org/BridgeNetworkConnections#Bridging_with_a_wireless_NIC but eventually I have not got to come up with an actual proof-of-concept implementation. I have not considered two-way 1:1 NAT, that is also definitely something I should look into -- thanks for the hint.

cqexbesd commented 2 years ago

It's a bit hacky but I wrote a UDP to TAP script to make a remote ethernet device appear to be local to a VirtualBox instance.

Presumably one could create a tap device on both Linux and FreeBSD and then just tunnel between them, using UDP or something better.

pgj commented 2 years ago

Thanks for the idea @cqexbesd, I will investigate that one as well.

ltning commented 2 years ago

At least on the FreeBSD side, for bridging of wifi to work, the sysctl net.link.bridge.inherit_mac needs to be set to 1. I presume something similar is needed in the VM.

fearedbliss commented 2 years ago

I agree that this is needed. At the moment I'm unable to open any ports on my laptops using wifibox since the actual IP assigned to the machine is in the VM, but the actual ssh instance is running on the host. Even though I can correctly ping with the real IP across all machines, I can't ssh into them. The host sshd daemon is listening on all addresses (but the real assigned IP in the VM won't be part of that given the dual NAT setup). In the situation where I have two laptops using wifibox, they could both see each other (192.168.1.136 and 192.168.1.139), can ping each other as well, but even if sshd is running , an nmap -sT -p 22 localhost would show it as open, but an nmap -sT -p 22 192.168.1.136 (136 = the IP of the same machine but given to the VM interface) shows as closed.

fearedbliss commented 2 years ago

I applied the following rule in order to get this working (This isn't a permanent solution but will help):

(Applied inside the VM.. which is read-only but you can still manipulate the IP table):

iptables -t nat -A PREROUTING -p tcp --dport 22 -j DNAT --to-destination 10.1.0.2:22

Once you do that and test that you can reach whatever service you need on the host side (from another machine), you can do the following in the VM to get the final rules:

iptables-save

Once you have that, you can dump all of that and put it back in your host's /usr/local/etc/wifibox/appliance/iptables file. That way when you start the VM again, it will be applied.

(The one line that translates to the above yields: -A PREROUTING -p tcp -m tcp --dport 22 -j DNAT -to-destination 10.1.0.2:22 and goes under the POSTROUTING section of *nat)

You'll need to know the assigned double-NATTed IP on the host side before you can do this. Although given that we are most likely only ever going to have 1 IP assigned (or you can just use a static IP as well), you can just use that one IP across all these rules.

I get ping times from 1 to 700 ms, but this could just be actual latency given these are multiple wireless devices.. but I think there may be something else going on here given all of this double natting and all of that. I'm sure if all of these devices were directly communicating with each other without double natting, it would be a lot lower.. given it's all on the same LAN and location in the house. Doing some internal file transfers between two machines, I was able to get around 10 MB/s (one laptop on an 10/100 connection, and the other is on wifibox). Not the worse but not the best. Although good enough for what I'm trying to do atm.

pgj commented 2 years ago

I have changed the interaction between wifibox and the guest system so that the ipfilter rules can be adapted in a way as you described above. This is also documented on the wifibox-alpine(5) manual page (see the section called "Configuration of Networking Packet Filtering"). I have not had the chance to test this but it is great to hear that it indeed works as a workaround. Of course, ideally, wifibox should be able to do this automatically -- this is something left to do.

I was doing some investigation on how to create a bridge between wlan0 and eth0 in the guest, but without any tangible results so far. Maybe I should put this on the back burner and focus on making the iptables rule configuration automated.

@fearedbliss I think 10 MB/s is quite good on a 100-Mbit network, theoretically it cannot be faster than 12.5 MB/s but that is without signalling and such I guess.

fearedbliss commented 2 years ago

@pgj Sorry, I should have said it's a 10/100/1000 connection! I forgot to write the 1000 haha. So that first laptop is using gigabit Ethernet via my type C Anker dongle's Ethernet port. The other laptop (framework) was using wifibox. I'm not surprised there is a drop in performance given the wireless part but I feel it could still be faster than 10 MB/s.

Regarding the firewall rules, yea it would be nice if all the ports could be automatically routed. But an easily configurable firewall configuration would be a good workaround for now. It gives you a little bit of security as well haha.

orbitz commented 1 year ago

Another case this has come up for me is Chromecast. I'm assuming Chromecasts use some sort of multicast and my Chromium no longer can see it.

FWIW, I'd be totally fine with wifibox CLI tool giving me the tools to setup these port forwards on the fly as a first-step. I can just run a script on first run every time. I'd rather be able to get something going than wait

pgj commented 1 year ago

If this is about multicast UDP you can use the forwarding service that has been added to the recent wifibox-alpine images. You can find out more about the details from the wifibox-alpine(5) manual page and the provided sample files upon installing the latest version of the port. Let me know if this helps in this case.

orbitz commented 1 year ago

I think, unfortunately, I just don't know enough about networking yet to properly play with this for Chromecast. I think I need to do some mDNS stuff (I have built the latest version of alpine with mDNS and forwarding). I'm not entirely sure which forwarding I need. I found some iptables commands online but it looks like it needs the multiport iptables extension which isn't in alpine, and the port range is large, and I haven't gotten a chance to script it out. But I'm also kind of flailing about and not really sure how to debug whatever change I make (for example, just turning mDNS on didn't have an impact).

I know this is outside wifibox directly, but if you have any recommendations as what to do, I'd love to know. Chromecasting is a nice feature to have.

pgj commented 1 year ago

Mind that simply enabling the XX_MDNS and XX_FORWARDING port options will not do too much. You will have to configure each of those through the respective configuration files. The mDNS support is mostly about providing support for advertising services and it is not applicable in your case. Regarding the iptables(8) multiport support, I guess that is not Alpine-specific. I removed all the unused kernel modules from the (linux-lts) upstream package so adding it back could provide the desired functionality.

Anyhow, in theory the following should be enough for Chromecasting:

iptables -A INPUT -s ${CHROMECAST_IP}/32 -p udp -m multiport --sports 32768:61000 -m multiport --dports 32768:61000 -m comment --comment "Allow Chromecast UDP data (inbound)" -j ACCEPT
iptables -A OUTPUT -d ${CHROMECAST_IP}/32 -p udp -m multiport --sports 32768:61000 -m multiport --dports 32768:61000 -m comment --comment "Allow Chromecast UDP data (outbound)" -j ACCEPT
iptables -A OUTPUT -d ${CHROMECAST_IP}/32 -p tcp -m multiport --dports 8008:8009 -m comment --comment "Allow Chromecast TCP data (outbound)" -j ACCEPT
iptables -A OUTPUT -d 239.255.255.250/32 -p udp --dport 1900 -m comment --comment "Allow Chromecast SSDP" -j ACCEPT

Looks like nothing else is needed here, just the reintroduction of the iptables(8) multiport support in the kernel. I will post here once it is fixed.

pgj commented 1 year ago

All right, in the latest version of freebsd-wifibox-port the multiport support is added so the rules that I mentioned in my comment above should work. Note that the quoted rules would also leverage the comment extension but that is not added. Therefore the following version is recommended for testing, i.e. added for (the *filter section of) appliance/iptables under ${ETCDIR}/wifibox:

[0:0] -A INPUT -s ${CHROMECAST_IP}/32 -p udp -m multiport --sports 32768:61000 -m multiport --dports 32768:61000  -j [0:0] ACCEPT
[0:0] -A OUTPUT -d ${CHROMECAST_IP}/32 -p udp -m multiport --sports 32768:61000 -m multiport --dports 32768:61000  -j ACCEPT
[0:0] -A OUTPUT -d ${CHROMECAST_IP}/32 -p tcp -m multiport --dports 8008:8009  -j ACCEPT
[0:0] -A OUTPUT -d 239.255.255.250/32 -p udp --dport 1900  -j ACCEPT

where ${CHROMECAST_IP} should be replaced for a specific IP address per the blog post in the previous comment. Then the iptables -L -nv command can be used (on the guest console) to see if the rules are working or not.

Let me know if this works -- in case of success, I can document this on the manual page.

orbitz commented 1 year ago

Unfortunately I do not know the IP of the chromecast and because Google is Google I was not having luck finding it. So I tried the following (this is what the whole *filter section looks like):


*filter
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
[0:0] -A INPUT -p udp -m multiport --sports 32768:61000 -m multiport --dports 32768:61000  -j ACCEPT
[0:0] -A OUTPUT -p udp -m multiport --sports 32768:61000 -m multiport --dports 32768:61000  -j ACCEPT
[0:0] -A OUTPUT -p tcp -m multiport --dports 8008:8009  -j ACCEPT
[0:0] -A OUTPUT -p udp --dport 1900  -j ACCEPT
[0:0] -A FORWARD -i eth0 -o wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT
[0:0] -A FORWARD -i eth0 -o wlan0 -j ACCEPT
COMMIT

I'm not sure if removing the -s was a bad decision. But this does not work for me.

Maybe I can install tcpdump on the alpine and see what it's getting to see what to accept?

pgj commented 1 year ago

Sure, some more analytics can probably help. But please mind that you cannot simply install Alpine packages to the guest image. Instead, they have to be added for the port's Makefile and the image has to be rebuild with them. (This is for security considerations.)

pgj commented 1 year ago

I have added support for tcpdump in version 20230201 of net/wifibox-alpine.