summaryrefslogtreecommitdiffstats
path: root/sshuttle/methods/nft.py
diff options
context:
space:
mode:
Diffstat (limited to 'sshuttle/methods/nft.py')
-rw-r--r--sshuttle/methods/nft.py80
1 files changed, 80 insertions, 0 deletions
diff --git a/sshuttle/methods/nft.py b/sshuttle/methods/nft.py
new file mode 100644
index 0000000..cd28a5b
--- /dev/null
+++ b/sshuttle/methods/nft.py
@@ -0,0 +1,80 @@
+import socket
+from sshuttle.firewall import subnet_weight
+from sshuttle.linux import nft, nft_get_handle, nonfatal
+from sshuttle.methods import BaseMethod
+
+
+class Method(BaseMethod):
+
+ # We name the chain based on the transproxy port number so that it's
+ # possible to run multiple copies of sshuttle at the same time. Of course,
+ # the multiple copies shouldn't have overlapping subnets, or only the most-
+ # recently-started one will win (because we use "-I OUTPUT 1" instead of
+ # "-A OUTPUT").
+ def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
+ user):
+ if udp:
+ raise Exception("UDP not supported by nft")
+
+ table = "nat"
+
+ def _nft(action, *args):
+ return nft(family, table, action, *args)
+
+ chain = 'sshuttle-%s' % port
+
+ # basic cleanup/setup of chains
+ _nft('add table', '')
+ _nft('add chain', 'prerouting',
+ '{ type nat hook prerouting priority -100; policy accept; }')
+ _nft('add chain', 'postrouting',
+ '{ type nat hook postrouting priority 100; policy accept; }')
+ _nft('add chain', 'output',
+ '{ type nat hook output priority -100; policy accept; }')
+ _nft('add chain', chain)
+ _nft('flush chain', chain)
+ _nft('add rule', 'output jump %s' % chain)
+ _nft('add rule', 'prerouting jump %s' % chain)
+
+ # create new subnet entries.
+ for _, swidth, sexclude, snet, fport, lport \
+ in sorted(subnets, key=subnet_weight, reverse=True):
+ tcp_ports = ('ip', 'protocol', 'tcp')
+ if fport:
+ tcp_ports = tcp_ports + ('dport { %d-%d }' % (fport, lport))
+
+ if sexclude:
+ _nft('add rule', chain, *(tcp_ports + (
+ 'ip daddr %s/%s' % (snet, swidth), 'return')))
+ else:
+ _nft('add rule', chain, *(tcp_ports + (
+ 'ip daddr %s/%s' % (snet, swidth), 'ip ttl != 42',
+ ('redirect to :' + str(port)))))
+
+ for _, ip in [i for i in nslist if i[0] == family]:
+ if family == socket.AF_INET:
+ _nft('add rule', chain, 'ip protocol udp ip daddr %s' % ip,
+ 'udp dport { 53 }', 'ip ttl != 42',
+ ('redirect to :' + str(dnsport)))
+ elif family == socket.AF_INET6:
+ _nft('add rule', chain, 'ip6 protocol udp ip6 daddr %s' % ip,
+ 'udp dport { 53 }', 'ip ttl != 42',
+ ('redirect to :' + str(dnsport)))
+
+ def restore_firewall(self, port, family, udp, user):
+ if udp:
+ raise Exception("UDP not supported by nft method_name")
+
+ table = "nat"
+
+ def _nft(action, *args):
+ return nft(family, table, action, *args)
+
+ chain = 'sshuttle-%s' % port
+
+ # basic cleanup/setup of chains
+ handle = nft_get_handle('chain ip nat output', chain)
+ nonfatal(_nft, 'delete rule', 'output', handle)
+ handle = nft_get_handle('chain ip nat prerouting', chain)
+ nonfatal(_nft, 'delete rule', 'prerouting', handle)
+ nonfatal(_nft, 'delete chain', chain)