scripts/update-cmd.pl: set device up before adding routes/addresses
[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 (keys %{$add->{ipaddr}}, keys %{$add->{route}}) > 0 and cmd("ip l s dev $ifname up");
133
134 foreach my $ip (keys %{$delete->{ipaddr}}) {
135 cmd("ip a d $ip dev $ifname");
136 }
137 foreach my $route (keys %{$delete->{route}}) {
138 cmd("ip r d $route dev $ifname");
139 }
140
141 foreach my $ip (keys %{$add->{ipaddr}}) {
142 cmd("ip a a $ip dev $ifname");
143 }
144 foreach my $route (keys %{$add->{route}}) {
145 cmd("ip r a $route dev $ifname");
146 }
147 }
148
149 sub set_active_data_darwin($$$) {
150 my $ifname = shift;
151 my $delete = shift;
152 my $add = shift;
153
154 foreach my $ip (keys %{$delete->{ipaddr}}) {
155 $ip =~ s/\/.*//;
156 if ($ip =~ /:/) {
157 cmd("ifconfig $ifname inet6 delete $ip");
158 } else {
159 cmd("ifconfig $ifname delete $ip");
160 }
161 }
162 foreach my $route (keys %{$delete->{route}}) {
163 if ($route =~ /:/) {
164 cmd("route delete -inet6 $route -iface $ifname");
165 } else {
166 cmd("route delete -inet $route -iface $ifname");
167 }
168 }
169 foreach my $ip (keys %{$add->{ipaddr}}) {
170 my @ip = split /\//, $ip;
171
172 if ($ip[0] =~ /:/) {
173 cmd("ifconfig $ifname inet6 add $ip[0] prefixlen $ip[1]");
174 } else {
175 cmd("ifconfig $ifname add $ip[0]/$ip[1] $ip[0]");
176 }
177 }
178 foreach my $route (keys %{$add->{route}}) {
179 if ($route =~ /:/) {
180 cmd("route add -inet6 $route -iface $ifname");
181 } else {
182 cmd("route add -inet $route -iface $ifname");
183 }
184 }
185 }
186
187 my $json = $ARGV[0];
188 my $platform = `uname`;
189 my $data = JSON::PP::decode_json($json) or die "Failed to decode JSON data\n";
190
191 my $delete = create_state();
192 my $add = create_state();
193
194 if ($platform =~ /Darwin/) {
195 fetch_active_data_darwin($data->{ifname}, $delete);
196 } elsif ($platform =~ /Linux/) {
197 fetch_active_data_linux($data->{ifname}, $delete);
198 } else {
199 die "Unsupported platform $platform\n";
200 }
201
202 update_data($data, $delete, $add);
203
204 if ($platform =~ /Darwin/) {
205 set_active_data_darwin($data->{ifname}, $delete, $add);
206 } elsif ($platform =~ /Linux/) {
207 set_active_data_linux($data->{ifname}, $delete, $add);
208 }
209
210 # print Data::Dumper->Dump([$add, $delete], ["add", "delete"])."\n";