diff --git a/config/menu/60-ipfire.menu b/config/menu/60-ipfire.menu index 9c0c596e9..a686fe1f0 100644 --- a/config/menu/60-ipfire.menu +++ b/config/menu/60-ipfire.menu @@ -15,6 +15,12 @@ 'title' => "$Lang::tr{'loxilb config'}", 'enabled' => 1, }; + $subipfire->{'40.loxilbfw'} = { + 'caption' => $Lang::tr{'loxilb fw'}, + 'uri' => '/cgi-bin/loxilbfw.cgi', + 'title' => "$Lang::tr{'loxilb fw'}", + 'enabled' => 1, + }; $subipfire->{'80.pakfire'} = {'caption' => 'Pakfire', 'uri' => '/cgi-bin/pakfire.cgi', 'title' => "Pakfire", diff --git a/config/rootfiles/common/web-user-interface b/config/rootfiles/common/web-user-interface index 5f4b7fe5e..c943cc3f9 100644 --- a/config/rootfiles/common/web-user-interface +++ b/config/rootfiles/common/web-user-interface @@ -93,6 +93,7 @@ srv/web/ipfire/cgi-bin/zoneconf.cgi srv/web/ipfire/cgi-bin/ddos.cgi srv/web/ipfire/cgi-bin/loxilb.cgi srv/web/ipfire/cgi-bin/loxilbconfig.cgi +srv/web/ipfire/cgi-bin/loxilbfw.cgi #srv/web/ipfire/html srv/web/ipfire/html/blob.gif #srv/web/ipfire/html/captive diff --git a/html/cgi-bin/loxilbfw.cgi b/html/cgi-bin/loxilbfw.cgi new file mode 100644 index 000000000..a9422c0aa --- /dev/null +++ b/html/cgi-bin/loxilbfw.cgi @@ -0,0 +1,641 @@ +#!/usr/bin/perl +############################################################################### +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2011 IPFire Team # +# Copyright (C) 2024 BPFire Team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### + +use strict; + +# enable only the following on debugging purpose +use warnings; +#use CGI::Carp 'fatalsToBrowser'; + +require '/var/ipfire/general-functions.pl'; +require "${General::swroot}/lang.pl"; +require "${General::swroot}/header.pl"; + +#workaround to suppress a warning when a variable is used only once +my @dummy = ( ${Header::colouryellow} ); +undef (@dummy); + +# Files used +my $setting = "${General::swroot}/main/settings"; +our $datafile = "${General::swroot}/loxilb/fwconfig"; #(our: used in subroutine) + +my %color = (); +my %mainsettings = (); +&General::readhash("${General::swroot}/main/settings", \%mainsettings); +&General::readhash("/srv/web/ipfire/html/themes/ipfire/include/colors.txt", \%color); + +our %settings = (); + +$settings{'EN'} = ''; # reuse for dummy field in position zero +$settings{'sourceIP'} = ''; +$settings{'destinationIP'} = ''; +$settings{'minSourcePort'} = ''; +$settings{'maxSourcePort'} = ''; +$settings{'minDestinationPort'} = ''; +$settings{'maxDestinationPort'} = ''; +$settings{'protocol'} = ''; +$settings{'portName'} = ''; +$settings{'preference'} = ''; +$settings{'ruleAction'} = ''; +my @nosaved=('EN','sourceIP','destinationIP','minSourcePort','maxSourcePort','minDestinationPort','maxDestinationPort','protocol','portName','preference','ruleAction'); # List here ALL setting2 fields. Mandatory + +$settings{'ACTION'} = ''; # add/edit/remove +$settings{'KEY1'} = ''; # point record for ACTION + +#Define each field that can be used to sort columns +my $sortstring='^sourceIP'; +$settings{'SORT_sourceIPLIST'} = 'sourceIP'; +my $errormessage = ''; +my $warnmessage = ''; + +&Header::showhttpheaders(); + +#Get GUI values +&Header::getcgihash(\%settings); + +############### +# DEBUG DEBUG +#&Header::openbox('100%', 'left', 'DEBUG'); +#my $debugCount = 0; +#foreach my $line (sort keys %settings) { +#print "$line = $settings{$line}
\n"; +# $debugCount++; +#} +#print " Count: $debugCount\n"; +#&Header::closebox(); +# DEBUG DEBUG +############### + +# Load multiline data +our @current = (); +if (open(FILE, "$datafile")) { + @current = ; + close (FILE); +} + +## Settings1 Box not used... +&General::readhash("${General::swroot}/main/settings", \%settings); + + +## Now manipulate the multi-line list with Settings2 +# Basic actions are: +# toggle the check box +# add/update a new line +# begin editing a line +# remove a line + + +# Toggle enable/disable field. Field is in second position +if ($settings{'ACTION'} eq $Lang::tr{'toggle enable disable'}) { + #move out new line + chomp(@current[$settings{'KEY1'}]); + my @temp = split(/\,/,@current[$settings{'KEY1'}]); + + $temp[0] = $temp[0] ne '' ? '' : 'on'; # Toggle the field + @current[$settings{'KEY1'}] = join (',',@temp)."\n"; + $settings{'KEY1'} = ''; # End edit mode + + &General::log($Lang::tr{'loxilb fw changed'}); + + #Save current + open(FILE, ">$datafile") or die 'loxilb fw datafile error'; + print FILE @current; + close(FILE); + + # Rebuild configuration file + #&BuildConfiguration; +} + +if ($settings{'ACTION'} eq $Lang::tr{'add'}) { + # Validate inputs + if (!&General::validipandmask($settings{'sourceIP'})){ + $errormessage = $Lang::tr{'invalid ip'}." / ".$Lang::tr{'invalid netmask'}; + }else{ + #set networkip if not already correctly defined + my($ip,$cidr) = split(/\//,$settings{'sourceIP'}); + $cidr = &General::iporsubtocidr($cidr); + my $netip=&General::getnetworkip($ip,$cidr); + $settings{'sourceIP'} = "$netip/$cidr"; + } + + if (!&General::validipandmask($settings{'destinationIP'})){ + $errormessage = $Lang::tr{'invalid ip'}." / ".$Lang::tr{'invalid netmask'}; + }else{ + #set networkip if not already correctly defined + my($ip,$cidr) = split(/\//,$settings{'destinationIP'}); + $cidr = &General::iporsubtocidr($cidr); + my $netip=&General::getnetworkip($ip,$cidr); + $settings{'destinationIP'} = "$netip/$cidr"; + } + + #Check for already existing routing entry + foreach my $line (@current) { + chomp($line); # remove newline + my @temp=split(/\,/,$line); + $temp[2] ='' unless defined $temp[2]; # destinationIP + $temp[3] ='' unless defined $temp[2]; # minSourcePort + $temp[4] ='' unless defined $temp[3]; # maxSourcePort + $temp[5] ='' unless defined $temp[4]; # minDestinationPort + $temp[6] ='' unless defined $temp[5]; # maxDestinationPort + $temp[7] ='' unless defined $temp[6]; # protocol + $temp[8] ='' unless defined $temp[7]; # portName + $temp[9] ='' unless defined $temp[8]; # preference + $temp[10] ='' unless defined $temp[9]; # ruleAction + #Same ip already used? + if($temp[1] eq $settings{'sourceIP'} && $settings{'KEY1'} eq ''){ + $errormessage = $Lang::tr{'ccd err loxilbconfigeexist'}; + last; + } + } + + unless ($errormessage) { + if ($settings{'KEY1'} eq '') { #add or edit ? + unshift (@current, "$settings{'EN'},$settings{'sourceIP'},$settings{'destinationIP'},$settings{'minSourcePort'},$settings{'maxSourcePort'},$settings{'minDestinationPort'},$settings{'maxDestinationPort'},$settings{'protocol'},$settings{'portName'},$settings{'preference'},$settings{'ruleAction'}\n"); + &General::log($Lang::tr{'loxilb lb config added'}); + } else { + @current[$settings{'KEY1'}] = "$settings{'EN'},$settings{'sourceIP'},$settings{'destinationIP'},$settings{'minSourcePort'},$settings{'maxSourcePort'},$settings{'minDestinationPort'},$settings{'maxDestinationPort'},$settings{'protocol'},$settings{'portName'},$settings{'preference'},$settings{'ruleAction'}\n"; + $settings{'KEY1'} = ''; # End edit mode + &General::log($Lang::tr{'loxilb fw changed'}); + } + + if ($settings{'EN'} eq 'on') { + &DeleteFW(%settings); + &CreateFW(%settings); + } + + # Write changes to config file. + &SortDataFile; # sort newly added/modified entry + + #map ($settings{$_}='' ,@nosaved); # Clear fields + } +} + +if ($settings{'ACTION'} eq $Lang::tr{'edit'}) { + #move out new line + my $line = @current[$settings{'KEY1'}]; # KEY1 is the index in current + chomp($line); + my @temp = split(/\,/, $line); + $settings{'EN'}=$temp[0]; # Prepare the screen for editing + $settings{'sourceIP'}=$temp[1]; + $settings{'destinationIP'}=$temp[2]; + $settings{'minSourcePort'}=$temp[3]; + $settings{'maxSourcePort'}=$temp[4]; + $settings{'minDestinationPort'}=$temp[5]; + $settings{'maxDestinationPort'}=$temp[6]; + $settings{'protocol'}=$temp[7]; + $settings{'portName'}=$temp[8]; + $settings{'preference'}=$temp[9]; + $settings{'ruleAction'}=$temp[10]; + if ($settings{'EN'} eq 'on') { + &CreateFW(%settings); + } +} + +if ($settings{'ACTION'} eq $Lang::tr{'remove'}) { + + my $line = @current[$settings{'KEY1'}]; # KEY1 is the index in current + chomp($line); + my @temp = split(/\,/, $line); + $settings{'EN'}=$temp[0]; # Prepare the screen for editing + $settings{'sourceIP'}=$temp[1]; + $settings{'destinationIP'}=$temp[2]; + $settings{'minSourcePort'}=$temp[3]; + $settings{'maxSourcePort'}=$temp[4]; + $settings{'minDestinationPort'}=$temp[5]; + $settings{'maxDestinationPort'}=$temp[6]; + $settings{'protocol'}=$temp[7]; + $settings{'portName'}=$temp[8]; + $settings{'preference'}=$temp[9]; + $settings{'ruleAction'}=$temp[10]; + + &DeleteFW(%settings); + + splice (@current,$settings{'KEY1'},1); # Delete line + open(FILE, ">$datafile") or die 'route datafile error'; + print FILE @current; + close(FILE); + $settings{'KEY1'} = ''; # End remove mode + &General::log($Lang::tr{'loxilb fw changed'}); + + #&BuildConfiguration; # then re-build conf which use new data +} + +## Check if sorting is asked +# If same column clicked, reverse the sort. +if ($ENV{'QUERY_STRING'} =~ /$sortstring/ ) { + my $newsort=$ENV{'QUERY_STRING'}; + my $actual=$settings{'SORT_sourceIPLIST'}; + #Reverse actual sort ? + if ($actual =~ $newsort) { + my $Rev=''; + if ($actual !~ 'Rev') { + $Rev='Rev'; + } + $newsort.=$Rev; + } + $settings{'SORT_sourceIPLIST'}=$newsort; + map (delete ($settings{$_}) ,(@nosaved,'ACTION','KEY1'));# Must never be saved + &General::writehash($setting, \%settings); + &SortDataFile; + $settings{'ACTION'} = 'SORT'; # Create an 'ACTION' + map ($settings{$_} = '' ,@nosaved,'KEY1'); # and reinit vars to empty +} + +if ($settings{'ACTION'} eq '' ) { # First launch from GUI + # Place here default value when nothing is initialized + $settings{'EN'} = 'on'; + $settings{'sourceIP'} = ''; + $settings{'destinationIP'} = ''; + $settings{'minSourcePort'} = ''; + $settings{'maxSourcePort'} = ''; + $settings{'minDestinationPort'} = ''; + $settings{'maxDestinationPort'} = ''; + $settings{'protocol'} = ''; + $settings{'portName'} = ''; + $settings{'preference'} = ''; + $settings{'ruleAction'} = ''; +} + +&Header::openpage($Lang::tr{'loxilb fw entries'}, 1, ''); +&Header::openbigbox('100%', 'left', '', $errormessage); +my %checked=(); # Checkbox manipulations + +if ($errormessage) { + &Header::openbox('100%', 'left', $Lang::tr{'error messages'}); + print "$errormessage "; + &Header::closebox(); +} + +# + +$checked{'EN'}{'on'} = ($settings{'EN'} eq '' ) ? '' : "checked='checked'"; + +my $buttontext = $Lang::tr{'add'}; +if ($settings{'KEY1'} ne '') { + $buttontext = $Lang::tr{'update'}; + &Header::openbox('100%', 'left', $Lang::tr{'loxilb fw edit'}); +} else { + &Header::openbox('100%', 'left', $Lang::tr{'loxilb fw add'}); +} + +my @PROTOCOLS = ("any", "tcp", "udp", "icmp"); +my @RULEACTIONS = ("allow", "drop"); + +#Edited line number (KEY1) passed until cleared by 'save' or 'remove' or 'new sort order' +print < + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
$Lang::tr{'loxilb fw sourceIP'}: 
$Lang::tr{'loxilb fw destinationIP'}: $Lang::tr{'enabled'}
$Lang::tr{'loxilb fw minSourcePort'}: 
$Lang::tr{'loxilb fw maxSourcePort'}: 
$Lang::tr{'loxilb fw minDestinationPort'}: 
$Lang::tr{'loxilb fw maxDestinationPort'}: 
$Lang::tr{'loxilb fw proto'}:  +
$Lang::tr{'loxilb fw portName'}: 
$Lang::tr{'loxilb fw preference'}: 
$Lang::tr{'loxilb fw action'}:  +
+
+ + + + +
+ +END + +&Header::closebox(); + +&Header::openbox('100%', 'left', $Lang::tr{'loxilb fw entries'}); +print < + + $Lang::tr{'loxilb fw sourceIP'} + $Lang::tr{'loxilb fw destinationIP'} + $Lang::tr{'loxilb fw minSourcePort'} + $Lang::tr{'loxilb fw maxSourcePort'} + $Lang::tr{'loxilb fw minDestinationPort'} + $Lang::tr{'loxilb fw maxDestinationPort'} + $Lang::tr{'loxilb fw proto'} + $Lang::tr{'loxilb fw portName'} + $Lang::tr{'loxilb fw preference'} + $Lang::tr{'loxilb fw action'} + $Lang::tr{'action'} + +END +; + +# +# Print each line of @current list +# + +my $key = 0; +my $col=""; +foreach my $line (@current) { + chomp($line); # remove newline + my @temp=split(/\,/,$line); + $temp[2] ='' unless defined $temp[2]; # not always populated + $temp[3] ='' unless defined $temp[2]; # not always populated + $temp[4] ='' unless defined $temp[3]; # not always populated + $temp[5] ='' unless defined $temp[4]; # not always populated + $temp[6] ='' unless defined $temp[5]; # not always populated + $temp[7] ='' unless defined $temp[6]; # not always populated + $temp[8] ='' unless defined $temp[7]; # not always populated + $temp[9] ='' unless defined $temp[8]; # not always populated + $temp[10] ='' unless defined $temp[9]; # not always populated + + #Choose icon for checkbox + my $gif = ''; + my $gdesc = ''; + if ($temp[0] ne '' ) { + $gif = 'on.gif'; + $gdesc = $Lang::tr{'click to disable'}; + } else { + $gif = 'off.gif'; + $gdesc = $Lang::tr{'click to enable'}; + } + + #Colorize each line + if ($settings{'KEY1'} eq $key) { + print ""; + } elsif ($key % 2) { + print ""; + $col="bgcolor='$color{'color20'}'"; + } else { + print ""; + $col="bgcolor='$color{'color22'}'"; + } + print <$temp[1] +$temp[2] +$temp[3] +$temp[4] +$temp[5] +$temp[6] +$temp[7] +$temp[8] +$temp[9] +$temp[10] + +
+ + + +
+ + + +
+ + + +
+ + +END +; + $key++; +} +print ""; + +# If table contains entries, print 'Key to action icons' +if ($key) { +print < + +  $Lang::tr{'legend'}:  + $Lang::tr{ + $Lang::tr{'click to disable'} +    + $Lang::tr{ + $Lang::tr{'click to enable'} +    + $Lang::tr{ + $Lang::tr{'remove'} + + +END +; +} + +&Header::closebox(); + +&Header::closebigbox(); +&Header::closepage(); + +## Ouf it's the end ! + +# Sort the "current" array according to choices +sub SortDataFile +{ + our %entries = (); + + # Sort pair of record received in $a $b special vars. + # When IP is specified use numeric sort else alpha. + # If sortname ends with 'Rev', do reverse sort. + # + sub fixedleasesort { + my $qs=''; # The sort field specified minus 'Rev' + if (rindex ($settings{'SORT_sourceIPLIST'},'Rev') != -1) { + $qs=substr ($settings{'SORT_sourceIPLIST'},0,length($settings{'SORT_sourceIPLIST'})-3); + if ($qs eq 'sourceIP') { + my @a = split(/\./,$entries{$a}->{$qs}); + my @b = split(/\./,$entries{$b}->{$qs}); + ($b[0]<=>$a[0]) || + ($b[1]<=>$a[1]) || + ($b[2]<=>$a[2]) || + ($b[3]<=>$a[3]); + } else { + $entries{$b}->{$qs} cmp $entries{$a}->{$qs}; + } + } else { #not reverse + $qs=$settings{'SORT_sourceIPLIST'}; + if ($qs eq 'sourceIP') { + my @a = split(/\./,$entries{$a}->{$qs}); + my @b = split(/\./,$entries{$b}->{$qs}); + ($a[0]<=>$b[0]) || + ($a[1]<=>$b[1]) || + ($a[2]<=>$b[2]) || + ($a[3]<=>$b[3]); + } else { + $entries{$a}->{$qs} cmp $entries{$b}->{$qs}; + } + } + } + + #Use an associative array (%entries) + my $key = 0; + foreach my $line (@current) { + chomp( $line); #remove newline because can be on field 5 or 6 (addition of REMARK) + my @temp = ( '','','', ''); + @temp = split (',',$line); + + # Build a pair 'Field Name',value for each of the data dataline. + # Each SORTABLE field must have is pair. + # Other data fields (non sortable) can be grouped in one + + my @record = ('KEY',$key++,'EN',$temp[0],'sourceIP',$temp[1],'destinationIP',$temp[2],'minSourcePort',$temp[3],'maxSourcePort',$temp[4],'minDestinationPort',$temp[5],'maxDestinationPort',$temp[6],'protocol',$temp[7],'portName',$temp[8],'preference',$temp[9],'ruleAction',$temp[10]); + my $record = {}; # create a reference to empty hash + %{$record} = @record; # populate that hash with @record + $entries{$record->{KEY}} = $record; # add this to a hash of hashes + } + + open(FILE, ">$datafile") or die 'routing datafile error'; + + # Each field value is printed , with the newline ! Don't forget separator and order of them. + foreach my $entry (sort fixedleasesort keys %entries) { + print FILE "$entries{$entry}->{EN},$entries{$entry}->{sourceIP},$entries{$entry}->{destinationIP},$entries{$entry}->{minSourcePort},$entries{$entry}->{maxSourcePort},$entries{$entry}->{minDestinationPort},$entries{$entry}->{maxDestinationPort},$entries{$entry}->{protocol},$entries{$entry}->{portName},$entries{$entry}->{preference},$entries{$entry}->{ruleAction}\n"; + } + + close(FILE); + # Reload sorted @current + open (FILE, "$datafile"); + @current = ; + close (FILE); +} + +sub manageFW { + my ($action, %settings) = @_; + + # Initialize variables + my @loxicmd_options; + my $command = 'loxicmd'; + my $firewallRule = "--firewallRule="; # Start quote + + # Construct firewall rule + $firewallRule .= "sourceIP:$settings{'sourceIP'},destinationIP:$settings{'destinationIP'}"; + $firewallRule .= ",minSourcePort:$settings{'minSourcePort'}" if $settings{'minSourcePort'}; + $firewallRule .= ",maxSourcePort:$settings{'maxSourcePort'}" if $settings{'maxSourcePort'}; + $firewallRule .= ",minDestinationPort:$settings{'minDestinationPort'}" if $settings{'minDestinationPort'}; + $firewallRule .= ",maxDestinationPort:$settings{'maxDestinationPort'}" if $settings{'maxDestinationPort'}; + $firewallRule .= ",portName:$settings{'portName'}" if $settings{'portName'}; + + if ($settings{'protocol'} eq "any") { + $firewallRule .= ",protocol:0"; + } elsif ($settings{'protocol'} eq "tcp") { + $firewallRule .= ",protocol:6"; + } elsif ($settings{'protocol'} eq "udp") { + $firewallRule .= ",protocol:17"; + } elsif ($settings{'protocol'} eq "icmp") { + $firewallRule .= ",protocol:1"; + } + + $firewallRule .= ",preference:$settings{'preference'}" if $settings{'preference'}; + + my $ruleAction = "--$settings{'ruleAction'}"; + + # Push options for loxicmd + if ($action eq "create") { + push(@loxicmd_options, $action, "firewall", $firewallRule, $ruleAction); + } else { + push(@loxicmd_options, $action, "firewall", $firewallRule); + } + + # Execute the command + my $result = &General::system($command, @loxicmd_options); + + # Check for errors + if ($result != 0) { + print "Error: Failed to execute loxicmd command.\n"; + # You might want to add more detailed error handling here + } +} + +sub CreateFW { + my (%settings) = @_; + manageFW("create", %settings); +} + +sub DeleteFW { + my (%settings) = @_; + manageFW("delete", %settings); +} + diff --git a/langs/en/cgi-bin/en.pl b/langs/en/cgi-bin/en.pl index 89d5c8d5c..5840e442a 100644 --- a/langs/en/cgi-bin/en.pl +++ b/langs/en/cgi-bin/en.pl @@ -2471,6 +2471,21 @@ 'loxilb lb endpoints' => 'ENDPOINTS', 'loxilb lb eport' => 'EPORT', 'loxilb lb monitor' => 'MONITOR', +'loxilb fw' => 'LoxiLB Firewall Configuration', +'loxilb fw changed' => 'LoxiLB Firewall Changed', +'loxilb fw entries' => 'LoxiLB Firewall Entries', +'loxilb fw edit' => 'Edit LoxiLB Firewall', +'loxilb fw add' => 'Add LoxiLB Firewall', +'loxilb fw sourceIP' => 'sourceIP', +'loxilb fw destinationIP' => 'destinationIP', +'loxilb fw minSourcePort' => 'minSourcePort', +'loxilb fw maxSourcePort' => 'maxSourcePort', +'loxilb fw minDestinationPort' => 'minDestinationPort', +'loxilb fw maxDestinationPort' => 'maxDestinationPort', +'loxilb fw proto' => 'protocol', +'loxilb fw portName' => 'iport', +'loxilb fw preference' => 'preference', +'loxilb fw action' => 'ruleAction', 'status' => 'Status', 'status information' => 'Status information', 'status ovpn' => 'OpenVPN', diff --git a/langs/zh/cgi-bin/zh.pl b/langs/zh/cgi-bin/zh.pl index 12444d859..e42b22a24 100644 --- a/langs/zh/cgi-bin/zh.pl +++ b/langs/zh/cgi-bin/zh.pl @@ -2438,6 +2438,21 @@ 'loxilb lb endpoints' => '后端服务器', 'loxilb lb eport' => '前端端口', 'loxilb lb monitor' => '后端服务监控', +'loxilb fw' => 'eBPF LoxiLB 防火墙配置', +'loxilb fw changed' => '防火墙配置被修改', +'loxilb fw entries' => '当前防火墙配置', +'loxilb fw edit' => '编辑防火墙 ', +'loxilb fw add' => '添加防火墙', +'loxilb fw sourceIP' => '源网络地址CIDR', +'loxilb fw destinationIP' => '目的网络地址CIDR', +'loxilb fw minSourcePort' => '源网络最小端口', +'loxilb fw maxSourcePort' => '源网络最大端口', +'loxilb fw minDestinationPort' => '目的网络最小端口', +'loxilb fw maxDestinationPort' => '目的网络最大端口', +'loxilb fw proto' => '网络协议', +'loxilb fw portName' => '入端口', +'loxilb fw preference' => '优先级', +'loxilb fw action' => '执行', 'status' => '状态', 'status information' => '状态信息', 'status ovpn' => 'OpenVPN',