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

Basic support for freebsd ipfw firewall #21

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

stuart-mclaren
Copy link

FreeBSD's ipfw firewall does not rewrite the destination IP address when forwarding
using rules such as:

00100 fwd 127.0.0.1,2080 tcp from me to not me 80,443

This commit adds ipfw support by assuming transparent redirection on freebsd.
If you create the above rule and point moproxy at a http proxy at IP address 1.2.3.4

$ moproxy -b 127.0.0.1 -p 2080 -t 1.2.3.4:8080

You can fetch remote 443 or 80 ports without setting proxy environment variables, eg:

$ curl https://example.com:443

...

FreeBSD's ipfw firewall does not rewrite the destination
IP address when forwarding using rules such as:

 00100 fwd 127.0.0.1,2080 tcp from me to not me 80,443

This commit adds ipfw support by assuming transparent
redirection on freebsd. If you create the above rule and
point moproxy at a http proxy at IP address 1.2.3.4

 $ moproxy -b 127.0.0.1 -p 2080 -t 1.2.3.4:8080

You can fetch remote 443 or 80 ports without setting
proxy environment variables, eg:

 $ curl https://example.com:443
 <HTML>...
@sorz
Copy link
Owner

sorz commented Apr 10, 2023

In current design, the TCP port for transparent proxy also serve as SOCKSv5 server.
Is it possible to distinguish connections that redirected by firewall and connections that direct to the port in FreeBSD? If it's a direct connection, pass it to SOCKS handshaking procedure.

@stuart-mclaren
Copy link
Author

Thanks for your feedback.

Looking at the ipfw man page (https://www.freebsd.org/cgi/man.cgi?ipfw(8))

For packets forwarded locally, the local address of the socket will be set to the original destination address of the packet.

I think this means that, in principle, we can distinguish between firewall redirected packets and direct connections.

Adding some debug to the code

    while let Some(sock) = clients.next().await {
        println!("sock is {:?}", sock);

and running

$ curl 216.58.193.142:80

produced this log output

sock is Ok(PollEvented { io: Some(TcpStream { addr: 216.58.193.142:80, peer: 10.0.0.1:62950, fd: 10 }) })
listeners: 127.0.0.1:2080 # Address for listeners[0].0.local_addr().unwrap())

Note: addr has been set to 216.58.193.142:80 as per the man page.

This is different to a direct connection, eg

$ curl localhost:2080
sock is Ok(PollEvented { io: Some(TcpStream { addr: 127.0.0.1:2080, peer: 127.0.0.1:10825, fd: 10 }) })
listeners: 127.0.0.1:2080 # unchanged

Note that addr is unchanged (127.0.0.1:2080) because it hasn't been affected by the firewall.

So (hopefully) something like this would work (pseudo code):

 if not (listener.ipv4_addr == sock.ipv4_addr and listener.port == sock.port) {
   // ipfw NAT detected
 }

@sorz
Copy link
Owner

sorz commented Apr 21, 2023

But if it's bind on 0.0.0.0 or :: rather than a specific address…

I start feeling that reusing the socket for SOCKS proxy is a mistake from the beginning. I should at least give a CLI switch for it.

@sorz sorz force-pushed the master branch 2 times, most recently from 3ba264b to b62babf Compare January 10, 2024 08:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants