Bonjour Gateway

Why would you want a Bonjour Gateway? Because you have more than 1 network and you want to have Bonjour devices be able to talk to each other even though they aren’t on the same network.

For example, at work, you have Apple TVs that are on one network, and guests that connect to a different network and they need to be able to present using the Apple TV. Why do I keep saying Bonjour instead of mDNS? They are, for the purposes of this article, equivalent, and I have to pick one. Since its more likely that people of an Apple bent will be interested in this, I’m using the Apple terminology.

Prerequisites

When building your machine (physical or virtual), you will either need 2 NICs (network interface card), or the ability to use a trunk port and multiple VLANs. If you choose the latter, don’t forget to adjust these settings as you go.

FreeBSD Install

Standard FreeBSD install. Used pkg to install the following:

  1accessibility/atk
  2converters/libiconv
  3databases/gdbm
  4devel/dbus
  5devel/dbus-glib
  6devel/gettext-runtime
  7devel/gettext-tools
  8devel/glib20
  9devel/gobject-introspection
 10devel/icu
 11devel/libdaemon
 12devel/libdevq
 13devel/libedit
 14devel/libevent2
 15devel/libffi
 16devel/libglade2
 17devel/libpciaccess
 18devel/libpthread-stubs
 19devel/llvm37
 20devel/pcre
 21devel/pkgconf
 22dns/libidn
 23editors/vim
 24emulators/open-vm-tools-nox11
 25emulators/tpm-emulator
 26graphics/cairo
 27graphics/gbm
 28graphics/gdk-pixbuf2
 29graphics/graphite2
 30graphics/gtk-update-icon-cache
 31graphics/jasper
 32graphics/jbigkit
 33graphics/jpeg-turbo
 34graphics/libEGL
 35graphics/libGL
 36graphics/libdrm
 37graphics/libglapi
 38graphics/png
 39graphics/tiff
 40lang/perl5.20
 41lang/python
 42lang/python2
 43lang/python27
 44math/gmp
 45misc/hicolor-icon-theme
 46misc/pciids
 47misc/shared-mime-info
 48net/avahi
 49net/avahi-app
 50net/avahi-autoipd
 51net/avahi-gtk
 52net/avahi-header
 53net/libdnet
 54ports-mgmt/dialog4ports
 55ports-mgmt/pkg
 56print/cups
 57print/freetype2
 58print/harfbuzz
 59print/indexinfo
 60print/libpaper
 61security/ca_root_nss
 62security/gnutls
 63security/libtasn1
 64security/nettle
 65security/p11-kit
 66security/sudo
 67security/trousers
 68sysutils/gnome_subr
 69sysutils/tmux
 70textproc/expat2
 71textproc/libxml2
 72textproc/xmlcatmgr
 73x11-fonts/dejavu
 74x11-fonts/encodings
 75x11-fonts/font-bh-ttf
 76x11-fonts/font-misc-ethiopic
 77x11-fonts/font-misc-meltho
 78x11-fonts/font-util
 79x11-fonts/fontconfig
 80x11-fonts/libXft
 81x11-fonts/libfontenc
 82x11-fonts/mkfontdir
 83x11-fonts/mkfontscale
 84x11-fonts/xorg-fonts-truetype
 85x11-toolkits/gtk20
 86x11-toolkits/libXt
 87x11-toolkits/pango
 88x11/compositeproto
 89x11/damageproto
 90x11/dri2proto
 91x11/fixesproto
 92x11/glproto
 93x11/inputproto
 94x11/kbproto
 95x11/libICE
 96x11/libSM
 97x11/libX11
 98x11/libXau
 99x11/libXcomposite
100x11/libXcursor
101x11/libXdamage
102x11/libXdmcp
103x11/libXext
104x11/libXfixes
105x11/libXi
106x11/libXinerama
107x11/libXrandr
108x11/libXrender
109x11/libXv
110x11/libXvMC
111x11/libXxf86vm
112x11/libxcb
113x11/libxshmfence
114x11/pixman
115x11/randrproto
116x11/renderproto
117x11/videoproto
118x11/xcb-util
119x11/xcb-util-renderutil
120x11/xextproto
121x11/xf86vidmodeproto
122x11/xineramaproto
123x11/xproto

