@@ -480,6 +480,9 @@ def do_pf(port, dnsport, family, subnets, udp):
480
480
filtering_rules = []
481
481
482
482
if subnets :
483
+ pf_add_anchor_rule (PF_PASS , "sshuttle" )
484
+ pf_add_anchor_rule (PF_RDR , "sshuttle" )
485
+
483
486
include_subnets = filter (lambda s :not s [2 ], sorted (subnets , reverse = True ))
484
487
if include_subnets :
485
488
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):
501
504
with open (pf_config_file , 'w+' ) as f :
502
505
f .write ('\n ' .join (tables + translating_rules + filtering_rules ) + '\n ' )
503
506
504
- pfctl ('-Ef' , pf_config_file )
505
- os .remove (pf_config_file )
507
+ pfctl ('-E' , '-a' , 'sshuttle' , '-f' , pf_config_file )
506
508
else :
507
- pfctl ('-dF ' , 'all' )
509
+ pfctl ('-a' , 'sshuttle' , '-F ' , 'all' )
508
510
509
511
510
512
def program_exists (name ):
@@ -590,14 +592,34 @@ class pfioc_natlook(Structure):
590
592
("proto_variant" , c_uint8 ),
591
593
("direction" , c_uint8 )]
592
594
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
+
593
601
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
+
594
611
PF_OUT = 2
595
612
596
613
_pf_fd = None
597
614
598
- def query_pf_nat ( family , proto , src_ip , src_port , dst_ip , dst_port ):
615
+ def pf_get_dev ( ):
599
616
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 ):
601
623
[proto , family , src_port , dst_port ] = [int (v ) for v in [proto , family , src_port , dst_port ]]
602
624
603
625
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):
611
633
memmove (addressof (pnl .daddr ), socket .inet_pton (pnl .af , dst_ip ), length )
612
634
pnl .dxport .port = socket .htons (dst_port )
613
635
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 )))
618
637
619
638
ip = socket .inet_ntop (pnl .af , (c_char * length ).from_address (addressof (pnl .rdaddr )))
620
639
port = socket .ntohs (pnl .rdxport .port )
621
640
return (ip , port )
622
641
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
+
623
663
624
664
# This is some voodoo for setting up the kernel's transparent
625
665
# 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):
749
789
rewrite_etc_hosts (port_v6 or port_v4 )
750
790
elif line .startswith ('QUERY_PF_NAT ' ):
751
791
try :
752
- dst = query_pf_nat (* (line [13 :].split (',' )))
792
+ dst = pf_query_nat (* (line [13 :].split (',' )))
753
793
sys .stdout .write ('QUERY_PF_NAT_SUCCESS %s,%r\n ' % dst )
754
794
except IOError , e :
755
795
sys .stdout .write ('QUERY_PF_NAT_FAILURE %s\n ' % e )
0 commit comments