diff --git a/sshuttle/methods/tproxy.py b/sshuttle/methods/tproxy.py
index 4c02286..5b4f408 100644
--- a/sshuttle/methods/tproxy.py
+++ b/sshuttle/methods/tproxy.py
@@ -118,9 +118,14 @@ class Method(BaseMethod):
                 "-- ignored UDP from %r: "
                 "couldn't determine destination IP address\n" % (srcip,))
             return None
-        return None
+        return srcip, dstip, data
 
     def send_udp(self, sock, srcip, dstip, data):
+        if not srcip:
+            debug1(
+                "-- ignored UDP to %r: "
+                "couldn't determine source IP address\n" % (dstip,))
+            return
         sender = socket.socket(sock.family, socket.SOCK_DGRAM)
         sender.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
         sender.setsockopt(socket.SOL_IP, IP_TRANSPARENT, 1)
diff --git a/sshuttle/tests/test_methods_nat.py b/sshuttle/tests/test_methods_nat.py
new file mode 100644
index 0000000..433c709
--- /dev/null
+++ b/sshuttle/tests/test_methods_nat.py
@@ -0,0 +1,145 @@
+import pytest
+from mock import Mock, patch, call
+import socket
+import struct
+
+from sshuttle.methods import get_method
+
+
+def test_get_supported_features():
+    method = get_method('nat')
+    features = method.get_supported_features()
+    assert not features.ipv6
+    assert not features.udp
+
+
+def test_get_tcp_dstip():
+    sock = Mock()
+    sock.getsockopt.return_value = struct.pack(
+        '!HHBBBB', socket.ntohs(socket.AF_INET), 1024, 127, 0, 0, 1)
+    method = get_method('nat')
+    assert method.get_tcp_dstip(sock) == ('127.0.0.1', 1024)
+    assert sock.mock_calls == [call.getsockopt(0, 80, 16)]
+
+
+def test_recv_udp():
+    sock = Mock()
+    sock.recvfrom.return_value = "11111", "127.0.0.1"
+    method = get_method('nat')
+    result = method.recv_udp(sock, 1024)
+    assert sock.mock_calls == [call.recvfrom(1024)]
+    assert result == ("127.0.0.1", None, "11111")
+
+
+def test_send_udp():
+    sock = Mock()
+    method = get_method('nat')
+    method.send_udp(sock, None, "127.0.0.1", "22222")
+    assert sock.mock_calls == [call.sendto("22222", "127.0.0.1")]
+
+
+def test_setup_tcp_listener():
+    listener = Mock()
+    method = get_method('nat')
+    method.setup_tcp_listener(listener)
+    assert listener.mock_calls == []
+
+
+def test_setup_udp_listener():
+    listener = Mock()
+    method = get_method('nat')
+    method.setup_udp_listener(listener)
+    assert listener.mock_calls == []
+
+
+def test_check_settings():
+    method = get_method('nat')
+    method.check_settings(True, True)
+    method.check_settings(False, True)
+
+
+def test_firewall_command():
+    method = get_method('nat')
+    assert not method.firewall_command("somthing")
+
+
+@patch('sshuttle.methods.nat.ipt')
+@patch('sshuttle.methods.nat.ipt_ttl')
+@patch('sshuttle.methods.nat.ipt_chain_exists')
+def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
+    mock_ipt_chain_exists.return_value = True
+    method = get_method('nat')
+    assert method.name == 'nat'
+
+    with pytest.raises(Exception) as excinfo:
+        method.setup_firewall(
+            1024, 1026,
+            [(10, u'2404:6800:4004:80c::33')],
+            10,
+            [(10, 64, False, u'2404:6800:4004:80c::'),
+                (10, 128, True, u'2404:6800:4004:80c::101f')],
+            True)
+    assert str(excinfo.value) \
+        == 'Address family "AF_INET6" unsupported by nat method_name'
+    assert mock_ipt_chain_exists.mock_calls == []
+    assert mock_ipt_ttl.mock_calls == []
+    assert mock_ipt.mock_calls == []
+
+    with pytest.raises(Exception) as excinfo:
+        method.setup_firewall(
+            1025, 1027,
+            [(2, u'1.2.3.33')],
+            2,
+            [(2, 24, False, u'1.2.3.0'), (2, 32, True, u'1.2.3.66')],
+            True)
+    assert str(excinfo.value) == 'UDP not supported by nat method_name'
+    assert mock_ipt_chain_exists.mock_calls == []
+    assert mock_ipt_ttl.mock_calls == []
+    assert mock_ipt.mock_calls == []
+
+    method.setup_firewall(
+        1025, 1027,
+        [(2, u'1.2.3.33')],
+        2,
+        [(2, 24, False, u'1.2.3.0'), (2, 32, True, u'1.2.3.66')],
+        False)
+    assert mock_ipt_chain_exists.mock_calls == [
+        call(2, 'nat', 'sshuttle-1025')
+    ]
+    assert mock_ipt_ttl.mock_calls == [
+        call(2, 'nat', '-A', 'sshuttle-1025', '-j', 'REDIRECT',
+             '--dest', u'1.2.3.0/24', '-p', 'tcp', '--to-ports', '1025'),
+        call(2, 'nat', '-A', 'sshuttle-1025', '-j', 'REDIRECT',
+             '--dest', u'1.2.3.33/32', '-p', 'udp',
+             '--dport', '53', '--to-ports', '1027')
+    ]
+    assert mock_ipt.mock_calls == [
+        call(2, 'nat', '-D', 'OUTPUT', '-j', 'sshuttle-1025'),
+        call(2, 'nat', '-D', 'PREROUTING', '-j', 'sshuttle-1025'),
+        call(2, 'nat', '-F', 'sshuttle-1025'),
+        call(2, 'nat', '-X', 'sshuttle-1025'),
+        call(2, 'nat', '-N', 'sshuttle-1025'),
+        call(2, 'nat', '-F', 'sshuttle-1025'),
+        call(2, 'nat', '-I', 'OUTPUT', '1', '-j', 'sshuttle-1025'),
+        call(2, 'nat', '-I', 'PREROUTING', '1', '-j', 'sshuttle-1025'),
+        call(2, 'nat', '-A', 'sshuttle-1025', '-j', 'RETURN',
+             '--dest', u'1.2.3.66/32', '-p', 'tcp')
+    ]
+    mock_ipt_chain_exists.reset_mock()
+    mock_ipt_ttl.reset_mock()
+    mock_ipt.reset_mock()
+
+    method.setup_firewall(1025, 0, [], 2, [], False)
+    assert mock_ipt_chain_exists.mock_calls == [
+        call(2, 'nat', 'sshuttle-1025')
+    ]
+    assert mock_ipt_ttl.mock_calls == []
+    assert mock_ipt.mock_calls == [
+        call(2, 'nat', '-D', 'OUTPUT', '-j', 'sshuttle-1025'),
+        call(2, 'nat', '-D', 'PREROUTING', '-j', 'sshuttle-1025'),
+        call(2, 'nat', '-F', 'sshuttle-1025'),
+        call(2, 'nat', '-X', 'sshuttle-1025')
+    ]
+    mock_ipt_chain_exists.reset_mock()
+    mock_ipt_ttl.reset_mock()
+    mock_ipt.reset_mock()
diff --git a/sshuttle/tests/test_methods_tproxy.py b/sshuttle/tests/test_methods_tproxy.py
new file mode 100644
index 0000000..82efed5
--- /dev/null
+++ b/sshuttle/tests/test_methods_tproxy.py
@@ -0,0 +1,272 @@
+from mock import Mock, patch, call
+
+from sshuttle.methods import get_method
+
+
+def test_get_supported_features():
+    method = get_method('tproxy')
+    features = method.get_supported_features()
+    assert features.ipv6
+    assert features.udp
+
+
+def test_get_tcp_dstip():
+    sock = Mock()
+    sock.getsockname.return_value = ('127.0.0.1', 1024)
+    method = get_method('tproxy')
+    assert method.get_tcp_dstip(sock) == ('127.0.0.1', 1024)
+    assert sock.mock_calls == [call.getsockname()]
+
+
+@patch("sshuttle.methods.tproxy.recv_udp")
+def test_recv_udp(mock_recv_udp):
+    mock_recv_udp.return_value = ("127.0.0.1", "127.0.0.2", "11111")
+
+    sock = Mock()
+    method = get_method('tproxy')
+    result = method.recv_udp(sock, 1024)
+    assert sock.mock_calls == []
+    assert mock_recv_udp.mock_calls == [call(sock, 1024)]
+    assert result == ("127.0.0.1", "127.0.0.2", "11111")
+
+
+@patch("sshuttle.methods.socket.socket")
+def test_send_udp(mock_socket):
+    sock = Mock()
+    method = get_method('tproxy')
+    method.send_udp(sock, "127.0.0.2", "127.0.0.1", "2222222")
+    assert sock.mock_calls == []
+    assert mock_socket.mock_calls == [
+        call(sock.family, 2),
+        call().setsockopt(1, 2, 1),
+        call().setsockopt(0, 19, 1),
+        call().bind('127.0.0.2'),
+        call().sendto("2222222", '127.0.0.1'),
+        call().close()
+    ]
+
+
+def test_setup_tcp_listener():
+    listener = Mock()
+    method = get_method('tproxy')
+    method.setup_tcp_listener(listener)
+    assert listener.mock_calls == [
+        call.setsockopt(0, 19, 1)
+    ]
+
+
+def test_setup_udp_listener():
+    listener = Mock()
+    method = get_method('tproxy')
+    method.setup_udp_listener(listener)
+    assert listener.mock_calls == [
+        call.setsockopt(0, 19, 1),
+        call.v4.setsockopt(0, 20, 1),
+        call.v6.setsockopt(41, 74, 1)
+    ]
+
+
+def test_check_settings():
+    method = get_method('tproxy')
+    method.check_settings(True, True)
+    method.check_settings(False, True)
+
+
+def test_firewall_command():
+    method = get_method('tproxy')
+    assert not method.firewall_command("somthing")
+
+
+@patch('sshuttle.methods.tproxy.ipt')
+@patch('sshuttle.methods.tproxy.ipt_ttl')
+@patch('sshuttle.methods.tproxy.ipt_chain_exists')
+def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
+    mock_ipt_chain_exists.return_value = True
+    method = get_method('tproxy')
+    assert method.name == 'tproxy'
+
+    # IPV6
+
+    method.setup_firewall(
+        1024, 1026,
+        [(10, u'2404:6800:4004:80c::33')],
+        10,
+        [(10, 64, False, u'2404:6800:4004:80c::'),
+            (10, 128, True, u'2404:6800:4004:80c::101f')],
+        True)
+    assert mock_ipt_chain_exists.mock_calls == [
+        call(10, 'mangle', 'sshuttle-m-1024'),
+        call(10, 'mangle', 'sshuttle-t-1024'),
+        call(10, 'mangle', 'sshuttle-d-1024')
+    ]
+    assert mock_ipt_ttl.mock_calls == []
+    assert mock_ipt.mock_calls == [
+        call(10, 'mangle', '-D', 'OUTPUT', '-j', 'sshuttle-m-1024'),
+        call(10, 'mangle', '-F', 'sshuttle-m-1024'),
+        call(10, 'mangle', '-X', 'sshuttle-m-1024'),
+        call(10, 'mangle', '-D', 'PREROUTING', '-j', 'sshuttle-t-1024'),
+        call(10, 'mangle', '-F', 'sshuttle-t-1024'),
+        call(10, 'mangle', '-X', 'sshuttle-t-1024'),
+        call(10, 'mangle', '-F', 'sshuttle-d-1024'),
+        call(10, 'mangle', '-X', 'sshuttle-d-1024'),
+        call(10, 'mangle', '-N', 'sshuttle-m-1024'),
+        call(10, 'mangle', '-F', 'sshuttle-m-1024'),
+        call(10, 'mangle', '-N', 'sshuttle-d-1024'),
+        call(10, 'mangle', '-F', 'sshuttle-d-1024'),
+        call(10, 'mangle', '-N', 'sshuttle-t-1024'),
+        call(10, 'mangle', '-F', 'sshuttle-t-1024'),
+        call(10, 'mangle', '-I', 'OUTPUT', '1', '-j', 'sshuttle-m-1024'),
+        call(10, 'mangle', '-I', 'PREROUTING', '1', '-j', 'sshuttle-t-1024'),
+        call(10, 'mangle', '-A', 'sshuttle-d-1024', '-j', 'MARK',
+             '--set-mark', '1'),
+        call(10, 'mangle', '-A', 'sshuttle-d-1024', '-j', 'ACCEPT'),
+        call(10, 'mangle', '-A', 'sshuttle-t-1024', '-m', 'socket',
+             '-j', 'sshuttle-d-1024', '-m', 'tcp', '-p', 'tcp'),
+        call(10, 'mangle', '-A', 'sshuttle-t-1024', '-m', 'socket',
+             '-j', 'sshuttle-d-1024', '-m', 'udp', '-p', 'udp'),
+        call(10, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'MARK',
+             '--set-mark', '1', '--dest', u'2404:6800:4004:80c::33/32',
+             '-m', 'udp', '-p', 'udp', '--dport', '53'),
+        call(10, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'TPROXY',
+             '--tproxy-mark', '0x1/0x1',
+             '--dest', u'2404:6800:4004:80c::33/32',
+             '-m', 'udp', '-p', 'udp', '--dport', '53', '--on-port', '1026'),
+        call(10, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'RETURN',
+             '--dest', u'2404:6800:4004:80c::101f/128',
+             '-m', 'tcp', '-p', 'tcp'),
+        call(10, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'RETURN',
+             '--dest', u'2404:6800:4004:80c::101f/128',
+             '-m', 'tcp', '-p', 'tcp'),
+        call(10, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'RETURN',
+             '--dest', u'2404:6800:4004:80c::101f/128',
+             '-m', 'udp', '-p', 'udp'),
+        call(10, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'RETURN',
+             '--dest', u'2404:6800:4004:80c::101f/128',
+             '-m', 'udp', '-p', 'udp'),
+        call(10, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'MARK',
+             '--set-mark', '1', '--dest', u'2404:6800:4004:80c::/64',
+             '-m', 'tcp', '-p', 'tcp'),
+        call(10, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'TPROXY',
+             '--tproxy-mark', '0x1/0x1', '--dest', u'2404:6800:4004:80c::/64',
+             '-m', 'tcp', '-p', 'tcp', '--on-port', '1024'),
+        call(10, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'MARK',
+             '--set-mark', '1', '--dest', u'2404:6800:4004:80c::/64',
+             '-m', 'udp', '-p', 'udp'),
+        call(10, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'TPROXY',
+             '--tproxy-mark', '0x1/0x1', '--dest', u'2404:6800:4004:80c::/64',
+             '-m', 'udp', '-p', 'udp', '--on-port', '1024')
+    ]
+    mock_ipt_chain_exists.reset_mock()
+    mock_ipt_ttl.reset_mock()
+    mock_ipt.reset_mock()
+
+    method.setup_firewall(1025, 0, [], 10, [], True)
+    assert mock_ipt_chain_exists.mock_calls == [
+        call(10, 'mangle', 'sshuttle-m-1025'),
+        call(10, 'mangle', 'sshuttle-t-1025'),
+        call(10, 'mangle', 'sshuttle-d-1025')
+    ]
+    assert mock_ipt_ttl.mock_calls == []
+    assert mock_ipt.mock_calls == [
+        call(10, 'mangle', '-D', 'OUTPUT', '-j', 'sshuttle-m-1025'),
+        call(10, 'mangle', '-F', 'sshuttle-m-1025'),
+        call(10, 'mangle', '-X', 'sshuttle-m-1025'),
+        call(10, 'mangle', '-D', 'PREROUTING', '-j', 'sshuttle-t-1025'),
+        call(10, 'mangle', '-F', 'sshuttle-t-1025'),
+        call(10, 'mangle', '-X', 'sshuttle-t-1025'),
+        call(10, 'mangle', '-F', 'sshuttle-d-1025'),
+        call(10, 'mangle', '-X', 'sshuttle-d-1025')
+    ]
+    mock_ipt_chain_exists.reset_mock()
+    mock_ipt_ttl.reset_mock()
+    mock_ipt.reset_mock()
+
+    # IPV4
+
+    method.setup_firewall(
+        1025, 1027,
+        [(2, u'1.2.3.33')],
+        2,
+        [(2, 24, False, u'1.2.3.0'), (2, 32, True, u'1.2.3.66')],
+        True)
+    assert mock_ipt_chain_exists.mock_calls == [
+        call(2, 'mangle', 'sshuttle-m-1025'),
+        call(2, 'mangle', 'sshuttle-t-1025'),
+        call(2, 'mangle', 'sshuttle-d-1025')
+    ]
+    assert mock_ipt_ttl.mock_calls == []
+    assert mock_ipt.mock_calls == [
+        call(2, 'mangle', '-D', 'OUTPUT', '-j', 'sshuttle-m-1025'),
+        call(2, 'mangle', '-F', 'sshuttle-m-1025'),
+        call(2, 'mangle', '-X', 'sshuttle-m-1025'),
+        call(2, 'mangle', '-D', 'PREROUTING', '-j', 'sshuttle-t-1025'),
+        call(2, 'mangle', '-F', 'sshuttle-t-1025'),
+        call(2, 'mangle', '-X', 'sshuttle-t-1025'),
+        call(2, 'mangle', '-F', 'sshuttle-d-1025'),
+        call(2, 'mangle', '-X', 'sshuttle-d-1025'),
+        call(2, 'mangle', '-N', 'sshuttle-m-1025'),
+        call(2, 'mangle', '-F', 'sshuttle-m-1025'),
+        call(2, 'mangle', '-N', 'sshuttle-d-1025'),
+        call(2, 'mangle', '-F', 'sshuttle-d-1025'),
+        call(2, 'mangle', '-N', 'sshuttle-t-1025'),
+        call(2, 'mangle', '-F', 'sshuttle-t-1025'),
+        call(2, 'mangle', '-I', 'OUTPUT', '1', '-j', 'sshuttle-m-1025'),
+        call(2, 'mangle', '-I', 'PREROUTING', '1', '-j', 'sshuttle-t-1025'),
+        call(2, 'mangle', '-A', 'sshuttle-d-1025',
+             '-j', 'MARK', '--set-mark', '1'),
+        call(2, 'mangle', '-A', 'sshuttle-d-1025', '-j', 'ACCEPT'),
+        call(2, 'mangle', '-A', 'sshuttle-t-1025', '-m', 'socket',
+             '-j', 'sshuttle-d-1025', '-m', 'tcp', '-p', 'tcp'),
+        call(2, 'mangle', '-A', 'sshuttle-t-1025', '-m', 'socket',
+             '-j', 'sshuttle-d-1025', '-m', 'udp', '-p', 'udp'),
+        call(2, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'MARK',
+             '--set-mark', '1', '--dest', u'1.2.3.33/32',
+             '-m', 'udp', '-p', 'udp', '--dport', '53'),
+        call(2, 'mangle', '-A', 'sshuttle-t-1025', '-j', 'TPROXY',
+             '--tproxy-mark', '0x1/0x1', '--dest', u'1.2.3.33/32',
+             '-m', 'udp', '-p', 'udp', '--dport', '53', '--on-port', '1027'),
+        call(2, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'RETURN',
+             '--dest', u'1.2.3.66/32', '-m', 'tcp', '-p', 'tcp'),
+        call(2, 'mangle', '-A', 'sshuttle-t-1025', '-j', 'RETURN',
+             '--dest', u'1.2.3.66/32', '-m', 'tcp', '-p', 'tcp'),
+        call(2, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'RETURN',
+             '--dest', u'1.2.3.66/32', '-m', 'udp', '-p', 'udp'),
+        call(2, 'mangle', '-A', 'sshuttle-t-1025', '-j', 'RETURN',
+             '--dest', u'1.2.3.66/32', '-m', 'udp', '-p', 'udp'),
+        call(2, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'MARK',
+             '--set-mark', '1', '--dest', u'1.2.3.0/24',
+             '-m', 'tcp', '-p', 'tcp'),
+        call(2, 'mangle', '-A', 'sshuttle-t-1025', '-j', 'TPROXY',
+             '--tproxy-mark', '0x1/0x1', '--dest', u'1.2.3.0/24',
+             '-m', 'tcp', '-p', 'tcp', '--on-port', '1025'),
+        call(2, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'MARK',
+             '--set-mark', '1', '--dest', u'1.2.3.0/24',
+             '-m', 'udp', '-p', 'udp'),
+        call(2, 'mangle', '-A', 'sshuttle-t-1025', '-j', 'TPROXY',
+             '--tproxy-mark', '0x1/0x1', '--dest', u'1.2.3.0/24',
+             '-m', 'udp', '-p', 'udp', '--on-port', '1025')
+    ]
+    mock_ipt_chain_exists.reset_mock()
+    mock_ipt_ttl.reset_mock()
+    mock_ipt.reset_mock()
+
+    method.setup_firewall(1025, 0, [], 2, [], True)
+    assert mock_ipt_chain_exists.mock_calls == [
+        call(2, 'mangle', 'sshuttle-m-1025'),
+        call(2, 'mangle', 'sshuttle-t-1025'),
+        call(2, 'mangle', 'sshuttle-d-1025')
+    ]
+    assert mock_ipt_ttl.mock_calls == []
+    assert mock_ipt.mock_calls == [
+        call(2, 'mangle', '-D', 'OUTPUT', '-j', 'sshuttle-m-1025'),
+        call(2, 'mangle', '-F', 'sshuttle-m-1025'),
+        call(2, 'mangle', '-X', 'sshuttle-m-1025'),
+        call(2, 'mangle', '-D', 'PREROUTING', '-j', 'sshuttle-t-1025'),
+        call(2, 'mangle', '-F', 'sshuttle-t-1025'),
+        call(2, 'mangle', '-X', 'sshuttle-t-1025'),
+        call(2, 'mangle', '-F', 'sshuttle-d-1025'),
+        call(2, 'mangle', '-X', 'sshuttle-d-1025')
+    ]
+    mock_ipt_chain_exists.reset_mock()
+    mock_ipt_ttl.reset_mock()
+    mock_ipt.reset_mock()