rc.conf settings:

 1hostname="bonjour-bridge.cmhome"
 2ifconfig_em0="DHCP"
 3ifconfig_em1="inet 198.18.9.10/24"
 4ifconfig_em0_ipv6="inet6 accept_rtadv"
 5ifconfig_em1_ipv6="inet6 fc00:101:ca7:9::10/64"
 6sshd_enable="YES"
 7ntpd_enable="YES"
 8ntpd_sync_on_start="YES"
 9dumpdev="AUTO"
10vmware_guest_vmblock_enable="YES"
11vmware_guest_vmhgfs_enable="YES"
12vmware_guest_vmmemctl_enable="YES"
13vmware_guest_vmxnet_enable="YES"
14vmware_guestd_enable="YES"
15avahi_daemon_enable="YES"
16dbus_enable="YES"
17pf_enable="YES"
18pflog_enable="YES"

I picked a static IP on one side, and left the other DHCP. Doesn’t really matter how you do it. Could be static on both sides, probably don’t want to do DHCP on both sides unless you ensure you don’t pick up 2 default routes.

Note: If you aren’t in a virtual machine, skip the open-vm-tools-nox11 pkg, and the vmware-* sysrc knobs.

Configuring Avahi

Literally, the only line I changed in avahi-daemon.conf was (uncomment):

1enable-reflector=no

Enable Avahi (and DBUS):

1sysrc avahi_daemon_enable=YES
2sysrc dbus_enable=YES

Start the services:

1service avahi-daemon start
2service dbus start

You should see something like the following, indicating the process are running:

