]> git.ipfire.org Git - people/mfischer/ipfire-2.x.git/blame - html/cgi-bin/zoneconf.cgi
zoneconf.cgi: Change NIC display order, improve code
[people/mfischer/ipfire-2.x.git] / html / cgi-bin / zoneconf.cgi
CommitLineData
1dcf513a
FB
1#!/usr/bin/perl
2###############################################################################
3# #
4# VLAN Management for IPFire #
5# Copyright (C) 2019 Florian Bührle <fbuehrle@ipfire.org> #
6# #
7# This program is free software: you can redistribute it and/or modify #
8# it under the terms of the GNU General Public License as published by #
9# the Free Software Foundation, either version 3 of the License, or #
10# (at your option) any later version. #
11# #
12# This program is distributed in the hope that it will be useful, #
13# but WITHOUT ANY WARRANTY; without even the implied warranty of #
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15# GNU General Public License for more details. #
16# #
17# You should have received a copy of the GNU General Public License #
18# along with this program. If not, see <http://www.gnu.org/licenses/>. #
19# #
20###############################################################################
21
22use strict;
23use Scalar::Util qw(looks_like_number);
24
25require '/var/ipfire/general-functions.pl';
26require "${General::swroot}/lang.pl";
27require "${General::swroot}/header.pl";
28
63a1c81a 29###--- HTML HEAD ---###
5c33a761 30my $extraHead = <<END
1dcf513a 31<style>
fc31c28d 32 table#zoneconf {
1dcf513a 33 width: 100%;
23b26ce5
MT
34 border-collapse: collapse;
35 table-layout: fixed;
1dcf513a
FB
36 }
37
fc31c28d 38 #zoneconf tr {
1dcf513a
FB
39 height: 4em;
40 }
41
fc31c28d
LAH
42 #zoneconf td {
43 padding: 5px 10px;
44 border: 0.5px solid black;
45 text-align: center;
1dcf513a
FB
46 }
47
fc31c28d
LAH
48 /* dark grey header cells */
49 #zoneconf td.heading {
50 background-color: grey;
51 color: white;
52 }
53 #zoneconf td.heading::first-line {
54 font-weight: bold;
55 line-height: 1.6;
1dcf513a
FB
56 }
57
fc31c28d
LAH
58 /* narrow left column */
59 #zoneconf tr > td:first-child {
60 width: 11em;
23b26ce5 61 }
7478903f 62
fc31c28d
LAH
63 /* alternating row background color */
64 #zoneconf tr:nth-child(2n+3) {
65 background-color: #F0F0F0;
1dcf513a
FB
66 }
67
fc31c28d 68 #zoneconf td.green {
1dcf513a
FB
69 background-color: $Header::colourgreen;
70 }
71
fc31c28d 72 #zoneconf td.red {
1dcf513a
FB
73 background-color: $Header::colourred;
74 }
75
fc31c28d 76 #zoneconf td.blue {
1dcf513a
FB
77 background-color: $Header::colourblue;
78 }
79
fc31c28d 80 #zoneconf td.orange {
1dcf513a
FB
81 background-color: $Header::colourorange;
82 }
83
fc31c28d
LAH
84 #zoneconf td.topleft {
85 background-color: $Header::pagecolour;
1dcf513a
FB
86 border-top-style: none;
87 border-left-style: none;
88 }
89
7f44ec04
AK
90 input.vlanid {
91 width: 4em;
92 }
93
1dcf513a 94 #submit-container {
1dcf513a 95 width: 100%;
23b26ce5 96 padding-top: 20px;
1d6bc7a0 97 text-align: right;
23b26ce5 98 color: red;
1dcf513a
FB
99 }
100
101 #submit-container.input {
102 margin-left: auto;
103 }
1dcf513a 104</style>
5c33a761
LAH
105
106<script src="/include/zoneconf.js"></script>
1dcf513a
FB
107END
108;
63a1c81a 109###--- END HTML HEAD ---###
1dcf513a 110
63a1c81a 111### Read configuration ###
1dcf513a
FB
112my %ethsettings = ();
113my %vlansettings = ();
114my %cgiparams = ();
115
7478903f
FB
116my $restart_notice = "";
117
1dcf513a
FB
118&General::readhash("${General::swroot}/ethernet/settings",\%ethsettings);
119&General::readhash("${General::swroot}/ethernet/vlans",\%vlansettings);
120
121&Header::getcgihash(\%cgiparams);
122&Header::showhttpheaders();
123
124# Define all zones we will check for NIC assignment
63a1c81a 125my @zones = ("red", "green", "orange", "blue");
1dcf513a
FB
126
127# Get all physical NICs present
128opendir(my $dh, "/sys/class/net/");
129my @nics = ();
130
131while (my $nic = readdir($dh)) {
132 if (-e "/sys/class/net/$nic/device") { # Indicates that the NIC is physical
133 push(@nics, [&Network::get_nic_property($nic, "address"), $nic, 0]);
134 }
135}
136
137closedir($dh);
138
139@nics = sort {$a->[0] cmp $b->[0]} @nics; # Sort nics by their MAC address
140
141# Name the physical NICs
142# Even though they may not be really named like this, we will name them ethX or wlanX
143my $ethcount = 0;
144my $wlancount = 0;
145
146foreach (@nics) {
147 my $nic = $_->[1];
148
149 if (-e "/sys/class/net/$nic/wireless") {
150 $_->[1] = "wlan$wlancount";
151 $_->[2] = 1;
152 $wlancount++;
153 } else {
154 $_->[1] = "eth$ethcount";
155 $ethcount++;
156 }
157}
158
63a1c81a
LAH
159### Functions ###
160
161# Check if a zone is in IP mode or in PPP, PPPoE, VDSL, ... mode
162sub is_zonetype_ip {
163 my $zone_type = shift;
164 return ($zone_type eq "STATIC" || $zone_type eq "DHCP");
165}
166
167# Check if a zone is activated (device assigned)
168sub is_zone_activated {
169 my $zone = uc shift;
170 return ($ethsettings{"${zone}_DEV"} ne "");
171}
172
173### START PAGE ###
5c33a761 174&Header::openpage($Lang::tr{"zoneconf title"}, 1, $extraHead);
1dcf513a
FB
175&Header::openbigbox('100%', 'center');
176
177### Evaluate POST parameters ###
178
179if ($cgiparams{"ACTION"} eq $Lang::tr{"save"}) {
180 my %VALIDATE_nic_check = ();
181 my $VALIDATE_error = "";
182
183 foreach (@zones) {
184 my $uc = uc $_;
185 my $slave_string = "";
186 my $zone_mode = $cgiparams{"MODE $uc"};
187 my $VALIDATE_vlancount = 0;
f60b61e0
FB
188 my $VALIDATE_zoneslaves = 0;
189
1dcf513a
FB
190 $ethsettings{"${uc}_MACADDR"} = "";
191 $ethsettings{"${uc}_MODE"} = "";
192 $ethsettings{"${uc}_SLAVES"} = "";
193 $vlansettings{"${uc}_PARENT_DEV"} = "";
194 $vlansettings{"${uc}_VLAN_ID"} = "";
195 $vlansettings{"${uc}_MAC_ADDRESS"} = "";
196
197 # If RED is not in DHCP or static mode, we only set its MACADDR property
198 if ($uc eq "RED" && ! $cgiparams{"PPPACCESS"} eq "") {
199 foreach (@nics) {
200 my $mac = $_->[0];
201
202 if ($mac eq $cgiparams{"PPPACCESS"}) {
203 $ethsettings{"${uc}_MACADDR"} = $mac;
204
205 # Check if this interface is already accessed by any other zone
206 # If this is the case, show an error message
207 if ($VALIDATE_nic_check{"ACC $mac"}) {
208 $VALIDATE_error = $Lang::tr{"zoneconf val ppp assignment error"};
209 }
210
211 $VALIDATE_nic_check{"RESTRICT $mac"} = 1;
212 last;
213 }
214 }
215
63a1c81a 216 # skip NIC/VLAN assignment and additional zone options for RED in PPP mode
1dcf513a
FB
217 next;
218 }
219
220 foreach (@nics) {
221 my $mac = $_->[0];
222 my $nic_access = $cgiparams{"ACCESS $uc $mac"};
223
bb90622c
MT
224 next unless ($nic_access);
225
a6695868 226 if ($nic_access ne "NONE") {
1dcf513a
FB
227 if ($VALIDATE_nic_check{"RESTRICT $mac"}) { # If this interface is already assigned to RED in PPP mode, throw an error
228 $VALIDATE_error = $Lang::tr{"zoneconf val ppp assignment error"};
f60b61e0
FB
229 last;
230 }
231
a6695868 232 if ($zone_mode ne "BRIDGE" && $VALIDATE_zoneslaves > 0 && $nic_access ne "") {
f60b61e0
FB
233 $VALIDATE_error = $Lang::tr{"zoneconf val zoneslave amount error"};
234 last;
1dcf513a
FB
235 }
236
237 $VALIDATE_nic_check{"ACC $mac"} = 1;
f60b61e0 238 $VALIDATE_zoneslaves++;
1dcf513a
FB
239 }
240
241 if ($nic_access eq "NATIVE") {
242 if ($VALIDATE_nic_check{"NATIVE $mac"}) {
243 $VALIDATE_error = $Lang::tr{"zoneconf val native assignment error"};
f60b61e0 244 last;
1dcf513a
FB
245 }
246
247 $VALIDATE_nic_check{"NATIVE $mac"} = 1;
248
249 if ($zone_mode eq "BRIDGE") {
250 $slave_string = "${slave_string}${mac} ";
251 } else {
252 $ethsettings{"${uc}_MACADDR"} = $mac;
253 }
254 } elsif ($nic_access eq "VLAN") {
255 my $vlan_tag = $cgiparams{"TAG $uc $mac"};
256
257 if ($VALIDATE_nic_check{"VLAN $mac $vlan_tag"}) {
258 $VALIDATE_error = $Lang::tr{"zoneconf val vlan tag assignment error"};
f60b61e0 259 last;
1dcf513a
FB
260 }
261
262 $VALIDATE_nic_check{"VLAN $mac $vlan_tag"} = 1;
263
264 if (! looks_like_number($vlan_tag)) {
f60b61e0 265 last;
1dcf513a
FB
266 }
267 if ($vlan_tag < 1 || $vlan_tag > 4095) {
f60b61e0 268 last;
1dcf513a
FB
269 }
270
271 my $rnd_mac = &Network::random_mac();
272
273 $vlansettings{"${uc}_PARENT_DEV"} = $mac;
274 $vlansettings{"${uc}_VLAN_ID"} = $vlan_tag;
275 $vlansettings{"${uc}_MAC_ADDRESS"} = $rnd_mac;
276
277 if ($zone_mode eq "BRIDGE") {
278 $slave_string = "${slave_string}${rnd_mac} ";
279 }
280
281 $VALIDATE_vlancount++; # We can't allow more than one VLAN per zone
282 }
283 }
284
285 if ($VALIDATE_vlancount > 1) {
286 $VALIDATE_error = $Lang::tr{"zoneconf val vlan amount assignment error"};
f60b61e0 287 last;
1dcf513a
FB
288 }
289
290 chop($slave_string);
291
292 if ($zone_mode eq "BRIDGE") {
293 $ethsettings{"${uc}_MODE"} = "bridge";
294 $ethsettings{"${uc}_SLAVES"} = $slave_string;
295 } elsif ($zone_mode eq "MACVTAP") {
296 $ethsettings{"${uc}_MODE"} = "macvtap";
297 }
298 }
299
63a1c81a 300 # validation failed, show error message and exit
1dcf513a
FB
301 if ($VALIDATE_error) {
302 &Header::openbox('100%', 'left', $Lang::tr{"error"});
303
8797526d 304 print "$VALIDATE_error<br><br><a href='$ENV{'SCRIPT_NAME'}'>$Lang::tr{'back'}</a>\n";
1dcf513a
FB
305
306 &Header::closebox();
307 &Header::closebigbox();
308 &Header::closepage();
309
310 exit 0;
311 }
312
63a1c81a 313 # new settings are valid, write configuration files
1dcf513a
FB
314 &General::writehash("${General::swroot}/ethernet/settings",\%ethsettings);
315 &General::writehash("${General::swroot}/ethernet/vlans",\%vlansettings);
7478903f 316
23b26ce5 317 $restart_notice = $Lang::tr{'zoneconf notice reboot'};
1dcf513a
FB
318}
319
1dcf513a
FB
320### START OF TABLE ###
321
63a1c81a
LAH
322&Header::openbox('100%', 'left', $Lang::tr{"zoneconf nic assignment"});
323
1dcf513a 324print <<END
0ec8e31a 325<form method='post' enctype='multipart/form-data'>
fc31c28d 326 <table id="zoneconf">
96d0c761 327 <tr>
fc31c28d 328 <td class="topleft"></td>
1dcf513a
FB
329END
330;
331
0ec8e31a 332# Fill the table header with all activated zones
1dcf513a 333foreach (@zones) {
1dcf513a 334 my $uc = uc $_;
1dcf513a 335
63a1c81a
LAH
336 # If the zone is not activated, don't show it
337 next unless is_zone_activated($_);
f60b61e0 338
63a1c81a 339 # If the red zone is in PPP mode, don't show a mode dropdown
23b26ce5 340 if ($uc eq "RED") {
1dcf513a 341 my $red_type = $ethsettings{"RED_TYPE"};
1dcf513a 342
63a1c81a
LAH
343 unless (is_zonetype_ip($red_type)) {
344 print "\t\t<td class='heading bold $_'>$uc ($red_type)</td>\n";
1dcf513a 345
1dcf513a
FB
346 next; # We're done here
347 }
348 }
349
350 my %mode_selected = ();
351 my $zone_mode = $ethsettings{"${uc}_MODE"};
352
353 if ($zone_mode eq "") {
354 $mode_selected{"DEFAULT"} = "selected";
355 } elsif ($zone_mode eq "bridge") {
356 $mode_selected{"BRIDGE"} = "selected";
357 } elsif ($zone_mode eq "macvtap") {
358 $mode_selected{"MACVTAP"} = "selected";
359 }
360
361 print <<END
fc31c28d 362 <td class='heading $_'>$uc<br>
1dcf513a
FB
363 <select name="MODE $uc">
364 <option value="DEFAULT" $mode_selected{"DEFAULT"}>$Lang::tr{"zoneconf nicmode default"}</option>
365 <option value="BRIDGE" $mode_selected{"BRIDGE"}>$Lang::tr{"zoneconf nicmode bridge"}</option>
366 <option value="MACVTAP" $mode_selected{"MACVTAP"}>$Lang::tr{"zoneconf nicmode macvtap"}</option>
367 </select>
368 </td>
369END
370;
0ec8e31a
FB
371}
372
96d0c761 373print "\t</tr>\n";
0ec8e31a 374
63a1c81a 375# NIC assignment matrix
0ec8e31a 376foreach (@nics) {
23b26ce5 377 my $mac = $_->[0];
0ec8e31a 378 my $nic = $_->[1];
23b26ce5 379 my $wlan = $_->[2];
0ec8e31a 380
96d0c761 381 print "\t<tr>\n";
fc31c28d 382 print "\t\t<td class='heading'>$nic<br>$mac</td>\n";
0ec8e31a 383
23b26ce5
MT
384 # Iterate through all zones and check if the current NIC is assigned to it
385 foreach (@zones) {
386 my $uc = uc $_;
5c33a761 387 my $highlight = "";
0ec8e31a 388
63a1c81a
LAH
389 # If the zone is not activated, don't show it
390 next unless is_zone_activated($_);
1dcf513a 391
23b26ce5 392 if ($uc eq "RED") {
23b26ce5 393 # VLANs/Bridging is not possible if the RED interface is set to PPP, PPPoE, VDSL, ...
63a1c81a 394 unless (is_zonetype_ip($ethsettings{"RED_TYPE"})) {
23b26ce5 395 my $checked = "";
1dcf513a 396
0ec8e31a
FB
397 if ($mac eq $ethsettings{"${uc}_MACADDR"}) {
398 $checked = "checked";
5c33a761 399 $highlight = $_;
0ec8e31a
FB
400 }
401
96d0c761 402 print <<END
5c33a761
LAH
403 <td class="$highlight">
404 <input type="radio" name="PPPACCESS" value="$mac" data-zone="RED" data-mac="$mac" onchange="highlightAccess(this)" $checked>
96d0c761
LAH
405 </td>
406END
407;
23b26ce5
MT
408 next; # We're done here
409 }
410 }
411
412 my %access_selected = ();
413 my $zone_mode = $ethsettings{"${uc}_MODE"};
414 my $zone_parent_dev = $vlansettings{"${uc}_PARENT_DEV"}; # ZONE_PARENT_DEV is set if this zone accesses any interface via a VLAN
415 my $field_disabled = "disabled"; # Only enable the VLAN ID input field if the current access mode is VLAN
1dcf513a
FB
416 my $zone_vlan_id = "";
417
23b26ce5
MT
418 # If ZONE_PARENT_DEV is set to a NICs name (e.g. green0 or eth0) instead of a MAC address, we have to find out this NICs MAC address
419 $zone_parent_dev = &Network::get_mac_by_name($zone_parent_dev);
0ec8e31a 420
23b26ce5
MT
421 # If the current NIC is accessed by the current zone via a VLAN, the ZONE_PARENT_DEV option corresponds to the current NIC
422 if ($mac eq $zone_parent_dev) {
1dcf513a
FB
423 $access_selected{"VLAN"} = "selected";
424 $field_disabled = "";
425 $zone_vlan_id = $vlansettings{"${uc}_VLAN_ID"};
0ec8e31a 426 } elsif ($zone_mode eq "bridge") { # If the current zone is in bridge mode, all corresponding NICs (Native as well as VLAN) are set via the ZONE_SLAVES option
1dcf513a
FB
427 my @slaves = split(/ /, $ethsettings{"${uc}_SLAVES"});
428
429 foreach (@slaves) {
430 # Slaves can be set to a NICs name so we have to find out its MAC address
431 $_ = &Network::get_mac_by_name($_);
432
433 if ($_ eq $mac) {
434 $access_selected{"NATIVE"} = "selected";
435 last;
436 }
437 }
0ec8e31a
FB
438 } elsif ($mac eq $ethsettings{"${uc}_MACADDR"}) { # Native access via ZONE_MACADDR is only set if the zone does not access a NIC via a VLAN and the zone is not in bridge mode
439 $access_selected{"NATIVE"} = "selected";
1dcf513a
FB
440 }
441
23b26ce5 442 $access_selected{"NONE"} = ($access_selected{"NATIVE"} eq "") && ($access_selected{"VLAN"} eq "") ? "selected" : "";
1dcf513a
FB
443 my $vlan_disabled = ($wlan) ? "disabled" : "";
444
5c33a761
LAH
445 # If the interface is assigned, hightlight table cell
446 if ($access_selected{"NONE"} eq "") {
447 $highlight = $_;
448 }
449
23b26ce5 450 print <<END
5c33a761
LAH
451 <td class="$highlight">
452 <select name="ACCESS $uc $mac" data-zone="$uc" data-mac="$mac" onchange="highlightAccess(this)">
96d0c761
LAH
453 <option value="NONE" $access_selected{"NONE"}>- $Lang::tr{"zoneconf access none"} -</option>
454 <option value="NATIVE" $access_selected{"NATIVE"}>$Lang::tr{"zoneconf access native"}</option>
455 <option value="VLAN" $access_selected{"VLAN"} $vlan_disabled>$Lang::tr{"zoneconf access vlan"}</option>
456 </select>
8797526d 457 <input type="number" class="vlanid" id="TAG-$uc-$mac" name="TAG $uc $mac" min="1" max="4095" value="$zone_vlan_id" $field_disabled>
96d0c761 458 </td>
1dcf513a
FB
459END
460;
23b26ce5 461 }
1dcf513a 462
96d0c761 463 print "\t</tr>\n";
1dcf513a
FB
464}
465
63a1c81a 466# footer and submit button
1dcf513a
FB
467print <<END
468 </table>
1d6bc7a0
MT
469
470 <div id="submit-container">
23b26ce5 471 $restart_notice
1d6bc7a0
MT
472 <input type="submit" name="ACTION" value="$Lang::tr{"save"}">
473 </div>
474</form>
1dcf513a
FB
475END
476;
477
478### END OF TABLE ###
479
480&Header::closebox();
481&Header::closebigbox();
482&Header::closepage();