1
0

sync_iptables.pl 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. #!/usr/bin/perl -w
  2. #
  3. # Copyright (C) Roman Dmitiriev, rnd@rajven.ru
  4. #
  5. use FindBin '$Bin';
  6. use lib "$Bin/";
  7. use strict;
  8. use Time::Local;
  9. use FileHandle;
  10. use Data::Dumper;
  11. use Rstat::config;
  12. use Rstat::main;
  13. use Rstat::cmd;
  14. use Net::Patricia;
  15. use Date::Parse;
  16. use Rstat::net_utils;
  17. use Rstat::mysql;
  18. use IPTables::libiptc;
  19. use DBI;
  20. use utf8;
  21. use open ":encoding(utf8)";
  22. #exit;
  23. $|=1;
  24. my $gate = get_record_sql($dbh,"SELECT * FROM devices WHERE device_type=2 and user_acl=1 and deleted=0 and vendor_id=19 and device_name='".$HOSTNAME."'");
  25. if (!$gate) { exit 0; }
  26. my $router_name=$gate->{device_name};
  27. my $router_ip=$gate->{ip};
  28. my $shaper_enabled = $gate->{queue_enabled};
  29. my $connected_users_only = $gate->{connected_user_only};
  30. my @lan_int=();
  31. my @wan_int=();
  32. my @l3_int = get_records_sql($dbh,'SELECT * FROM device_l3_interfaces WHERE device_id='.$gate->{'id'});
  33. foreach my $l3 (@l3_int) {
  34. if ($l3->{'interface_type'} eq '0') { push(@lan_int,$l3->{'name'}); }
  35. if ($l3->{'interface_type'} eq '1') { push(@wan_int,$l3->{'name'}); }
  36. }
  37. my $connected_users = new Net::Patricia;
  38. if ($connected_users_only) {
  39. foreach my $int (@lan_int) {
  40. $int=trim($int);
  41. next if (!$int);
  42. #get ip addr at interface
  43. foreach my $int_str (@lan_int) {
  44. $int_str=trim($int_str);
  45. my $int_addr=do_exec('/sbin/ip addr show '.$int_str.' | grep "scope global"');
  46. foreach my $address (split(/\n/,$int_addr)) {
  47. if ($address=~/inet\s+(.*)\s+brd/i) {
  48. if ($1) { $connected_users->add_string($1); }
  49. }
  50. }
  51. }
  52. }
  53. }
  54. db_log_verbose($dbh,"Sync user state at router $router_name started.");
  55. #get userid list
  56. my $user_auth_sql="SELECT User_auth.ip, User_auth.filter_group_id
  57. FROM User_auth, User_list
  58. WHERE User_auth.user_id = User_list.id
  59. AND User_auth.deleted =0
  60. AND User_auth.enabled =1
  61. AND User_auth.blocked =0
  62. AND User_list.blocked =0
  63. AND User_auth.user_id <> $hotspot_user_id
  64. ORDER BY ip_int";
  65. my %users;
  66. my @authlist_ref = get_records_sql($dbh,$user_auth_sql);
  67. #print Dumper(\@authlist_ref);
  68. foreach my $row (@authlist_ref) {
  69. if ($connected_users_only) {
  70. next if (!$connected_users->match_string($row->{ip}));
  71. }
  72. $users{'group_'.$row->{filter_group_id}}->{ips}{$row->{ip}}=1;
  73. }
  74. #get filters
  75. my @filter_list = get_records_sql($dbh,"SELECT id,name,proto,dst,dstport,action FROM Filter_list where type=0");
  76. my %filters;
  77. foreach my $row (@filter_list) {
  78. $filters{$row->{id}}->{id}=$row->{id};
  79. $filters{$row->{id}}->{proto}=$row->{proto};
  80. $filters{$row->{id}}->{dst}=$row->{dst};
  81. $filters{$row->{id}}->{port}=$row->{dstport};
  82. $filters{$row->{id}}->{action}=$row->{action};
  83. }
  84. #get groups
  85. my @group_list = get_records_sql($dbh,"SELECT group_id,filter_id,Group_filters.order FROM Group_filters ORDER BY Group_filters.group_id,Group_filters.order" );
  86. my %group_filters;
  87. my %lists;
  88. my $index=0;
  89. foreach my $row (@group_list) {
  90. $group_filters{'group_'.$row->{group_id}}->{$index}=$row->{filter_id};
  91. $lists{'group_'.$row->{group_id}}=1;
  92. $index++;
  93. }
  94. my %cur_users;
  95. my @new_iptables_users=();
  96. foreach my $group_name (keys %lists) {
  97. #new users chains
  98. push(@new_iptables_users,"-A USERS -m set --match-set $group_name src -j $group_name");
  99. push(@new_iptables_users,"-A USERS -m set --match-set $group_name dst -j $group_name");
  100. #current user chains members
  101. my $address_lists=do_exec('/sbin/ipset list '.$group_name.' 2>/dev/null');
  102. $cur_users{$group_name}{found}=0;
  103. foreach my $row (split(/\n/,$address_lists)) {
  104. $row=trim($row);
  105. next if (!$row);
  106. if ($row=~/^Error$/i) { $cur_users{$group_name}{found}=0; last; }
  107. next if ($row !~ /^[0-9]/);
  108. $cur_users{$group_name}{ips}{$row}=1;
  109. $cur_users{$group_name}{found}=1;
  110. }
  111. }
  112. #recreate ipsets if not found
  113. foreach my $group_name (keys %lists) {
  114. next if ($cur_users{$group_name}{found});
  115. do_exec("/sbin/ipset create $group_name hash:net family inet maxelem 2655360 2>/dev/null");
  116. }
  117. my @cmd_list=();
  118. #new-ips
  119. foreach my $group_name (keys %users) {
  120. next if (!$users{$group_name}{ips});
  121. foreach my $user_ip (keys %{$users{$group_name}{ips}}) {
  122. if (!exists($cur_users{$group_name}{ips}{$user_ip})) {
  123. db_log_verbose($dbh,"Add user with ip: $user_ip to access-list $group_name");
  124. do_exec("/sbin/ipset add $group_name $user_ip");
  125. }
  126. }
  127. }
  128. #old-ips
  129. foreach my $group_name (keys %cur_users) {
  130. next if (!$cur_users{$group_name}{ips});
  131. foreach my $user_ip (keys %{$cur_users{$group_name}{ips}}) {
  132. if (!exists($users{$group_name}{ips}{$user_ip})) {
  133. db_log_verbose($dbh,"Remove user with ip: $user_ip from access-list $group_name");
  134. do_exec("/sbin/ipset del $group_name $user_ip");
  135. }
  136. }
  137. }
  138. timestamp;
  139. #filters
  140. my %chain_rules;
  141. foreach my $group_name (keys %lists) {
  142. next if (!$group_name);
  143. next if (!exists($group_filters{$group_name}));
  144. push(@{$chain_rules{$group_name}},"-N $group_name");
  145. foreach my $filter_index (sort keys %{$group_filters{$group_name}}) {
  146. my $filter_id=$group_filters{$group_name}->{$filter_index};
  147. next if (!$filters{$filter_id});
  148. my $src_rule='-A '.$group_name;
  149. my $dst_rule='-A '.$group_name;
  150. if ($filters{$filter_id}->{proto} and ($filters{$filter_id}->{proto}!~/all/i)) {
  151. $src_rule=$src_rule." -p ".$filters{$filter_id}->{proto};
  152. $dst_rule=$dst_rule." -p ".$filters{$filter_id}->{proto};
  153. }
  154. if ($filters{$filter_id}->{dst} and $filters{$filter_id}->{dst} ne '0/0') {
  155. $src_rule=$src_rule." -s ".trim($filters{$filter_id}->{dst});
  156. $dst_rule=$dst_rule." -d ".trim($filters{$filter_id}->{dst});
  157. }
  158. if ($filters{$filter_id}->{port} and $filters{$filter_id}->{port} ne '0') {
  159. my $module=" -m ".$filters{$filter_id}->{proto};
  160. if ($filters{$filter_id}->{port}=~/\-/ or $filters{$filter_id}->{port}=~/\,/ or $filters{$filter_id}->{port}=~/\:/) {
  161. $module=" -m multiport";
  162. $filters{$filter_id}->{port}=~s/\-/:/g;
  163. }
  164. $src_rule=$src_rule.$module." --sport ".trim($filters{$filter_id}->{port});
  165. $dst_rule=$dst_rule.$module." --dport ".trim($filters{$filter_id}->{port});
  166. }
  167. if ($filters{$filter_id}->{action}) {
  168. $src_rule=$src_rule." -j ACCEPT";
  169. $dst_rule=$dst_rule." -j ACCEPT";
  170. } else {
  171. $src_rule=$src_rule." -j REJECT";
  172. $dst_rule=$dst_rule." -j REJECT";
  173. }
  174. if ($src_rule ne $dst_rule) {
  175. push(@{$chain_rules{$group_name}},$src_rule);
  176. push(@{$chain_rules{$group_name}},$dst_rule);
  177. } else {
  178. push(@{$chain_rules{$group_name}},$src_rule);
  179. }
  180. }
  181. }
  182. ######## get current iptables USERS chain state
  183. my $cur_iptables = do_exec("/sbin/iptables --list-rules USERS 2>/dev/null");
  184. my @cur_iptables_users = split(/\n/,$cur_iptables);
  185. my $users_chain_ok=(scalar @cur_iptables_users eq scalar @new_iptables_users);
  186. #if count records in chain ok - check stuff
  187. if ($users_chain_ok) {
  188. for (my $i = 0; $i <= $#cur_iptables_users; $i++) {
  189. if ($cur_iptables_users[$i]!~/$new_iptables_users[$i]/i) { $users_chain_ok=0; last; }
  190. }
  191. }
  192. #group rules
  193. my %cur_chain_rules;
  194. foreach my $group_name (keys %lists) {
  195. next if (!$group_name);
  196. my $tmp=do_exec("/sbin/iptables --list-rules $group_name 2>/dev/null");
  197. foreach my $rule (split(/\n/,$tmp)) {
  198. if ($rule=~/Error/i) {
  199. $lists{$group_name}=0;
  200. last;
  201. }
  202. push(@{$cur_chain_rules{$group_name}},$rule);
  203. }
  204. }
  205. #check filter group chain
  206. foreach my $group_name (keys %lists) {
  207. my @tmp = ();
  208. if ($chain_rules{$group_name}) { @tmp = @{$chain_rules{$group_name}}; }
  209. my @cur_tmp = ();
  210. if ($cur_chain_rules{$group_name}) { @cur_tmp=@{$cur_chain_rules{$group_name}}; }
  211. my $group_chain_ok=($#tmp eq $#cur_tmp);
  212. #if count records in chain ok - check stuff
  213. if ($group_chain_ok) {
  214. for (my $i = 0; $i <= $#tmp; $i++) {
  215. if ($tmp[$i]!~/$cur_tmp[$i]/i) { $group_chain_ok=0; last; }
  216. }
  217. }
  218. if (!$group_chain_ok) {
  219. if ($lists{$group_name}) {
  220. push(@cmd_list,"-D USERS -m set --match-set $group_name src -j $group_name");
  221. push(@cmd_list,"-D USERS -m set --match-set $group_name dst -j $group_name");
  222. push(@cmd_list,"-D $group_name");
  223. }
  224. push(@cmd_list,@{$chain_rules{$group_name}});
  225. if ($users_chain_ok) {
  226. push(@cmd_list,"-A USERS -m set --match-set $group_name src -j $group_name");
  227. push(@cmd_list,"-A USERS -m set --match-set $group_name dst -j $group_name");
  228. }
  229. }
  230. }
  231. #recreate users chain
  232. if (!$users_chain_ok) {
  233. for (my $i = 0; $i <= $#new_iptables_users; $i++) {
  234. push(@cmd_list,$new_iptables_users[$i]);
  235. }
  236. }
  237. my $table = IPTables::libiptc::init('filter');
  238. foreach my $row (@cmd_list) {
  239. print "$row\n" if ($debug);
  240. my @cmd_array = split(" ",$row);
  241. $table->iptables_do_command(\@cmd_array);
  242. }
  243. $table->commit();
  244. db_log_verbose($dbh,"Sync user state at router $router_name stopped.");
  245. $dbh->disconnect();
  246. exit;