Page MenuHomeFreeBSD

ipfw_nat: allow redirect_port if missing config ip or if
AbandonedPublic

Authored by eugen_grosbein.net on Jun 12 2017, 10:29 AM.
Tags
None
Referenced Files
Unknown Object (File)
Feb 15 2024, 11:43 AM
Unknown Object (File)
Feb 15 2024, 11:06 AM
Unknown Object (File)
Feb 15 2024, 10:54 AM
Unknown Object (File)
Feb 1 2024, 7:32 PM
Unknown Object (File)
Jan 9 2024, 8:44 AM
Unknown Object (File)
Dec 20 2023, 12:18 AM
Unknown Object (File)
Dec 19 2023, 11:43 PM
Unknown Object (File)
Dec 16 2023, 7:13 AM

Details

Reviewers
fahri_hasan_hotmail.com
Group Reviewers
network
Summary

Simple patch that allow port redirection with any destination address.

Example:
ipfw nat 3000 config log redirect_port tcp localhost:3000 80
ipfw add 65500 nat 3000 ip from me to any src-port 3000
ipfw add 65501 nat 3000 ip from any to any recv ${IFACE_INTERNAL}* dst-port 80

Full example:
imslu

Diff Detail

Repository
rS FreeBSD src repository - subversion
Lint
Lint Skipped
Unit
Tests Skipped

Event Timeline

fahri_hasan_hotmail.com created this object with edit policy "network (Project)".

Can anyone check this functionality and add it to the kernel?

If you think there is no need for such functionality, please check out what Linux offers.

You should better describe a problem solved by this change.

I want to redirect all outgoing traffic from LAN to destination IP:any port 80 ---> localhost:3000
It seems simple. Example rule:
ipfw nat 3000 config redirect_port tcp localhost:3000 80

I can add this rule, but it will never work, because I do not have configuration parameters "config ip" or "config if".
I do not want to redirect traffic to specific IP, I want to redirect all traffic to any IP with destination port 80.

See: https://svnweb.freebsd.org/base?view=revision&revision=277714

"natd(8) will work with an unconfigured interface and effectively not do
anything until the interface is assigned an address."

This patch fix these problem in ipfw_nat and allow port redirect to any IP with destination port XXX.
Works only with single port!

We already have "ipfw fwd" command for this task that works just fine and does not require any patches.

For example: "ipfw add 2000 fwd 127.0.0.1,3000 tcp from any to any 80 out xmit em0"
This way, kernel redirects matched packets to local application's socket and makes applocation think that original request was really addressed to this socket. And kernel fixes (translates) replies so that they look as returning from port 3000. Basically, the kernel does such simple port translation without using full-blown NAT.

Transparent proxying works this way in FreeBSD for ages: you can redirect HTTP requests to local proxy that is ready to process them. For example, squid is capable to perform transparent proxying this way.

I see no need to use full-blown NAT for this task and no need in this patch unless there are other problems it could solve.

I have never tested ipfw fwd on a external interface, because:

  1. I redirect only traffic for expired users, destination port 80 to ISP warning page. After first check (internal interface) this traffic dropped and never have going to second ipfw check on external interface.
  2. I allow traffic on a external interface with first rule. I try to make minimal ipfw rules for high performance router.

Example:

ipfw show

00011 31009 6232918 allow ip from any to any via re0*
00012 0 0 allow ip from any to me
00013 34 2824 allow ip from me not 3000 to any
00051 0 0 skipto 71 ip from table(11) to any out
00052 0 0 pipe tablearg ip from any to table(3) out
00061 0 0 skipto 72 ip from any to table(11) in
00062 0 0 pipe tablearg ip from table(4) to any in
00071 0 0 pipe tablearg ip from any to table(1)
00072 0 0 pipe tablearg ip from table(2) to any
65500 0 0 nat 3000 ip from me 3000 to any
65501 0 0 nat 3000 ip from any to any recv re1* dst-port 80
65535 45 3712 deny ip from any to any

I will not use squid to redirect traffic.

You can use any matching expression, it will work for "recv re1* dst-port 80" too.
You can use "ipfw fwd" to redirect packets to http server too, without transparent proxying.
It just works.

Before I used ipfw nat I did a lot of tests with ipfw fwd.
I upgrade kernel to 11-RC3 and made a new test with ipfw fwd.

"ipfw fwd" forwarding the package to localhost but not changing destination port.

Please, tested and you:

  1. cat rc.firewall #

#!/bin/sh

WAN Interface

