update-cmd.pl 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. #!/usr/bin/perl
  2. use FindBin qw($Bin);
  3. require "$Bin/json_pp.pm";
  4. use Data::Dumper;
  5. use strict;
  6. sub create_state() {
  7. return {
  8. route => {},
  9. ipaddr => {},
  10. };
  11. }
  12. sub cmd($) {
  13. my $cmd = shift;
  14. print STDERR "command: $cmd\n";
  15. system($cmd);
  16. }
  17. sub fetch_active_data_linux($$) {
  18. my $ifname = shift;
  19. my $data = shift;
  20. open DATA, "(ip -4 r s dev $ifname; ip -6 r s dev $ifname) |";
  21. while (<DATA>) {
  22. chomp;
  23. s/^\s+//;
  24. my @data = split /\s+/, $_;
  25. next if $data[0] =~ /^fe80:/;
  26. next if $data[0] =~ /^ff..:/;
  27. $data[0] =~ /(:|\/)/ or do {
  28. $data[0] .= '/32';
  29. };
  30. $data->{route}->{$data[0]} = 'delete';
  31. }
  32. close DATA;
  33. open DATA, "ip a s dev $ifname |";
  34. while (<DATA>) {
  35. chomp;
  36. s/^\s+//;
  37. my @data = split /\s+/, $_;
  38. next unless $data[0] =~ /inet/;
  39. next if $data[1] =~ /^fe80:/;
  40. $data->{ipaddr}->{$data[1]} = 'delete';
  41. }
  42. close DATA;
  43. }
  44. sub fetch_active_data_darwin($$) {
  45. my $ifname = shift;
  46. my $data = shift;
  47. open DATA, "netstat -rn |";
  48. while (<DATA>) {
  49. chomp;
  50. s/^\s+//;
  51. my @data = split /\s+/, $_;
  52. next unless $data[3] eq $ifname;
  53. next if $data[0] =~ /^fe80:/;
  54. next if $data[0] =~ /^ff..:/;
  55. $data[0] =~ /(:|\/)/ or do {
  56. my $mask = 32;
  57. my @addr = split /\./, $data[0];
  58. while (@addr < 4) {
  59. push @addr, '0';
  60. $mask -= 8;
  61. }
  62. $data[0] = join(".", @addr)."/$mask";
  63. };
  64. $data->{route}->{$data[0]} = 'delete';
  65. }
  66. close DATA;
  67. open DATA, "ifconfig $ifname |";
  68. while (<DATA>) {
  69. chomp;
  70. s/^\s+//;
  71. my @data = split /\s+/, $_;
  72. next unless $data[0] =~ /inet/;
  73. next if $data[1] =~ /^fe80:/;
  74. $data->{ipaddr}->{$data[1]} = 'delete';
  75. }
  76. close DATA;
  77. }
  78. sub update_data($$$) {
  79. my $data = shift;
  80. my $delete = shift;
  81. my $add = shift;
  82. return unless $data->{"link-up"} eq 1;
  83. foreach my $val (@{$data->{ipaddr}}, @{$data->{ip6addr}}) {
  84. my $ip = $val->{ipaddr};
  85. my $mask = $val->{mask};
  86. if ($ip =~ /:/) {
  87. my $route = $ip;
  88. if (not($ip =~ /::/) and $mask eq 64) {
  89. $route =~ s/((\w+):(\w+):(\w+):(\w+)):.*/$1::/;
  90. } else {
  91. $route = "$ip/128";
  92. }
  93. push @{$data->{routes6}}, { target => "$route", "netmask" => $mask };
  94. } else {
  95. push @{$data->{routes}}, { target => "$ip", "netmask" => 32 };
  96. };
  97. if ($delete->{ipaddr}->{$ip}) {
  98. delete $delete->{ipaddr}->{$ip};
  99. } elsif ($delete->{ipaddr}->{"$ip/$mask"}) {
  100. delete $delete->{ipaddr}->{"$ip/$mask"};
  101. } else {
  102. $add->{ipaddr}->{"$ip/$mask"} = 'add';
  103. }
  104. }
  105. foreach my $val (@{$data->{routes}}, @{$data->{routes6}}) {
  106. my $ip = $val->{target}.'/'.$val->{netmask};
  107. if ($delete->{route}->{$ip}) {
  108. delete $delete->{route}->{$ip};
  109. } else {
  110. $add->{route}->{$ip} = 'add';
  111. }
  112. }
  113. }
  114. sub set_active_data_linux($$$) {
  115. my $ifname = shift;
  116. my $delete = shift;
  117. my $add = shift;
  118. (keys %{$add->{ipaddr}}, keys %{$add->{route}}) > 0 and cmd("ip l s dev $ifname up");
  119. foreach my $ip (keys %{$delete->{ipaddr}}) {
  120. cmd("ip a d $ip dev $ifname");
  121. }
  122. foreach my $route (keys %{$delete->{route}}) {
  123. cmd("ip r d $route dev $ifname");
  124. }
  125. foreach my $ip (keys %{$add->{ipaddr}}) {
  126. cmd("ip a a $ip dev $ifname");
  127. }
  128. foreach my $route (keys %{$add->{route}}) {
  129. cmd("ip r a $route dev $ifname");
  130. }
  131. }
  132. sub set_active_data_darwin($$$) {
  133. my $ifname = shift;
  134. my $delete = shift;
  135. my $add = shift;
  136. foreach my $ip (keys %{$delete->{ipaddr}}) {
  137. $ip =~ s/\/.*//;
  138. if ($ip =~ /:/) {
  139. cmd("ifconfig $ifname inet6 delete $ip");
  140. } else {
  141. cmd("ifconfig $ifname delete $ip");
  142. }
  143. }
  144. foreach my $route (keys %{$delete->{route}}) {
  145. if ($route =~ /:/) {
  146. cmd("route delete -inet6 $route -iface $ifname");
  147. } else {
  148. cmd("route delete -inet $route -iface $ifname");
  149. }
  150. }
  151. foreach my $ip (keys %{$add->{ipaddr}}) {
  152. my @ip = split /\//, $ip;
  153. if ($ip[0] =~ /:/) {
  154. cmd("ifconfig $ifname inet6 add $ip[0] prefixlen $ip[1]");
  155. } else {
  156. cmd("ifconfig $ifname add $ip[0]/$ip[1] $ip[0]");
  157. }
  158. }
  159. foreach my $route (keys %{$add->{route}}) {
  160. if ($route =~ /:/) {
  161. cmd("route add -inet6 $route -iface $ifname");
  162. } else {
  163. cmd("route add -inet $route -iface $ifname");
  164. }
  165. }
  166. }
  167. my $json = $ARGV[0];
  168. my $platform = `uname`;
  169. my $data = JSON::PP::decode_json($json) or die "Failed to decode JSON data\n";
  170. foreach my $round (1 .. 2) {
  171. my $delete = create_state();
  172. my $add = create_state();
  173. if ($platform =~ /Darwin/) {
  174. fetch_active_data_darwin($data->{ifname}, $delete);
  175. } elsif ($platform =~ /Linux/) {
  176. fetch_active_data_linux($data->{ifname}, $delete);
  177. } else {
  178. die "Unsupported platform $platform\n";
  179. }
  180. update_data($data, $delete, $add);
  181. if ($platform =~ /Darwin/) {
  182. set_active_data_darwin($data->{ifname}, $delete, $add);
  183. } elsif ($platform =~ /Linux/) {
  184. set_active_data_linux($data->{ifname}, $delete, $add);
  185. }
  186. }
  187. # print Data::Dumper->Dump([$add, $delete], ["add", "delete"])."\n";