1[louisk@bonjour-bridge avahi 34 ]$ ps ax | egrep 'dbus|avahi'
2 670  -  Is     0:00.00 /usr/local/bin/dbus-daemon --system
3 674  -  S      0:03.21 avahi-daemon: running [bonjour-bridge.local] (avahi-dae
4[louisk@bonjour-bridge avahi 35 ]$

Configuring PF

I wanted to block essentially everything but Bonjour (mDNS).

  1#
  2# Based on Calomel.org pf.conf
  3#
  4################ FreeBSD pf.conf ##########################
  5# Required order: options, normalization, queueing, translation, filtering.
  6# Note: translation rules are first match while filter rules are last match.
  7################ Macros ###################################
  8
  9### Interfaces ###
 10 ExtIf ="em0"
 11 IntIf ="em1"
 12
 13### Queues, States and Types ###
 14 IcmpPing ="icmp-type 8 code 0"
 15 SshQueue ="(ssh_bulk, ssh_login)"
 16 SynState ="flags S/UAPRSF synproxy state"
 17 TcpState ="flags S/UAPRSF modulate state"
 18 UdpState ="keep state"
 19
 20### Ports ###
 21 AntiScanPort="{23:79, 6000:8000}"
 22 BlockNoLogPort="{17500,445,136:139}"
 23
 24### Stateful Tracking Options (STO) ###
 25 OpenSTO     ="(max 500, source-track rule, max-src-conn 10, max-src-nodes 256)"
 26 SshSTO      ="(max  10, source-track rule, max-src-conn 10, max-src-nodes  10, max-src-conn-rate 5/3,  overload <BLOCKTEMP> flush global)"
 27 WebSTO      ="(max 100, source-track rule, max-src-conn 10, max-src-nodes  10, max-src-conn-rate 5/10, overload <BLOCKTEMP> flush global)"
 28 AntiScanSTO ="(max  10, source-track rule, max-src-conn  1, max-src-nodes  10, max-src-conn-rate 1/60, overload <BLACKTEMP> flush global)"
 29
 30### Tables ###
 31 table <BLOCKTEMP> counters
 32
 33################ Options ######################################################
 34### Misc Options
 35 set skip on lo
 36 set debug urgent
 37 set block-policy drop
 38 set loginterface $ExtIf
 39 set loginterface $IntIf
 40 set state-policy if-bound
 41 set fingerprints "/etc/pf.os"
 42 set ruleset-optimization none
 43
 44### Timeout Options
 45 set optimization normal
 46 set timeout { tcp.closing 60, tcp.established 7200}
 47
 48################ Normalization ###############################################
 49# set-tos 0x1c is Maximize-Reliability + Minimize-Delay + Maximize-Throughput
 50scrub out log on $ExtIf all random-id min-ttl 15 set-tos 0x1c fragment reassemble
 51scrub out log on $IntIf all random-id min-ttl 15 set-tos 0x1c fragment reassemble
 52
 53################ Filtering ###################################################
 54# Rules are best (closest) match. Notice we optimized the rules so external
 55# interface parsing is first followed by the internal interface.
 56
 57### Things to block silently
 58 block drop in quick      on $ExtIf inet proto tcp from any         to any	port $BlockNoLogPort
 59 block drop in quick      on $ExtIf inet proto udp from any         to any	port $BlockNoLogPort
 60
 61### $ExtIf block abusive hosts in temp tables
 62 block drop in  log quick on $ExtIf                from <BLOCKTEMP> to any
 63 block drop in  log quick on $IntIf                from <BLOCKTEMP> to any
 64
 65### $ExtIf default block with drop
 66 block drop in  log       on $ExtIf
 67 block drop in  log       on $IntIf
 68
 69### $ExtIf $IntIf inbound
 70# DHCP
 71 pass in       on $ExtIf inet   proto tcp   from any         to any         port 67:68         $UdpState $OpenSTO
 72 pass in       on $ExtIf inet   proto udp   from any         to any         port 67:68         $UdpState $OpenSTO
 73 pass in       on $ExtIf inet   proto tcp   from !($ExtIf)   to 224.0.0.251 port 5353:5354     $UdpState $OpenSTO
 74 pass in       on $ExtIf inet   proto udp   from !($ExtIf)   to 224.0.0.251 port 5353:5354     $UdpState $OpenSTO
 75 pass in       on $IntIf inet   proto tcp   from !($IntIf)   to 224.0.0.251 port 5353:5354     $UdpState $OpenSTO
 76 pass in       on $IntIf inet   proto udp   from !($IntIf)   to 224.0.0.251 port 5353:5354     $UdpState $OpenSTO
 77 pass in       on $ExtIf inet   proto tcp   from !($ExtIf)   to ($ExtIf)    port ssh           $TcpState $SshSTO
 78 pass in       on $ExtIf inet6  proto tcp   from !($ExtIf)   to ($ExtIf)    port ssh           $TcpState $SshSTO
 79 pass in       on $ExtIf inet   proto icmp  from !($ExtIf)   to any                            $UdpState $OpenSTO
 80 pass in       on $IntIf inet   proto icmp  from !($IntIf)   to any                            $UdpState $OpenSTO
 81 pass in       on $ExtIf inet6  proto icmp6 from !($ExtIf)   to any                            $UdpState $OpenSTO
 82 pass in       on $IntIf inet6  proto icmp6 from !($IntIf)   to any                            $UdpState $OpenSTO
 83 pass in log   on $ExtIf inet   proto tcp   from any         to any         port $AntiScanPort $SynState $AntiScanSTO
 84 pass in log   on $IntIf inet   proto tcp   from any         to any         port $AntiScanPort $SynState $AntiScanSTO
 85 pass in log   on $ExtIf inet6  proto tcp   from any         to any         port $AntiScanPort $SynState $AntiScanSTO
 86 pass in log   on $IntIf inet6  proto tcp   from any         to any         port $AntiScanPort $SynState $AntiScanSTO
 87
 88### $ExtIf $IntIf outbound
 89# DHCP
 90 pass out      on $ExtIf inet   proto tcp   from any         to any         port 67:68         $UdpState $OpenSTO
 91 pass out      on $ExtIf inet   proto udp   from any         to any         port 67:68         $UdpState $OpenSTO
 92# MDNS
 93 pass out      on $ExtIf inet   proto tcp   from ($ExtIf)    to 224.0.0.251 port 5353:5354     $UdpState $OpenSTO
 94 pass out      on $ExtIf inet   proto udp   from ($ExtIf)    to 224.0.0.251 port 5353:5354     $UdpState $OpenSTO
 95 pass out      on $IntIf inet   proto tcp   from ($ExtIf)    to 224.0.0.251 port 5353:5354     $UdpState $OpenSTO
 96 pass out      on $IntIf inet   proto udp   from ($ExtIf)    to 224.0.0.251 port 5353:5354     $UdpState $OpenSTO
 97
 98 pass out      on $ExtIf inet   proto tcp   from ($ExtIf)    to any                            $TcpState $OpenSTO
 99 pass out      on $IntIf inet   proto tcp   from ($ExtIf)    to any                            $TcpState $OpenSTO
100 pass out      on $ExtIf inet6  proto tcp   from ($ExtIf)    to any                            $TcpState $OpenSTO
101 pass out      on $IntIf inet6  proto tcp   from ($ExtIf)    to any                            $TcpState $OpenSTO
102 pass out      on $ExtIf inet   proto udp   from ($ExtIf)    to any                            $UdpState $OpenSTO
103 pass out      on $IntIf inet   proto udp   from ($ExtIf)    to any                            $UdpState $OpenSTO
104 pass out      on $ExtIf inet6  proto udp   from ($ExtIf)    to any                            $UdpState $OpenSTO
105 pass out      on $IntIf inet6  proto udp   from ($ExtIf)    to any                            $UdpState $OpenSTO
106 pass out      on $ExtIf inet   proto icmp  from ($ExtIf)    to any                            $UdpState $OpenSTO
107 pass out      on $IntIf inet   proto icmp  from ($ExtIf)    to any                            $UdpState $OpenSTO
108 pass out      on $ExtIf inet6  proto icmp6 from ($ExtIf)    to any                            $UdpState $OpenSTO
109 pass out      on $IntIf inet6  proto icmp6 from ($ExtIf)    to any                            $UdpState $OpenSTO
110
111############# END of FreeBSD pf.conf #######################

Enable pf and pflog with:

1sysrc pf_enable=YES
2sysrc pflog_enable=YES

Start the services (starting pf will kill any remote sessions such as ssh, but if things are configured properly, you will not be locked out, just existing sessions are terminated):

1service pflog start
2service pf start

You can check that pf is running by doing:

1pfctl -s rules

You should see output like:

 1[louisk@bonjour-bridge avahi 35 ]$ sudo pfctl -s rules
 2scrub out log on em0 all random-id min-ttl 15 set-tos 0x1c fragment reassemble
 3scrub out log on em1 all random-id min-ttl 15 set-tos 0x1c fragment reassemble
 4block drop in quick on em0 inet proto tcp from any to any port = 17500
 5block drop in quick on em0 inet proto tcp from any to any port = microsoft-ds
 6block drop in quick on em0 inet proto tcp from any to any port 136:139
 7block drop in quick on em0 inet proto udp from any to any port = 17500
 8block drop in quick on em0 inet proto udp from any to any port = microsoft-ds
 9block drop in quick on em0 inet proto udp from any to any port 136:139
10block drop in log quick on em0 from <BLOCKTEMP> to any
11block drop in log quick on em1 from <BLOCKTEMP> to any
12block drop in log on em0 all
13block drop in log on em1 all
14pass in on em0 inet proto tcp from any to any port 67:68 flags S/SA keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
15pass in on em0 inet proto udp from any to any port 67:68 keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
16pass in on em0 inet proto tcp from ! (em0) to 224.0.0.251 port 5353:5354 flags S/SA keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
17pass in on em0 inet proto udp from ! (em0) to 224.0.0.251 port 5353:5354 keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
18pass in on em1 inet proto tcp from ! (em1) to 224.0.0.251 port 5353:5354 flags S/SA keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
19pass in on em1 inet proto udp from ! (em1) to 224.0.0.251 port 5353:5354 keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
20pass in on em0 inet proto tcp from ! (em0) to (em0) port = ssh flags S/FSRPAU modulate state (max 10, source-track rule, max-src-conn 10, max-src-conn-rate 5/3, max-src-nodes 10, overload <BLOCKTEMP> flush global, if-bound, adaptive.start 6, adaptive.end 12, src.track 3)
21pass in on em0 inet6 proto tcp from ! (em0) to (em0) port = ssh flags S/FSRPAU modulate state (max 10, source-track rule, max-src-conn 10, max-src-conn-rate 5/3, max-src-nodes 10, overload <BLOCKTEMP> flush global, if-bound, adaptive.start 6, adaptive.end 12, src.track 3)
22pass in on em0 inet proto icmp from ! (em0) to any keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
23pass in on em1 inet proto icmp from ! (em1) to any keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
24pass in on em0 inet6 proto ipv6-icmp from ! (em0) to any keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
25pass in on em1 inet6 proto ipv6-icmp from ! (em1) to any keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
26pass in log on em0 inet proto tcp from any to any port 23:79 flags S/FSRPAU synproxy state (max 10, source-track rule, max-src-conn 1, max-src-conn-rate 1/60, max-src-nodes 10, overload <BLACKTEMP> flush global, if-bound, adaptive.start 6, adaptive.end 12, src.track 60)
27pass in log on em0 inet proto tcp from any to any port 6000:8000 flags S/FSRPAU synproxy state (max 10, source-track rule, max-src-conn 1, max-src-conn-rate 1/60, max-src-nodes 10, overload <BLACKTEMP> flush global, if-bound, adaptive.start 6, adaptive.end 12, src.track 60)
28pass in log on em1 inet proto tcp from any to any port 23:79 flags S/FSRPAU synproxy state (max 10, source-track rule, max-src-conn 1, max-src-conn-rate 1/60, max-src-nodes 10, overload <BLACKTEMP> flush global, if-bound, adaptive.start 6, adaptive.end 12, src.track 60)
29pass in log on em1 inet proto tcp from any to any port 6000:8000 flags S/FSRPAU synproxy state (max 10, source-track rule, max-src-conn 1, max-src-conn-rate 1/60, max-src-nodes 10, overload <BLACKTEMP> flush global, if-bound, adaptive.start 6, adaptive.end 12, src.track 60)
30pass in log on em0 inet6 proto tcp from any to any port 23:79 flags S/FSRPAU synproxy state (max 10, source-track rule, max-src-conn 1, max-src-conn-rate 1/60, max-src-nodes 10, overload <BLACKTEMP> flush global, if-bound, adaptive.start 6, adaptive.end 12, src.track 60)
31pass in log on em0 inet6 proto tcp from any to any port 6000:8000 flags S/FSRPAU synproxy state (max 10, source-track rule, max-src-conn 1, max-src-conn-rate 1/60, max-src-nodes 10, overload <BLACKTEMP> flush global, if-bound, adaptive.start 6, adaptive.end 12, src.track 60)
32pass in log on em1 inet6 proto tcp from any to any port 23:79 flags S/FSRPAU synproxy state (max 10, source-track rule, max-src-conn 1, max-src-conn-rate 1/60, max-src-nodes 10, overload <BLACKTEMP> flush global, if-bound, adaptive.start 6, adaptive.end 12, src.track 60)
33pass in log on em1 inet6 proto tcp from any to any port 6000:8000 flags S/FSRPAU synproxy state (max 10, source-track rule, max-src-conn 1, max-src-conn-rate 1/60, max-src-nodes 10, overload <BLACKTEMP> flush global, if-bound, adaptive.start 6, adaptive.end 12, src.track 60)
34pass out on em0 inet proto tcp from any to any port 67:68 flags S/SA keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
35pass out on em0 inet proto udp from any to any port 67:68 keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
36pass out on em0 inet proto tcp from (em0) to 224.0.0.251 port 5353:5354 flags S/SA keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
37pass out on em0 inet proto udp from (em0) to 224.0.0.251 port 5353:5354 keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
38pass out on em1 inet proto tcp from (em0) to 224.0.0.251 port 5353:5354 flags S/SA keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
39pass out on em1 inet proto udp from (em0) to 224.0.0.251 port 5353:5354 keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
40pass out on em0 inet proto tcp from (em0) to any flags S/FSRPAU modulate state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
41pass out on em1 inet proto tcp from (em0) to any flags S/FSRPAU modulate state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
42pass out on em0 inet6 proto tcp from (em0) to any flags S/FSRPAU modulate state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
43pass out on em1 inet6 proto tcp from (em0) to any flags S/FSRPAU modulate state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
44pass out on em0 inet proto udp from (em0) to any keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
45pass out on em1 inet proto udp from (em0) to any keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
46pass out on em0 inet6 proto udp from (em0) to any keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
47pass out on em1 inet6 proto udp from (em0) to any keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
48pass out on em0 inet proto icmp from (em0) to any keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
49pass out on em1 inet proto icmp from (em0) to any keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
50pass out on em0 inet6 proto ipv6-icmp from (em0) to any keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
51pass out on em1 inet6 proto ipv6-icmp from (em0) to any keep state (max 500, source-track rule, max-src-conn 10, max-src-nodes 256, if-bound, adaptive.start 300, adaptive.end 600)
52[louisk@bonjour-bridge avahi 36 ]$

If you need to debug the firewall rules, you can run tcpdump against pflog0 and see whats getting blocked.

Summary

You should now be able to have your (as an example) Apple TV on one network and your phone/computer on another network and be able to use AirPlay. The Bonjour Gateway will block incoming traffic that isn’t part of mDNS, while allowing your airplay to function as expected.

Footnotes and References

Copyright

Comments