Routing SSH traffic on a multi-homed server

I'm running a server with two interfaces, tun0 which is connected to a WAN, and bge0 which is connected to a LAN:

Code:
bge0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=c019b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,TSO4,VLAN_HWTSO,LINKSTATE>
        ether xx:xx:xx:xx:xx:xx
        inet 10.0.0.11 netmask 0xffffff00 broadcast 10.0.0.255
        media: Ethernet autoselect (100baseTX <full-duplex>)
        status: active
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
        options=3<RXCSUM,TXCSUM>
        inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2 
        inet6 ::1 prefixlen 128 
        inet 127.0.0.1 netmask 0xff000000 
        nd6 options=3<PERFORMNUD,ACCEPT_RTADV>
pflog0: flags=141<UP,RUNNING,PROMISC> metric 0 mtu 33152
tun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> metric 0 mtu 1500
        options=80000<LINKSTATE>
        inet 85.xx.xx.xx --> 10.1.0.1 netmask 0xffffffff 
        Opened by PID 1063

The default route is through tun0:

[cmd=]netstat -rf inet[/cmd]
Code:
Routing tables
Internet:
Destination Gateway Flags Refs Use Netif Expire
default 10.1.0.1 UGS 0 19314424 tun0
10.0.0.0 link#1 U 1 1652 bge0
10.0.0.11 link#1 UHS 1 0 lo0
10.1.0.1 link#4 UHS 0 19548 tun0
xxxxxxxxxxxxxx link#4 UHS 0 0 lo0
localhost link#2 UH 0 18 lo0

I'd like to access the box using SSH through both interfaces. I've configured my ADSL modem to forward SSH traffic to the LAN interface. For testing purposes, I started a nc(1) instance on port 22:

# nc -l 22

The trouble is that outgoing replies are routed through tun0 (the default route) instead of bge0 where the request came in on:

# tcpdump -s 0 -i bge0 -vv port 22
Code:
tcpdump: listening on bge0, link-type EN10MB (Ethernet), capture size 65535 bytes
10:04:10.331575 IP (tos 0x0, ttl 57, id 11513, offset 0, flags [DF], proto TCP (6), length 60)
foo.bar.tld.39704 > 10.0.0.11.ssh: Flags [S], cksum 0x6da1 (correct), seq 3829463700, win 5840, options [mss 1460,sackOK,TS val 1224857197 ecr 0,nop,wscale 6], length 0

# tcpdump -s 0 -i tun0 -vv port 22
Code:
tcpdump: listening on tun0, link-type NULL (BSD loopback), capture size 65535 bytes
10:04:10.332029 IP (tos 0x0, ttl 64, id 2854, offset 0, flags [DF], proto TCP (6), length 40)
10.0.0.11.ssh > foo.bar.tld.39704: Flags [R.], cksum 0x0baf (correct), seq 0, ack 3829463701, win 0, length 0

My pf.conf is as follows (note that sshd(8) is really running on port 10000, I've only used port 22 for testing the connection):

Code:
int_if="bge0"
ext_if="tun0"

set block-policy return
set loginterface $ext_if
set skip on lo

scrub in all

block in log
pass out all

antispoof quick for { lo $int_if $ext_if }

tcp_services="{ 10000 }"
icmp_types="{ echoreq, unreach }"

pass on $ext_if proto tcp to port $tcp_services
pass in inet proto icmp all icmp-type $icmp_types
pass in on $int_if

How can I configure FreeBSD to route outgoing traffic through the same interface the incoming traffic came in on? I figure a static route is not an option, since SSH connections can be initiated from anywhere. I'd also like to keep tun0 as the default gateway.
 
pf.conf(5), maybe?

Code:
     route-to
           The route-to option routes the packet to the specified interface
           with an optional address for the next hop.  When a route-to rule
           creates state, only packets that pass in the same direction as the
           filter rule specifies will be routed in this way.  Packets passing
           in the opposite direction (replies) are not affected and are routed
           normally.

     reply-to
           The reply-to option is similar to route-to, but routes packets that
           pass in the opposite direction (replies) to the specified inter-
           face.  Opposite direction is only defined in the context of a state
           entry, and reply-to is useful only in rules that create state.  It
           can be used on systems with multiple external connections to route
           all outgoing packets of a connection through the interface the
           incoming connection arrived through (symmetric routing enforce-
           ment).

     dup-to
           The dup-to option creates a duplicate of the packet and routes it
           like route-to.  The original packet gets routed as it normally
           would.
 
Thanks, the reply-to keyword was exactly what I wanted. I included it in the inbound rule for $int_if, and now traffic routes swimmingly:
Code:
int_gw="10.0.0.2"
pass in on $int_if reply-to ($int_if $int_gw)
 
Back
Top