Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 10 Jan 2006 02:16:10 -0800
From:      Andrew Fremantle <freebsd@skyhawk.ca>
To:        freebsd-questions@freebsd.org, freebsd-ipfw@freebsd.org
Subject:   Advanced IPFW2 Forward rule problem / bug / misunderstanding
Message-ID:  <43C3896A.7090704@skyhawk.ca>

next in thread | raw e-mail | index | archive | help
Hello,

I have searched the lists for information pertaining to this problem, 
but I haven't been able to find anything relevant to my attempted usage 
of IPFWs "forward" action. If there are any preexisting threads that 
address my concern, please direct me to them.

I have also sent this message to the freebsd-ipfw list, as this seems 
consistent with some other threads I see in their archive.

Here's the situation :

I have two ISPs, each providing two IPs. One of these ISPs is providing 
IPs on totally different subnets, and is MAC sensitive. I have two 
internal servers (Actually, just one listening on two addresses), and I 
want this server to be available externally to both ISPs. (We're 
migrating ISPs, and we don't want any interruption in service). I am 
using port forwarding in NATd to allow the necessary ports through to 
the server. My problem comes with the replies - FreeBSD has only one 
default gateway, and all traffic going out, regardless of which external 
IP address it is from, goes to that gateway. Since ISP2 doesen't care 
much for routing traffic from ISP1, and vice-versa, I have a problem.

I should note here that I am not trying to load balance - I am perfectly 
happy with all outbound LAN connections being NATted over one link, I 
just need the ability to service inbound connections on all four IPs.

I am using forward rules in my firewall to match packets belonging to 
these other interfaces, to forward them to the appropriate gateway. 
According to the manpage for ipfw, "If /ipaddr/ is not a local address, 
then the port number (if specified) is ignored, and the packet will be 
forwarded to the remote address, using the route as found in the local 
routing table for that IP." I interpret that as "The packet's next hop 
will be compared to the routing table, and routed out the appropriate 
interface to reach that next hop". The problem is that doesen't seem to 
be happening. I have tried fiddling a few knobs to no effect - 
specifically net.inet.ip.fastforwarding, net.inet.ip.sourceroute and 
net.inet.ip.accept_sourceroute.

Telus is the "legacy" ISP, so when I'm trying these rules all the 
inbound server requests are from the two Telus interfaces. I have 
numbered a rule here "42000". This rule will catch all kinds of packets 
outbound from ${ext1_ip}:80 and ${ext1_ip}:443 to clients on the internet.

This tells me my inbound NAT translation is working, the packets are 
getting to the server, replies are coming back, they're matching my 
forward rules, but still going out the wrong interface anyways!

As attached as I am to the idea of doing this via ipfw, if anyone has 
any suggestions on alternate methods to achieve the same results, I'd 
love to hear them!!!

On to the technical details - I have obscured IP addresses here, but the 
networks and subnet masks remain the same

-------------------------------------------

bsdbox# uname -a
FreeBSD bsdbox 6.0-RELEASE-p1 FreeBSD 6.0-RELEASE-p1 #1: Mon Jan  9 
08:15:08 PST 2006     root@bsdbox:/usr/obj/usr/src/sys/BSDBOX  i386

-------------------------------------------

bsdbox# cat /usr/src/sys/i386/conf/BSDBOX
.
.
.
### FIREWALLING
options         IPFIREWALL
options         IPFIREWALL_FORWARD
options         IPFIREWALL_FORWARD_EXTENDED ( I just did this to test - 
it made no difference)
options         IPDIVERT

-------------------------------------------

bsdbox# cat /etc/rc.conf
.
.
.
##########
## Networking
##########

gateway_enable="YES"

## Ensure interface configuration and Firewall script remain consistent!!

defaultrouter="24.85.92.1"

ifconfig_rl0="192.168.1.1"
ifconfig_vr0="142.179.109.xxx netmask 255.255.248.0"
ifconfig_vr1="216.232.85.xxx netmask 255.255.254.0"

ifconfig_rue0="24.85.9x.xxx netmask 255.255.252.0"
ifconfig_rue0_alias0="24.85.9x.xxx netmask 255.255.255.255"

natd_enable="NO"
firewall_enable="YES"
firewall_script="/usr/local/etc/firewall.telus+shaw-test"

-------------------------------------------------

bsdbox# cat /usr/local/etc/firewall.telus+shaw-test
##### firewall.telus+shaw       0.9.8

# Aquire variables from /etc/rc.conf
if [ -r /etc/rc.conf ]; then
        . /etc/rc.conf
fi

fwcmd="/sbin/ipfw -q"

##########
## THIS SCRIPT REQUIRES THE FOLLOWING VARIABLES
## TO BE CORRECTLY DEFINED!
##########

##########
# PRIMARY external interface (Telus)
ext1="vr0"  # Device name
ext1_ip="142.179.109.xxx"  # IP Address
ext1_gw="142.179.104.254"  # IP Gateway
ext1_bc="142.179.111.255"  # Broadcast Address
ext1_srv="192.168.1.10"  # Server IP Address
##########

##########
# SECONDARY external interface (Telus)
ext2="vr1"  # Device name
ext2_ip="216.232.85.xxx"  # IP Address
ext2_nm="255.255.254.0"  # Network Mask
ext2_bc="216.232.85.255"  # Broadcast Address
ext2_gw="216.232.84.254"  # IP Gateway
ext2_srv="192.168.1.11"  # IP Address of internal server
##########

##########
# Shaw Cable Interface(s)
# PRIMARY IP
shaw="rue0"  # Device Name
shaw_ip="24.85.93.xxx"  # IP Address
shaw_nm="255.255.252.0"  # Network Mask
shaw_bc="24.85.95.255"  # Broadcast Address
shaw_gw="24.85.92.1"  # IP Gateway

srv1_int="192.168.1.10"  # Internal IP of server
srv1_ext="24.85.93.xxx"  # External IP of server (Same as ${shaw_ip})

# SECONDARY IP
srv2_int="192.168.1.11"  # Internal IP of server
srv2_ext="24.85.93.xxx"  # External IP of server
##########

##########
# INTERNAL interface
int="rl0"  # Device name
int_ip="192.168.1.1"  # IP Address
int_nm="255.255.255.0"  # Network Mask
int_bc="192.168.1.255"  # Broadcast Address
##########

## I have to handle NATd manually from this script because it's got
## too many connection specific options.

nat_in="8667"
nat_out="8669"

## Kill any running instance
if [ -r /var/run/natd.pid ]; then
        kill -9 `cat /var/run/natd.pid`
        sleep 1
fi

# And run our new NATd
/sbin/natd -log_ipfw_denied -i ${nat_in} -o ${nat_out} -s -m -u -n 
${shaw} -punch_fw 36000:100 -redirect_port tcp ${ext1_srv}:22 
${ext1_ip}:xxxx -redirect_port tcp ${ext1_srv}:53 ${ext1_ip}:53 
-redirect_port tcp ${ext1_srv}:80 ${ext1_ip}:80 -redirect_port tcp 
${ext1_srv}:443 ${ext1_ip}:443 -redirect_port udp ${ext1_srv}:53 
${ext1_ip}:53 -redirect_port tcp ${ext2_srv}:80 ${ext2_ip}:80 
-redirect_port tcp ${ext2_srv}:443 ${ext2_ip}:443 -redirect_port tcp 
${srv1_int}:22 ${shaw_ip}:xxxx -redirect_port tcp ${srv1_int}:53 
${shaw_ip}:53 -redirect_port udp ${srv1_int}:53 ${shaw_ip}:53 
-redirect_port tcp ${srv1_int}:80 ${shaw_ip}:80 -redirect_port tcp 
${srv1_int}:443 ${shaw_ip}:443 -redirect_port tcp ${srv2_int}:80 
${srv2_ext}:80 -redirect_port tcp ${srv2_int}:443 ${srv2_ext}:443

# Blow away the existing firewall ruleset
${fwcmd} flush

##########
# Now I start defining rules to handle traffic
##########
# Divide and Conquer based on interface and direction

## Inbound Internal
${fwcmd} add 0500 skipto 5000 all from any to any in recv ${int}

## Outbound Internal
${fwcmd} add skipto 10000 all from any to any out xmit ${int}

# Inbound Primary External (Telus)
${fwcmd} add skipto 15000 all from any to any in recv ${ext1}

# Outbound Primary External (Telus)
${fwcmd} add skipto 20000 all from any to any out xmit ${ext1}

# Inbound Secondary External (Telus)
${fwcmd} add skipto 25000 all from any to any in recv ${ext2}

# Outbound Secondary External (Telus)
${fwcmd} add skipto 30000 all from any to any out xmit ${ext2}

# Inbound Shaw
${fwcmd} add skipto 35000 all from any to any in recv ${shaw}

# Outbound Shaw
${fwcmd} add skipto 40000 all from any to any out xmit ${shaw}

# Local Loopback
${fwcmd} add allow all from any to any via lo0

# If I still haven't matched it, it's a damn weirdo. Drop it.
${fwcmd} add reset all from any to any

### *****************
### Inbound Internal Traffic
### *****************

# Drop all packets not originating on my network
${fwcmd} add 5000 drop all from not ${int_ip}:${int_nm} to any // 
Inbound Internal

# Skip the next rule for all packets destined to the firewall itself ( & 
Broadcast )
${fwcmd} add 5010 skipto 5040 all from any to \( ${int_ip} or ${int_bc} \)

