Skip to content

Commit 4c31bc0

Browse files
committed
add anchor rule directly
1 parent 8404708 commit 4c31bc0

File tree

1 file changed

+50
-10
lines changed

1 file changed

+50
-10
lines changed

src/firewall.py

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,9 @@ def do_pf(port, dnsport, family, subnets, udp):
480480
filtering_rules = []
481481

482482
if subnets:
483+
pf_add_anchor_rule(PF_PASS, "sshuttle")
484+
pf_add_anchor_rule(PF_RDR, "sshuttle")
485+
483486
include_subnets = filter(lambda s:not s[2], sorted(subnets, reverse=True))
484487
if include_subnets:
485488
tables.append('table <include_subnets> {%s}' % ','.join(["%s/%s" % (n[3], n[1]) for n in include_subnets]))
@@ -501,10 +504,9 @@ def do_pf(port, dnsport, family, subnets, udp):
501504
with open(pf_config_file, 'w+') as f:
502505
f.write('\n'.join(tables + translating_rules + filtering_rules) + '\n')
503506

504-
pfctl('-Ef', pf_config_file)
505-
os.remove(pf_config_file)
507+
pfctl('-E', '-a', 'sshuttle', '-f', pf_config_file)
506508
else:
507-
pfctl('-dF', 'all')
509+
pfctl('-a', 'sshuttle', '-F', 'all')
508510

509511

510512
def program_exists(name):
@@ -590,14 +592,34 @@ class pfioc_natlook(Structure):
590592
("proto_variant", c_uint8),
591593
("direction", c_uint8)]
592594

595+
pfioc_rule = c_char * 3104 # sizeof(struct pfioc_rule)
596+
597+
pfioc_pooladdr = c_char * 1136 # sizeof(struct pfioc_pooladdr)
598+
599+
MAXPATHLEN = 1024
600+
593601
DIOCNATLOOK = ((0x40000000L | 0x80000000L) | ((sizeof(pfioc_natlook) & 0x1fff) << 16) | ((ord('D')) << 8) | (23))
602+
DIOCCHANGERULE = ((0x40000000L | 0x80000000L) | ((sizeof(pfioc_rule) & 0x1fff) << 16) | ((ord('D')) << 8) | (26))
603+
DIOCBEGINADDRS = ((0x40000000L | 0x80000000L) | ((sizeof(pfioc_pooladdr) & 0x1fff) << 16) | ((ord('D')) << 8) | (51))
604+
605+
PF_CHANGE_ADD_TAIL = 2
606+
PF_CHANGE_GET_TICKET = 6
607+
608+
PF_PASS = 0
609+
PF_RDR = 8
610+
594611
PF_OUT = 2
595612

596613
_pf_fd = None
597614

598-
def query_pf_nat(family, proto, src_ip, src_port, dst_ip, dst_port):
615+
def pf_get_dev():
599616
global _pf_fd
600-
617+
if _pf_fd == None:
618+
_pf_fd = os.open('/dev/pf', os.O_RDWR)
619+
620+
return _pf_fd
621+
622+
def pf_query_nat(family, proto, src_ip, src_port, dst_ip, dst_port):
601623
[proto, family, src_port, dst_port] = [int(v) for v in [proto, family, src_port, dst_port]]
602624

603625
length = 4 if family == socket.AF_INET else 16
@@ -611,15 +633,33 @@ def query_pf_nat(family, proto, src_ip, src_port, dst_ip, dst_port):
611633
memmove(addressof(pnl.daddr), socket.inet_pton(pnl.af, dst_ip), length)
612634
pnl.dxport.port = socket.htons(dst_port)
613635

614-
if _pf_fd == None:
615-
_pf_fd = open('/dev/pf', 'r')
616-
617-
ioctl(_pf_fd, DIOCNATLOOK, (c_char * sizeof(pnl)).from_address(addressof(pnl)))
636+
ioctl(pf_get_dev(), DIOCNATLOOK, (c_char * sizeof(pnl)).from_address(addressof(pnl)))
618637

619638
ip = socket.inet_ntop(pnl.af, (c_char * length).from_address(addressof(pnl.rdaddr)))
620639
port = socket.ntohs(pnl.rdxport.port)
621640
return (ip, port)
622641

642+
def pf_add_anchor_rule(type, name):
643+
ACTION_OFFSET = 0
644+
POOL_TICKET_OFFSET = 8
645+
ANCHOR_CALL_OFFSET = 1040
646+
RULE_ACTION_OFFSET = 3068
647+
648+
pr = pfioc_rule()
649+
ppa = pfioc_pooladdr()
650+
651+
ioctl(pf_get_dev(), DIOCBEGINADDRS, ppa)
652+
653+
memmove(addressof(pr) + POOL_TICKET_OFFSET, ppa[4:8], 4) #pool_ticket
654+
memmove(addressof(pr) + ANCHOR_CALL_OFFSET, name, min(MAXPATHLEN, len(name))) #anchor_call = name
655+
memmove(addressof(pr) + RULE_ACTION_OFFSET, struct.pack('I', type), 4) #rule.action = type
656+
657+
memmove(addressof(pr) + ACTION_OFFSET, struct.pack('I', PF_CHANGE_GET_TICKET), 4) #action = PF_CHANGE_GET_TICKET
658+
ioctl(pf_get_dev(), DIOCCHANGERULE, pr)
659+
660+
memmove(addressof(pr) + ACTION_OFFSET, struct.pack('I', PF_CHANGE_ADD_TAIL), 4) #action = PF_CHANGE_ADD_TAIL
661+
ioctl(pf_get_dev(), DIOCCHANGERULE, pr)
662+
623663

624664
# This is some voodoo for setting up the kernel's transparent
625665
# proxying stuff. If subnets is empty, we just delete our sshuttle rules;
@@ -749,7 +789,7 @@ def main(port_v6, port_v4, dnsport_v6, dnsport_v4, method, udp, syslog):
749789
rewrite_etc_hosts(port_v6 or port_v4)
750790
elif line.startswith('QUERY_PF_NAT '):
751791
try:
752-
dst = query_pf_nat(*(line[13:].split(',')))
792+
dst = pf_query_nat(*(line[13:].split(',')))
753793
sys.stdout.write('QUERY_PF_NAT_SUCCESS %s,%r\n' % dst)
754794
except IOError, e:
755795
sys.stdout.write('QUERY_PF_NAT_FAILURE %s\n' % e)

0 commit comments

Comments
 (0)