IFACE_EXTERNAL=re0

LAN Interface

IFACE_INTERNAL=re1
IPFW=/sbin/ipfw

${IPFW} -f flush
${IPFW} -f queue flush
${IPFW} -f pipe flush
${IPFW} -f table all flush

${IPFW} add 11 allow ip from any to any via ${IFACE_EXTERNAL}*
${IPFW} add 12 allow ip from any to me
${IPFW} add 13 allow ip from me to any

expired users

${IPFW} add 65500 fwd 127.0.0.1,3000 tcp from any to any recv ${IFACE_INTERNAL}* dst-port 80
${IPFW} add 65501 allow ip from any to any src-port 80,3000 out

uname -a

FreeBSD imslu 11.1-RC3 FreeBSD 11.1-RC3 #0 r320976: Fri Jul 14 02:20:44 UTC 2017 root@releng2.nyi.freebsd.org:/usr/obj/usr/src/sys/GENERIC amd64

  1. ipfw show

00011 5706 948620 allow ip from any to any via re0*
00012 0 0 allow ip from any to me
00013 16 1456 allow ip from me to any
65500 18 1636 fwd 127.0.0.1,3000 tcp from any to any recv re1* dst-port 80
65501 20 1599 allow ip from any to any src-port 80,3000 out
65535 710 45127 deny ip from any to any

"ipfw fwd" works for me just fine in 11.1
What is an application do you have that listens for socket at port tcp/3000?
Why do you think that packets do not get redirected to its server socket?
For local redirect there is no port translation needed, the data just get delivered to the socket directly.

Apache 2.4.25 listens on ports 80,443,3000.
I try to access a random web site from a PC in LAN and the reply comes from Apache port 80, not from port 3000.

/bin/freebsd-version

11.0-RELEASE-p1

sockstat -4 -l | grep www

www httpd 14117 4 tcp4 *:80 *:*
www httpd 14117 6 tcp4 *:3000 *:*
www httpd 14117 8 tcp4 *:443 *:*
www httpd 3609 4 tcp4 *:80 *:*
www httpd 3609 6 tcp4 *:3000 *:*
www httpd 3609 8 tcp4 *:443 *:*
www httpd 1058 4 tcp4 *:80 *:*
www httpd 1058 6 tcp4 *:3000 *:*
www httpd 1058 8 tcp4 *:443 *:*
www httpd 99583 4 tcp4 *:80 *:*
www httpd 99583 6 tcp4 *:3000 *:*
www httpd 99583 8 tcp4 *:443 *:*
www httpd 96973 4 tcp4 *:80 *:*
www httpd 96973 6 tcp4 *:3000 *:*
www httpd 96973 8 tcp4 *:443 *:*
www httpd 93773 4 tcp4 *:80 *:*
www httpd 93773 6 tcp4 *:3000 *:*
www httpd 93773 8 tcp4 *:443 *:*

/usr/local/sbin/httpd -v

Server version: Apache/2.4.25 (FreeBSD)
Server built: unknown

That is how it is supposed to work. An answer must be returned from the port if original request, or else client TCP stack would drop it instead of delivering to HTTP browser.

I wrote earlier "And kernel fixes (translates) replies so that they look as returning from port 3000". That was misprint, I meant to write "And kernel fixes (translates) replies so that they look as returning from port 80".

http://85.187.224.234:3000 <--- Expected content. (Black page for the outside world)
http://85.187.224.234:80 <--- ipfw fwd returns this content (TEST OK)

I expect ipfw fwd to return CONTENT of http://85.187.224.234:3000, but it return CONTENT of http://85.187.224.234:80

Some time ago, I made changes to the kernel to find the problem with ipfw fwd, but without success.
Then I decided to use the ipfw nat.

I use this howto: https://github.com/mysticall/imslu/blob/master/doc/freebsd-11-howto.txt
All config files are here: https://github.com/mysticall/imslu/tree/master/conf/freebsd

IPFW FWD not working in my case.

"ipfw fwd" does nothing with contents of packets. Apache determines contents to return, not ipfw fwd.
All you need it to properly configure Apache to match virtualhost right.

Easiest way is to disable port 3000 for "normal" Apache altogether, create distinct httpd3000.conf to listen port 3000 only and run extra httpd process with that config. This works just fine.

After small changes to the config file, everything works great.

From:
Listen 3000
<VirtualHost *:3000>
To:
Listen localhost:3000
<VirtualHost localhost:3000>

Thank you, Eugene!

FreeBSD already has neened fuction, the change is not needed.