Anders Brownworth

Technology and Disruption

Making AppleTV Cross Network Boundaries

Because the AppleTV also acts as the hub for HomeKit devices, it sits in an ambiguous position if your network separates IoT from standard user devices. In my case, I have two internal networks, 10.20.0.0/16 for user devices such as laptops and 10.30.0.0/16 which is where IoT light switches and thermostats reside. I do this primarily to limit how some rogue IoT device might impact the user network.

When streaming content or screen sharing from user devices, the AppleTV seems to belong on the user devices network. (10.20.0.0/16) However, the AppleTV also acts as a hub for HomeKit (IoT) devices as well so that would imply it needs to be there as well. You could opt to put it in a DMZ that both networks can get to but that seems like more work than just keeping it on the one network and poking the correct holes to the other network. (why have 3 networks when you only need 2?)

The way I solved this was to put the AppleTV on the IoT network and allow devices on the user network to find and use it. The trick was to figure out what the AppleTV needed so I could make as minimal a crossover as possible.

It turns out that (amongst other things Bonjour related) the AppleTV uses mDNS (multicast DNS) to resolve names to IP addresses. As these multicast packets aren?t shared between the 10.20.0.0/16 and 10.30.0.0/16 networks (in part because they are on physically separate Ethernet in the case of my AppleTV) I needed a way for those multicast packets to be shared without opening the access between the two networks carte blanche.

Once discovered, I needed a way for the user devices to then get bi-directional sessions to the AppleTV without again opening the kimono. The solution here was to allow user devices to send traffic to the AppleTV but only allow established and related traffic back to the 10.20.0.0/16 network. The effect is the user devices on 10.20.0.0/16 can ping the AppleTV on the 10.30.0.0/16 network but the AppleTV can?t ping devices on the 10.20.0.0/16 network.

I route my networks with Linux so I needed to figure out a way to get the mDNS traffic across the networks in both directions. It turns out this is simple with a little C app called mdns-repeater (https://github.com/anders94/mdns-repeater - my changes are only logging related) By running mdns-repeater on the linux machine that has interfaces on both the IoT network and the user network, I was able to accomplish this.

Getting only packets back to 10.20.0.0/16 that were part of an already established connection was easy with an iptables forwarding rule that matches on ESTABLISHED and RELATED states. (RELATED matches packets that are related to an already ESTABLISHED session) It is similar to how a typical NAT setup works. Entities on the public network can?t interact with hosts on the private network unless it is in response to a request from the private network. While 10.20.0.0/16 hosts can send packets directly to hosts in 10.30.0.0/16, only packets in an established session (or related to an established session) are let back through.

In my case, I have both the 10.20.0.0/16 and 10.30.0.0/16 network on the same ethernet but keep them separate by tagging them as two different VLANS. Simply put, eth1 has two VLANS on it, VLAN 20 which has 10.20.0.0/16 and VLAN 30 which hosts 10.30.0.0/16. All crosstalk between the networks is handled by the Linux machine:

The relevant parts of "ip address show" are:

2: eth1.20@eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 01:e6:64:03:28:27 brd ff:ff:ff:ff:ff:ff
inet 10.20.1.1/16 brd 10.20.255.255 scope global eth2.20
valid_lft forever preferred_lft forever
5: eth1.30@eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue 3tate UP group default qlen 1000
link/ether 01:e6:64:03:28:27 brd ff:ff:ff:ff:ff:ff
inet 10.30.1.1/16 brd 10.30.255.255 scope global eth2.30
valid_lft forever preferred_lft forever

And the relevant iptables setup looks like this: (the default FORWARD policy is DROP)

# internet access
iptables -A FORWARD -i eth2.20 -o eth1 -j ACCEPT
iptables -A FORWARD -i eth2.30 -o eth1 -j ACCEPT
iptables -A FORWARD -i eth1 -o eth2.20 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i eth1 -o eth2.30 -m state --state RELATED,ESTABLISHED -j ACCEPT

# 20 -> 30 but only established 30 -> 20
iptables -A FORWARD -i eth2.20 -o eth2.30 -j ACCEPT
iptables -A FORWARD -i eth2.30 -o eth2.20 -m state --state RELATED,ESTABLISHED -j ACCEPT

This ignores things like the masquerading setup for Internet access but you get the idea.

With mdns-repeater and several lines of iptables, you can fairly cleanly lock down two different networks yet leave the ability to stream content and control IoT devices between the networks. I wouldn?t advise a beginner start here but if you?ve read this far I?m guessing you are past that point.

Fun tweaks:

Because we have split networks, we can easily look fairly closely at what happens when streaming to an AppleTV by checking how much traffic goes between the networks. For example, iptables can show how many bytes are matched at each iptables rule:

iptables -L -n -v
...
Chain FORWARD (policy DROP 52940 packets, 2007K bytes)
pkts bytes target prot opt in out source destination
19M 36G ACCEPT all -- eth1 eth2.20 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
291K 507M ACCEPT all -- eth1 eth2.30 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
9042K 1276M ACCEPT all -- eth2.20 eth1 0.0.0.0/0 0.0.0.0/0
348K 45M ACCEPT all -- eth2.30 eth1 0.0.0.0/0 0.0.0.0/0
247K 27M ACCEPT all -- eth2.20 eth2.30 0.0.0.0/0 0.0.0.0/0
249K 42M ACCEPT all -- eth2.30 eth2.20 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED

So in that last line that shows how much traffic is coming back on established connections, we?ve seen about 42M of data. Right above that we see 27M from VLAN 20 to VLAN 30 which were (amongst other things) the initiation connections. Incoming Internet traffic to the user network (VLAN 20) is about 36G (first line of stats) and the IoT network did about 507M in that same timeframe. Outgoing Internet connections (next 2 lines) are 1,276M for VLAN 20 and 45M for VLAN 30.

You can reset packet counters for the FORWARDING chain with "iptables -Z FORWARD" if you want to watch a little more dynamically.

Another fun tool to watch in realtime is iftop which shows a realtime view of traffic over a specified interface which I?ll leave as an exercise for the reader.

You could also do some traffic shaping between the networks with "tc". For example, you might constrain the maximum Internet bandwidth the IoT network can use or constrain the bandwidth between the private networks. (and of course also do so on an IP by IP basis) In short, there are way more options than one has time to investigate but with a little bit of concentration you can do some fairly powerful stuff with linux and a bunch of off the shelf components!

Note: I've noticed old clients need TCP 7001 and a high UDP port forwarded as well so I've had to add these for the IP address that is my AppleTV (10.30.5.41):

iptables -A FORWARD -s 10.30.5.41/32 -d 10.20.0.0/16 -p tcp -m tcp --dport 7001 -m state --state NEW -j ACCEPT
iptables -A FORWARD -s 10.30.5.41/32 -d 10.20.0.0/16 -p udp -m multiport --dports 49152:65535 -j ACCEPT

Comments (0)

Leave a Comment

Name:
Location: (city / state / country)
Email: (not published / no spam)
Comment:

No HTML is allowed. Cookies must be enabled to post. Your comment will appear on this page after a moderator OKs it. Offensive content will not be published.

Click the umbrella to submit your comment.

To create links in comments:
[link:https://andersbrownworth.com/] becomes https://andersbrownworth.com/
[link:https://andersbrownworth.com/|AndersBrownworth.com] becomes AndersBrownworth.com
Notice there is no rel="nofollow" in these hrefs. Links in comments will carry page rank from this site so only link to things worthy of people's attention.