Hi Mike. Here's a couple of points.
First, Windows uses ICMP only on traceroute (tracert) so there's consistency between your Windows and FreeBSD internal hosts - it's an ICMP blocked (in or out) issue. http://technet.microsoft.com/en-us/library/cc940128.aspx Can you ping and traceroute your router from your internal hosts? Can you go the other way? Second, and here we go into grey area, I'm no expert at the pf thing and I do it slightly different to you. However, I use a simple ruleset and don't explicitly allow ICMP ... and yet it works from internal Windows and OpenBSD hosts. Here is the basics (in case there's a clue there): # options set block-policy return set debug urgent set loginterface pppoe0 set optimization normal set reassemble no set require-order yes set ruleset-optimization basic set skip on lo #set state-defaults set state-policy if-bound #set timeout # traffic normalization antispoof quick for lo inet antispoof quick for vr1 inet antispoof quick for vr2 inet # packet filtering block all # pppoe0:network match in log on pppoe0 pass out on pppoe0 inet from (pppoe0) to any pass out on pppoe0 inet from vr1:network nat-to (pppoe0) pass out on pppoe0 inet from vr2:network nat-to (pppoe0) #pass in on pppoe0 inet proto icmp from any to (pppoe0) icmp-type 8 code 0 # vr1:network pass in on vr1 inet from vr1:network to any pass out on vr1 inet from vr1 to vr1:network pass out on vr1 inet from vr2:network to vr1:network # vr2:network pass in on vr2 inet from vr2:network to any pass out on vr2 inet from vr2 to vr2:network pass out on vr2 inet from vr1:network to vr2:network Most or all of the "options" are default. The commented icmp line is to allow outsiders to icmp echo request this machine and get a reply. I've commented it to make sure it's not why mine works and yours doesn't. There's a few items in the pf.conf man page that lead me to guess that care needs to be taken with ICMP (as far as state and UDP and TCP being directly referenced but ICMP requests requiring special care). For ICMP, pass out/in ping queries. State matching is done on host addresses and ICMP ID (not type/code), so replies (like 0/0 for 8/0) will match queries. ICMP error messages (which always refer to a TCP/UDP packet) are handled by the TCP/UDP states. pass on $ext_if inet proto icmp all icmp-type 8 code 0 Furthermore, correct handling of ICMP error messages is critical to many protocols, particularly TCP. pf(4) matches ICMP error messages to the correct connection, checks them against connection parameters, and passes them if appropriate. For example if an ICMP source quench message referring to a stateful TCP connection arrives, it will be matched to the state and get passed. pass out inet proto icmp all icmp-type echoreq Etcetera. Like I said I'm guessing but it might be a state issue (a design feature) and something to do with the order of your match/block versus my block/pass - I notice in the man page that ICMP is treated as a special case (see "block") and also this: set block-policy The block-policy option sets the default behaviour for the packet block action: drop Packet is silently dropped. return A TCP RST is returned for blocked TCP packets, an ICMP UNREACHABLE is returned for blocked UDP packets, and all other packets are silently dropped. So on. I'd try removing your "block in" for testing. Consider adding a rule (flavour as necessary): pass out inet proto icmp all icmp-type echoreq Best wishes.

