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