# Drop all packets destined to my internal network
${fwcmd} add 5020 drop all from any to ${int_ip}:${int_nm}

# Allow existing conversations, and new ICMP and SSH connections
${fwcmd} add 5040 check-state
${fwcmd} add allow icmp from any to \( ${int_ip} or ${int_bc} \) keep-state
${fwcmd} add allow tcp from any to ${int_ip} 22 setup keep-state

# Pass packets to NATd for possible translation
${fwcmd} add divert ${nat_out} all from any to any

# Redirect to gateway for ext1 if it's an existing connection on ext1
${fwcmd} add forward ${ext1_gw} all from ${ext1_ip} to any

# Redirect to gateway for ext2 if it's an existing connection on ext2
${fwcmd} add forward ${ext2_gw} all from ${ext2_ip} to any

# Allow all packets adjusted by NATd
${fwcmd} add allow ip from \( ${shaw_ip} or ${srv2_ext} \) to any

# Reject this packet
${fwcmd} add reset log all from any to any


### *****************
### Outbound Internal Traffic
### *****************

# Allow Existing Conversations
${fwcmd} add 10000 check-state // Outbound Internal

# Allow me to start ICMP, SSH and DNS sessions
${fwcmd} add allow icmp from ${int_ip} to ${int_ip}:${int_nm} keep-state
${fwcmd} add allow tcp from ${int_ip} to ${int_ip}:${int_nm} 22 keep-state
${fwcmd} add allow udp from ${int_ip} to ${srv1_int} 53 keep-state

