@@ -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
510512def 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+
593601DIOCNATLOOK = ((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+
594611PF_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