Some guesswork below so caveat emptor. It is however a very typical setup which confuses many people who start out by conflating routing, packet filtering (firewall) and NAT (Network Address Translation).
You do not state it clearly - but I would guess that you network looks like this:
Internet <-A-> SOHO Router <-B-> Server/workstation <-C-> VM
Your DNS server is on the B network which is 192.168.1.0/24
I am guessing that you Internet SOHO router is 192.168.1.1 and is set as default gateway for the network. This would be an extremely common setup.
You state yourself that the DNS server is on 192.168.1.2 and the servers bridged interface is 192.168.1.10. Behind that you have the network 10.9.9.0/24.
Your iptables setup will forward all packets on the interface. In practice you send all packet from one network to the other - even local packets. That is the important difference.
In your pf config you do not forward all packets on the interface. You have specified a network em1:network. We do not have the full config but I would guess that you actually have a nice and working barebones configuration. What bites you is the missing routes.
When you send a packet from the 10.9.9.0/24 it will reach the 192.168.1.0/24 net. Your server has a leg into that net so you will reach your DNS directly. But the DNS server on the B network has no clue how to reach the non-local C 10.9.9.0/24 network. All responses would then be sent to the "default router" which I guess is your SOHO router. This router also only knows where to find the 192.168.1.0/24 network (not 10.9.9.0/24) and would normally route everything to your external Internet link. In this case you are using proper private addresses - so the packet would instead be dropped as private addresses are not routed on the Internet.
The "proper" solution would be to setup a route on your SOHO router which tells it to route packets for 10.9.9.0/24 to 192.168.1.10. A decent router will allow you to do that. Many cheap SOHO routers unfortunately do not. In that case you could add the route on your DNS server to test it.
- The reason it works for you with iptables is that the response packet is seen on the eth0 interface and all packets are forwarded. All traffic on the B net is sent to the C net (and reverse). This includes traffic which could/should have stayed local. In effect you have setup a network bridge.
- The reason it does not work for you with your first pf setup is that you have specified what network you expect to see. Only one machine on the B net knows where to find the C net. This is 192.168.1.10 as it has an interface on the C net. In effect you have setup a basic firewall. Filtering is ready but you do not filter anything yet. But routing is missing.
- The reason it works in your second pf config (your own answer) is that you NAT the 10.9.9.0/24 network into the address space of 192.168.1.0/24. All traffic from the C net 10.9.9.0/24 will on the B net appear to come from 192.168.1.10. NAT should avoided whenever possible and only be used as a last resort.
If you do not need/want to filter packages - then I would advice you to not use a firewall. What you are trying to do should be handled by simple routing.