# Allow NATd translated traffic through
${fwcmd} add allow all from not ${int_ip}:${int_nm} to ${int_ip}:${int_nm}

# Reject this packet
${fwcmd} add reset log all from any to any

### *****************
### Inbound External (primary) Traffic
### *****************

# Ensure this packet did not originate from a private network
${fwcmd} add 15000 drop all from 192.168.0.0/16 to any // Inbound Telus 
Primary
${fwcmd} add 15020 drop all from 172.16.0.0/12 to any
${fwcmd} add 15030 drop all from 10.0.0.0/8 to any
${fwcmd} add 15040 drop all from 127.0.0.0/8 to any
${fwcmd} add 15050 drop all from 0.0.0.0/8 to any
${fwcmd} add 15060 drop all from 169.254.0.0/16 to any
${fwcmd} add 15070 drop all from 192.0.2.0/24 to any
${fwcmd} add 15080 drop all from 204.152.64.0/23 to any
${fwcmd} add 15090 drop all from 224.0.0.0/3 to any

# Drop the packet if it's not broadcast or destined to us
${fwcmd} add 15500 skipto 15600 all from any to \( ${ext1_ip} or 
${ext1_bc} \)
${fwcmd} add 15520 deny all from any to any

# Pass to NATd for possible reverse-translation
${fwcmd} add 15600 divert ${nat_in} all from any to any

# Pass anything NATd has translated
${fwcmd} add allow all from any to ${int_ip}:${int_nm}

# Drop whatever's left
${fwcmd} add deny all from any to any

### *****************
### Outbound External (primary) Traffic
### *****************

# Deny all traffic to private nets
${fwcmd} add 20000 drop all from any to 192.168.0.0/16 // Outbound Telus 
Primary
${fwcmd} add drop all from any to 172.16.0.0/12
${fwcmd} add drop all from any to 10.0.0.0/8
${fwcmd} add drop all from any to 127.0.0.0/8
${fwcmd} add drop all from any to 0.0.0.0/8
${fwcmd} add drop all from any to 169.254.0.0/16
${fwcmd} add drop all from any to 192.0.2.0/24
${fwcmd} add drop all from any to 204.152.64.0/23
${fwcmd} add drop all from any to 224.0.0.0/3

# Allow outbound traffic from this machine (NATd translated)
${fwcmd} add allow all from ${ext1_ip} to any

# Drop whatever's left
${fwcmd} add deny all from any to any

### *****************
### Inbound External (secondary) Traffic
### *****************

# Ensure this packet did not originate from a private network
${fwcmd} add 25000 drop all from 192.168.0.0/16 to any // Inbound Telus 
Secondary
${fwcmd} add 25020 drop all from 172.16.0.0/12 to any
${fwcmd} add 25030 drop all from 10.0.0.0/8 to any
${fwcmd} add 25040 drop all from 127.0.0.0/8 to any
${fwcmd} add 25050 drop all from 0.0.0.0/8 to any
${fwcmd} add 25060 drop all from 169.254.0.0/16 to any
${fwcmd} add 25070 drop all from 192.0.2.0/24 to any
${fwcmd} add 25080 drop all from 204.152.64.0/23 to any
${fwcmd} add 25090 drop all from 224.0.0.0/3 to any

