diff --git a/debian/changelog b/debian/changelog
index 55e1f6b0da2c9bee3333882025a034ff2d3dc17b..cb996edcbe312532a57c01a1efb0b6ed40be0eb8 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,4 +1,4 @@
-riseup-vpn (0.21.2.5) groovy; urgency=medium
+riseup-vpn (0.21.2.6) groovy; urgency=medium
 
   * Initial Release.
 
diff --git a/helpers/bitmask-root b/helpers/bitmask-root
index 6615d3b74106b836983e2549ea0c0feb4b074adc..f105bfc8b42ddd97d996bbd6269cfd87e42b841a 100644
--- a/helpers/bitmask-root
+++ b/helpers/bitmask-root
@@ -43,6 +43,7 @@ The `openvpn start` action is special: it calls exec on openvpn and replaces
 the current process. If the `restart` parameter is passed, the firewall will
 not be teared down in the case of an error during launch.
 """
+import ipaddress
 import os
 import re
 import signal
@@ -83,7 +84,7 @@ def get_no_group_name():
 def tostr(s):
     return s.decode('utf-8')
 
-VERSION = "12"
+VERSION = "13"
 SCRIPT = "bitmask-root"
 NAMESERVER_TCP = "10.41.0.1"
 NAMESERVER_UDP = "10.42.0.1"
@@ -275,6 +276,29 @@ def get_process_list():
     return filter(None, res)
 
 
+def getIPv4AllowAddresses():
+    lines = []
+    try:
+        with open("/etc/bitmask/ipv4.allow", 'r') as f:
+            lines = [l.strip() for l in f.readlines()]
+    except FileNotFoundError:
+        return lines
+
+    lines = filter(lambda x: ipaddress.ip_address(x).version == 4, lines)
+    return list(filter(lambda x: ipaddress.ip_address(x).is_private, lines))
+
+def getIPv6AllowAddresses():
+    lines = []
+    try:
+        with open("/etc/bitmask/ipv6.allow", 'r') as f:
+            lines = [l.strip() for l in f.readlines()]
+    except FileNotFoundError:
+        return lines
+
+    lines = filter(lambda x: ipaddress.ip_address(x).version == 6, lines)
+    return list(filter(lambda x: ipaddress.ip_address(x).is_private, lines))
+
+
 def run(command, *args, **options):
     """
     Run an external command.
@@ -655,6 +679,16 @@ def firewall_start(args):
     local_network_ipv6 = get_local_network_ipv6(default_device)
     gateways = get_gateways(args)
 
+    # allow local address in listed exception list
+    # this will allow all ports and both tcp and udp.
+    def allow4(ip):
+        ip4tables("--append", BITMASK_CHAIN, "--destination", ip,
+                  "-o", default_device, "--jump", "ACCEPT")
+
+    def allow6(ip):
+        ip6tables("--append", BITMASK_CHAIN, "--destination", ip,
+                  "-o", default_device, "--jump", "ACCEPT")
+
     # add custom chain "bitmask" to front of OUTPUT chain for both
     # the 'filter' and the 'nat' tables.
     if not ipv4_chain_exists(BITMASK_CHAIN):
@@ -707,11 +741,14 @@ def firewall_start(args):
                   "--protocol", "tcp", "--dport", "53", "--jump", "MASQUERADE")
 
     # allow local network traffic
+
+    ipv4_exceptions = getIPv4AllowAddresses()
     if local_network_ipv4:
-        # allow local network destinations
-        ip4tables("--append", BITMASK_CHAIN,
-                  "--destination", local_network_ipv4, "-o", default_device,
-                  "--jump", "ACCEPT")
+        if len(ipv4_exceptions) == 0:
+            # allow all local network destinations if no explicit allow rules defined
+            ip4tables("--append", BITMASK_CHAIN,
+                      "--destination", local_network_ipv4, "-o", default_device,
+                      "--jump", "ACCEPT")
         # allow local network sources for DNS
         # (required to allow local network DNS that gets rewritten by NAT
         #  to get passed through so that MASQUERADE can set correct source IP)
@@ -731,10 +768,15 @@ def firewall_start(args):
                   "--protocol", "udp",
                   "--destination", "224.0.0.251", "--dport", "5353",
                   "-o", default_device, "--jump", "RETURN")
+
+
+    ipv6_exceptions = getIPv6AllowAddresses()
     if local_network_ipv6:
-        ip6tables("--append", BITMASK_CHAIN,
-                  "--destination", local_network_ipv6, "-o", default_device,
-                  "--jump", "ACCEPT")
+        if len(ipv6_exceptions) == 0:
+            # allow all local network destinations if no explicit allow rules defined
+            ip6tables("--append", BITMASK_CHAIN,
+                      "--destination", local_network_ipv6, "-o", default_device,
+                      "--jump", "ACCEPT")
         # allow multicast Simple Service Discovery Protocol
         ip6tables("--append", BITMASK_CHAIN,
                   "--protocol", "udp",
@@ -751,12 +793,29 @@ def firewall_start(args):
         ip4tables("--append", BITMASK_CHAIN, "--destination", gateway,
                   "-o", default_device, "--jump", "ACCEPT")
 
+    # TODO allow ipv6 traffic to gws too
+
     # log rejected packets to syslog
     if DEBUG:
         iptables("--append", BITMASK_CHAIN, "-o", default_device,
                  "--jump", "LOG", "--log-prefix", "iptables denied: ",
                  "--log-level", "7")
 
+    # allow explicit private exceptions
+    if len(ipv4_exceptions) != 0:
+        for ip in ipv4_exceptions:
+            allow4(ip)
+        ip4tables("--append", BITMASK_CHAIN,
+                  "--destination", local_network_ipv4, "-o", default_device,
+                  "--jump", "REJECT")
+
+    if len(ipv6_exceptions) != 0:
+        for ip in ipv6_exceptions:
+            allow6(ip)
+        ip6tables("--append", BITMASK_CHAIN,
+                  "--destination", local_network_ipv6, "-o", default_device,
+                  "--jump", "REJECT")
+
     # for now, ensure all other ipv6 packets get rejected (regardless of
     # device). not sure why, but "-p any" doesn't work.
     ip6tables("--append", BITMASK_CHAIN, "-p", "tcp", "--jump", "REJECT")
@@ -766,6 +825,7 @@ def firewall_start(args):
     ip4tables("--append", BITMASK_CHAIN, "-o",
               default_device, "--jump", "REJECT")
 
+
     # On Qubes OS, add anti-leak rules for proxyVM qubes-firewall.service
     # Must stay on 'top' of chain!
     if QUBES_PROXY and QUBES_VER >= 3 and run("grep", "installed\ by\ " +