# Drop the packet if it's not broadcast or destined to us
${fwcmd} add 25100 skipto 25500 all from any to \( ${ext2_ip} or 
${ext2_bc} \)
${fwcmd} add 25120 deny all from any to any

# Pass to NATd for possible reverse-translation
${fwcmd} add 25500 divert ${nat_in} all from any to any

# Allow all packets translated by NATd
${fwcmd} add allow all from any to ${int_ip}:${int_nm}

# Drop whatever's left
${fwcmd} add reject all from any to any

### *****************
### outbound External (secondary) Traffic
### *****************

# Deny all traffic to private nets
${fwcmd} add 30000 drop all from any to 192.168.0.0/16 // Outbound Telus 
Secondary
${fwcmd} add drop all from any to 172.16.0.0/12
${fwcmd} add drop all from any to 10.0.0.0/8
${fwcmd} add drop all from any to 127.0.0.0/8
${fwcmd} add drop all from any to 0.0.0.0/8
${fwcmd} add drop all from any to 169.254.0.0/16
${fwcmd} add drop all from any to 192.0.2.0/24
${fwcmd} add drop all from any to 204.152.64.0/23
${fwcmd} add drop all from any to 224.0.0.0/3

# Pass all packets from ext2_ip
${fwcmd} add pass all from ${ext2_ip} to any

# Drop whatever's left
${fwcmd} add deny all from any to any

### *****************
### Inbound Shaw Traffic
### *****************

# Ensure this packet did not originate on a private network
${fwcmd} add 35000 drop all from 192.168.0.0/16 to any // Inbound Shaw
${fwcmd} add 35010 drop all from 172.16.0.0/12 to any
${fwcmd} add 35020 drop all from 10.0.0.0/8 to any
${fwcmd} add 35030 drop all from 127.0.0.0/8 to any
${fwcmd} add 35040 drop all from 0.0.0.0/8 to any
${fwcmd} add 35050 drop all from 169.254.0.0/16 to any
${fwcmd} add 35060 drop all from 192.0.2.0/24 to any
${fwcmd} add 35070 drop all from 204.152.64.0/23 to any
${fwcmd} add 35080 drop all from 224.0.0.0/3 to any

# Drop the packet if it's not broadcast or destined to us
${fwcmd} add 35100 skipto 35500 all from any to \( ${shaw_ip} or 
${srv2_ext} or ${shaw_bc} \)
${fwcmd} add 35200 deny all from any to any

# Allow Inbound ICMP and SSH (To me)
${fwcmd} add 35510 allow icmp from any to me
${fwcmd} add 35520 allow tcp from any to me 22

# Pass to NATd for possible reverse translation
${fwcmd} add 35550 divert ${nat_in} all from any to any

# NATd punches holes from 36000 - 36100

# Pass anything NATd has translated
${fwcmd} add 36200 allow all from any to ${int_ip}:${int_nm}

# Deny whatever's left
${fwcmd} add deny all from any to any

### *****************
### Outbound Shaw Traffic
### *****************

# Deny all traffic to private nets
${fwcmd} add 40000 drop all from any to 192.168.0.0/16 // Outbound Shaw
${fwcmd} add drop all from any to 172.16.0.0/12
${fwcmd} add drop all from any to 10.0.0.0/8
${fwcmd} add drop all from any to 127.0.0.0/8
${fwcmd} add drop all from any to 0.0.0.0/8
${fwcmd} add drop all from any to 169.254.0.0/16
${fwcmd} add drop all from any to 192.0.2.0/24
${fwcmd} add drop all from any to 204.152.64.0/23
${fwcmd} add drop all from any to 224.0.0.0/3

# Allow all outbound traffic from me (includes NATted stuff)
${fwcmd} add allow all from \( ${shaw_ip} or ${srv2_ext} or ${shaw_bc} 
\) to any

# Drop whatever's left
${fwcmd} add 42000 deny log all from any to any

######
######
# Log all packets falling through the firewall
${fwcmd} add 65534 deny log all from any to any

------------------------------------------

Excerpts from /var/log/security
Jan  9 09:11:55 bsdbox kernel: ipfw: 40900 Deny TCP 142.179.109.xxx:443 
207.216.1
81.74:1111 out via rue0
Jan  9 09:12:07 bsdbox kernel: ipfw: 40900 Deny TCP 142.179.109.xxx:443 
154.20.34
.158:61974 out via rue0
Jan  9 09:14:40 bsdbox kernel: ipfw: 40900 Deny TCP 142.179.109.xxx:443 
64.180.16
4.232:44707 out via rue0

------------------------------------------
All questions, comments, suggestions and flames (Well, okay maybe not 
the flames) are welcome!

My apologies for the extremely long post.

- Andrew



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?43C3896A.7090704>