From 38657d9f98dbb82165e3c09a44577b5718803cf5 Mon Sep 17 00:00:00 2001 From: Tom Eastep Date: Fri, 4 Jan 2013 09:17:57 -0800 Subject: [PATCH] Support for arptables. Signed-off-by: Tom Eastep --- Shorewall-core/lib.cli | 89 ++++- Shorewall/Perl/Shorewall/ARP.pm | 294 ++++++++++++++ Shorewall/Perl/Shorewall/Compiler.pm | 60 ++- Shorewall/Perl/Shorewall/Config.pm | 22 +- Shorewall/Perl/Shorewall/Misc.pm | 6 +- Shorewall/Samples/Universal/shorewall.conf | 4 + .../Samples/one-interface/shorewall.conf | 4 + .../Samples/three-interfaces/shorewall.conf | 4 + .../Samples/two-interfaces/shorewall.conf | 4 + Shorewall/configfiles/arprules | 8 + Shorewall/configfiles/shorewall.conf | 4 + Shorewall/install.sh | 13 + Shorewall/manpages/shorewall-arprules.xml | 378 ++++++++++++++++++ Shorewall/manpages/shorewall.conf.xml | 48 ++- docs/Manpages.xml | 9 + 15 files changed, 921 insertions(+), 26 deletions(-) create mode 100644 Shorewall/Perl/Shorewall/ARP.pm create mode 100644 Shorewall/configfiles/arprules create mode 100644 Shorewall/manpages/shorewall-arprules.xml diff --git a/Shorewall-core/lib.cli b/Shorewall-core/lib.cli index 01934b17d..aef6e4078 100644 --- a/Shorewall-core/lib.cli +++ b/Shorewall-core/lib.cli @@ -25,7 +25,7 @@ # loaded after this one and replaces some of the functions declared here. # -SHOREWALL_CAPVERSION=40509 +SHOREWALL_CAPVERSION=40512 [ -n "${g_program:=shorewall}" ] @@ -329,11 +329,30 @@ logwatch() # $1 = timeout -- if negative, prompt each time that done } + +# +# Try to find the arptables binary -- sets the variable 'arptables' +# +resolve_arptables() { + arptables="$ARPTABLES" + + [ -n "${arptables:=arptables}" ] + + case $arptables in + */*) + ;; + *) + arptables=$(mywhich "$arptables") + ;; + esac +} + # # Save currently running configuration # do_save() { local status + local arptables status=0 if [ -f ${VARDIR}/firewall ]; then @@ -353,6 +372,42 @@ do_save() { status=1 fi + case ${SAVE_ARPTABLES:=No} in + [Yy]es) + resolve_arptables + + if [ -n "$arptables" ]; then + # + # 'sed' command is a hack to work around broken arptables_jf + # + if ${arptables}-save | sed 's/-p[[:space:]]\+0\([[:digit:]]\)00\/ffff/-p 000\1\/ffff/' > ${VARDIR}/restore-$$; then + if grep -q '^-A' ${VARDIR}/restore-$$; then + mv -f ${VARDIR}/restore-$$ ${g_restorepath}-arptables + else + rm -f ${VARDIR}/restore-$$ + fi + fi + else + case "$ARPTABLES" in + */*) + error_message "ERROR: ARPTABLES=$ARPTABLES does not exist or is not executable - arptables not saved" + ;; + *) + error_message "ERROR: The arptables utility cannot be located - arptables not saved" + ;; + esac + + rm -f ${g_restorepath}-arptables + fi + ;; + [Nn]o) + rm -f ${g_restorepath}-arptables + ;; + *) + error_message "WARNING: Invalid value ($SAVE_ARPTABLES) for SAVE_ARPTABLES" + ;; + esac + case ${SAVE_IPSETS:=No} in [Yy]es) case ${IPSET:=ipset} in @@ -683,6 +738,7 @@ show_command() { table_given= local output_filter output_filter=cat + local arptables show_macro() { foo=`grep 'This macro' $macro | sed 's/This macro //'` @@ -999,6 +1055,17 @@ show_command() { echo show_nfacct ;; + arptables) + [ $# -gt 1 ] && usage 1 + resolve_arptables + if [ -n "$arptables" -a -x $arptables ]; then + echo "$g_product $SHOREWALL_VERSION arptables at $g_hostname - $(date)" + echo + $arptables -L -n -v + else + error_message "Cannot locate the arptables executable" + fi + ;; *) case "$g_program" in *-lite) @@ -1156,6 +1223,9 @@ dump_filter() { do_dump_command() { local finished finished=0 + local arptables + + resolve_arptables while [ $finished -eq 0 -a $# -gt 0 ]; do option=$1 @@ -1230,6 +1300,11 @@ do_dump_command() { host=$(echo $g_hostname | sed 's/\..*$//') $g_tool -L $g_ipt_options + if [ -n "$arptables" -a -x "$arptables" ]; then + heading "ARP rules" + $arptables -L -n -v + fi + heading "Log ($LOGFILE)" packet_log 20 @@ -2035,6 +2110,7 @@ determine_capabilities() { local tool local chain local chain1 + local arptables if [ -z "$g_tool" ]; then [ $g_family -eq 4 ] && tool=iptables || tool=ip6tables @@ -2125,6 +2201,7 @@ determine_capabilities() { RPFILTER_MATCH= NFACCT_MATCH= CHECKSUM_TARGET= + ARPTABLESJF= AMANDA_HELPER= FTP_HELPER= FTP0_HELPER= @@ -2141,6 +2218,12 @@ determine_capabilities() { TFTP_HELPER= TFTP0_HELPER= + resolve_arptables + + if [ -n "$arptables" -a -x $arptables ]; then + qt $arptables -L OUT && ARPTABLESJF=Yes + fi + chain=fooX$$ if [ -n "$NAT_ENABLED" ]; then @@ -2524,6 +2607,7 @@ report_capabilities_unsorted() { report_capability "RPFilter match" $RPFILTER_MATCH report_capability "NFAcct match" $NFACCT_MATCH report_capability "Checksum Target" $CHECKSUM_TARGET + report_capability "Arptables JF" $ARPTABLESJF report_capability "Amanda Helper" $AMANDA_HELPER report_capability "FTP Helper" $FTP_HELPER @@ -2641,6 +2725,7 @@ report_capabilities_unsorted1() { report_capability1 RPFILTER_MATCH report_capability1 NFACCT_MATCH report_capability1 CHECKSUM_TARGET + report_capability1 ARPTABLESJF report_capability1 AMANDA_HELPER report_capability1 FTP_HELPER @@ -2784,6 +2869,7 @@ forget_command() { rm -f $g_restorepath rm -f ${g_restorepath}-iptables rm -f ${g_restorepath}-ipsets + rm -f ${g_restorepath}-arptables echo " $g_restorepath removed" elif [ -f $g_restorepath ]; then echo " $g_restorepath exists and is not a saved $g_product configuration" @@ -3215,6 +3301,7 @@ usage() # $1 = exit status echo " save [ ]" echo " show [ -b ] [ -x ] [ -t {filter|mangle|nat} ] [ {chain [ [ ... ]" echo " show [ -f ] capabilities" + echo " show arptables" echo " show classifiers" echo " show config" echo " show connections" diff --git a/Shorewall/Perl/Shorewall/ARP.pm b/Shorewall/Perl/Shorewall/ARP.pm new file mode 100644 index 000000000..226350cd2 --- /dev/null +++ b/Shorewall/Perl/Shorewall/ARP.pm @@ -0,0 +1,294 @@ +# +# Shorewall 4.5 -- /usr/share/shorewall/Shorewall/ARP.pm +# +# This program is under GPL [http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt] +# +# (c) 2013 - Tom Eastep (teastep@shorewall.net) +# +# Complete documentation is available at http://shorewall.net +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of Version 2 of the GNU General Public License +# as published by the Free Software Foundation. +# +# 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# This file is responsible for Shorewall's arptables support +# +package Shorewall::ARP; +require Exporter; + +use Shorewall::Config qw(:DEFAULT :internal); +use Shorewall::Zones; +use Shorewall::IPAddrs; +use strict; + +our @ISA = qw(Exporter); +our @EXPORT = ( qw( process_arprules create_arptables_load preview_arptables_load ) ); + +our %arp_table; +our $arp_input; +our $arp_output; +our $arp_forward; +our $sourcemac; +our $destmac; +our @builtins; +our $arptablesjf; +our @map = ( qw( 0 Request Reply Request_Reverse Reply_Reverse DRARP_Request DRARP_Reply DRARP_Error InARP_Request ARP_NAK ) ); + +sub match_arp_net( $$$ ) { + my ( $net, $mac, $source ) = @_; + + my $return = ''; + + if ( supplied $net ) { + my ( $addr , $mask ) = split( $net , '/', 2 ); + + my $invert = ( $addr =~ s/^!// ) ? '! ' : ''; + + validate_net $net, 0; + $return = $source ? "-s $net " : "-d $net "; + } + + if ( supplied $mac ) { + my ( $addr , $mask ) = split( $mac , '/', 2 ); + + my $invert = ( $addr =~ s/^!// ) ? '! ' : ''; + + fatal_error "Invalid MAC address ($net)" unless $addr =~ /^(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$/; + if ( supplied $mask ) { + fatal_error "Invalid MAC Mask ($mask)" unless $mask =~ /^(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$/; + $return .= $source ? "$sourcemac $invert$addr/$mask " : "$destmac $invert$addr/mask "; + } else { + $return .= $source ? "$sourcemac $invert$addr " : "$destmac $invert$addr "; + } + } + + $return; +} + +sub process_arprule() { + my ( $originalaction, $source, $dest, $opcode ) = split_line( 'arprules file entry', {action => 0, source => 1, dest => 2, opcode => 3 } ); + + my $chainref; + my $iiface; + my $diface; + my $saddr; + my $smac; + my $daddr; + my $dmac; + my $rule = ''; + + fatal_error "ACTION must be specified" if $originalaction eq '-'; + + my ( $action, $newaddr ) = split( ':', $originalaction, 2 ); + + my %functions = ( DROP => sub() { $rule .= "-j DROP" }, + ACCEPT => sub() { $rule .= "-j ACCEPT" }, + SNAT => sub() { validate_address $newaddr, 0; + $rule .= "-j mangle --mangle-ip-s $newaddr"; }, + DNAT => sub() { validate_address $newaddr, 0; + $rule .= "-j mangle --mangle-ip-d $newaddr"; }, + SMAT => sub() { fatal_error "Invalid MAC address ($newaddr)" unless $newaddr =~ /^(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$/; + $rule .= "-j mangle --mangle-mac-s $newaddr"; }, + DMAT => sub() { fatal_error "Invalid MAC address ($newaddr)" unless $newaddr =~ /^(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$/; + $rule .= "-j mangle --mangle-mac-d $newaddr"; }, + SNATC => sub() { validate_address $newaddr, 0; + $rule .= "-j mangle --mangle-ip-s $newaddr--mangle-target CONTINUE"; }, + DNATC => sub() { validate_address $newaddr, 0; + $rule .= "-j mangle --mangle-ip-d $newaddr--mangle-target CONTINUE"; }, + SMATC => sub() { fatal_error "Invalid MAC address ($newaddr)" unless $newaddr =~ /^(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$/; + $rule .= "-j mangle --mangle-mac-s $newaddr--mangle-target CONTINUE"; }, + DMATC => sub() { fatal_error "Invalid MAC address ($newaddr)" unless $newaddr =~ /^(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$/; + $rule .= "-j mangle --mangle-mac-d $newaddr --mangle-target CONTINUE"; }, + ); + + if ( supplied $newaddr ) { + fatal_error "The $action ACTION does not allow a new address" unless $action =~ /^SNAT|DNAT|SMAT|DMAT$/; + } else { + fatal_error "Invalid ACTION ($action)" unless $action =~ /^DROP|ACCEPT$/; + } + + if ( $source ne '-' ) { + if ( $source =~ /^(.+?):(.*)(?::(.*))?/ ) { + $iiface = $1; + $saddr = $2; + $smac = $3; + } else { + $iiface = $source; + } + + $iiface = find_interface( $iiface )->{physical}; + + fatal_error "Wildcard Interfaces ( $iiface )may not be used in this context" if $iiface =~ /\+$/; + + $rule .= "-i $iiface "; + $rule .= match_arp_net( $saddr , $smac, 1 ) if supplied( $saddr ); + $chainref = $arp_input; + } + + if ( $dest ne '-' ) { + if ( $dest =~ /^(.+?):(.*)(?::(.*))?/ ) { + $diface = $1; + $daddr = $2; + $dmac = $3; + } else { + $diface = $dest; + } + + $diface = find_interface( $diface )->{physical}; + + fatal_error "A wildcard interfaces ( $diface) may not be used in this context" if $diface =~ /\+$/; + + if ( $iiface ) { + fatal_error "When both SOURCE and DEST are given, the interfaces must be ports on the same bridge" + if $iiface->{bridge} ne $diface->{bridge}; + $chainref = $arp_forward; + } else { + $chainref = $arp_output; + } + + $rule .= "-o $diface "; + $rule .= match_arp_net( $daddr , $dmac, 0 ) if supplied( $daddr ); + + } + + if ( $opcode ne '-' ) { + my $invert = ( $opcode =~ s/^!// ) ? '! ' : ''; + fatal_error "Invalid ARP OPCODE ($opcode)" unless $opcode =~ /^\d$/ && $opcode; + $rule .= $arptablesjf ? " --arpop ${invert}$map[$opcode] " : "--opcode $opcode "; + } + + $functions{$action} ->(); + + fatal_error "Either SOURCE or DEST must be specified" unless $chainref; + + push @$chainref, $rule; + +} + +sub process_arprules() { + my $result = 0; + + if ( $arptablesjf = have_capability 'ARPTABLESJF' ) { + $arp_input = $arp_table{IN} = []; + $arp_output = $arp_table{OUT} = []; + $arp_forward = $arp_table{FORWARD} = []; + @builtins = qw( IN OUT FORWARD ); + $sourcemac = '-z'; + $destmac = '-y'; + } else { + $arp_input = $arp_table{INPUT} = []; + $arp_output = $arp_table{OUTPUT} = []; + $arp_forward = $arp_table{FORWARD} = []; + @builtins = qw( INPUT OUTPUT FORWARD ); + $sourcemac = '--source-mac'; + $destmac = '--dest-mac'; + } + + my $fn = open_file 'arprules'; + + if ( $fn ) { + first_entry( sub() { + $result = 1; + progress_message2 "$doing $fn..."; } + ); + process_arprule while read_a_line( NORMAL_READ ); + } + + $result; +} + +sub create_arptables_load( $ ) { + my $test = shift; + + emit ( '#', + '# Create the input to arptables-restore and pass that input to the utility', + '#', + 'setup_arptables()', + '{' + ); + + push_indent; + + save_progress_message "Preparing arptables-restore input..."; + + emit ''; + + emit "exec 3>\${VARDIR}/.arptables-input"; + + my $date = localtime; + + unless ( $test ) { + emit_unindented '#'; + emit_unindented "# Generated by Shorewall $globals{VERSION} - $date"; + emit_unindented '#'; + } + + emit ''; + emit 'cat >&3 << __EOF__'; + + emit_unindented "*filter"; + + emit_unindented ":$_ ACCEPT" for @builtins; + + for ( @builtins ) { + my $rules = $arp_table{$_}; + + while ( my $rule = shift @$rules ) { + emit_unindented "-A $_ $rule"; + } + } + + emit_unindented "COMMIT\n" if $arptablesjf; + + emit_unindented "__EOF__"; + + # + # Now generate the actual ip[6]tables-restore command + # + emit( 'exec 3>&-', + '', + 'progress_message2 "Running $ARPTABLES_RESTORE..."', + '', + 'cat ${VARDIR}/.arptables-input | $ARPTABLES_RESTORE # Use this nonsensical form to appease SELinux', + 'if [ $? != 0 ]; then', + qq( fatal_error "arptables-restore Failed. Input is in \${VARDIR}/.arptables-input"), + "fi\n" + ); + + pop_indent; + emit "}\n"; +} + +sub preview_arptables_load() { + + my $date = localtime; + + print "#\n# Generated by Shorewall $globals{VERSION} - $date\n#\n"; + + print "*filter\n"; + + print ":$_ ACCEPT\n" for qw( INPUT OUTPUT FORWARD ); + + for ( @builtins ) { + my $rules = $arp_table{$_}; + + while ( my $rule = shift @$rules ) { + print "-A $rule\n"; + } + } + + print "COMMIT\n" if $arptablesjf; + + print "\n"; +} + +1; diff --git a/Shorewall/Perl/Shorewall/Compiler.pm b/Shorewall/Perl/Shorewall/Compiler.pm index 9246243ed..2cfe0b0ac 100644 --- a/Shorewall/Perl/Shorewall/Compiler.pm +++ b/Shorewall/Perl/Shorewall/Compiler.pm @@ -36,6 +36,7 @@ use Shorewall::Proc; use Shorewall::Proxyarp; use Shorewall::Raw; use Shorewall::Misc; +use Shorewall::ARP; use strict; @@ -50,6 +51,8 @@ our $test; our $family; +our $have_arptables; + # # Initilize the package-globals in the other modules # @@ -226,6 +229,22 @@ sub generate_script_2() { set_chain_variables; + my $need_arptables = $have_arptables || $config{SAVE_ARPTABLES}; + + if ( my $arptables = $config{ARPTABLES} ) { + emit( qq(ARPTABLES="$arptables"), + '[ -x "$ARPTABLES" ] || startup_error "ARPTABLES=$ARPTABLES does not exist or is not executable"', + ); + } elsif ( $need_arptables ) { + emit( '[ -z "$ARPTABLES" ] && ARPTABLES=$(mywhich arptables)', + '[ -n "$ARPTABLES" -a -x "$ARPTABLES" ] || startup_error "Can\'t find arptables executable"' ); + } + + if ( $need_arptables ) { + emit( 'ARPTABLES_RESTORE=${ARPTABLES}-restore', + '[ -x "$ARPTABLES_RESTORE" ] || startup_error "$ARPTABLES_RESTORE does not exist or is not executable"' ); + } + if ( $config{EXPORTPARAMS} ) { append_file 'params'; } else { @@ -323,6 +342,7 @@ sub generate_script_3($) { } create_netfilter_load( $test ); + create_arptables_load( $test ) if $have_arptables; create_chainlist_reload( $_[0] ); emit "#\n# Start/Restart the Firewall\n#"; @@ -450,16 +470,25 @@ sub generate_script_3($) { ' if [ -f $iptables_save_file ]; then' ); if ( $family == F_IPV4 ) { - emit ' cat $iptables_save_file | $IPTABLES_RESTORE # Use this nonsensical form to appease SELinux' + emit( ' cat $iptables_save_file | $IPTABLES_RESTORE # Use this nonsensical form to appease SELinux' ); + + emit( '', + ' arptables_save_file=${VARDIR}/$(basename $0)-arptables', + ' if [ -f $arptables_save_file ]; then', + ' cat $arptables_save_file | $ARPTABLES_RESTORE', + ' fi') + if $config{SAVE_ARPTABLES}; + } else { emit ' cat $iptables_save_file | $IP6TABLES_RESTORE # Use this nonsensical form to appease SELinux' } - emit<<'EOF'; - else - fatal_error "$iptables_save_file does not exist" - fi -EOF + emit( ' else', + ' fatal_error "$iptables_save_file does not exist"', + ' fi', + '' + ); + push_indent; setup_load_distribution; setup_forwarding( $family , 1 ); @@ -489,6 +518,7 @@ EOF ' setup_netfilter' ); push_indent; + emit 'setup_arptables' if $have_arptables; setup_load_distribution; pop_indent; @@ -544,8 +574,9 @@ sub compiler { my ( $scriptfilename, $directory, $verbosity, $timestamp , $debug, $chains , $log , $log_verbosity, $preview, $confess , $update , $annotate , $convert, $config_path, $shorewallrc , $shorewallrc1 , $directives ) = ( '', '', -1, '', 0, '', '', -1, 0, 0, 0, 0, , 0 , '' , '/usr/share/shorewall/shorewallrc', '' , 0 ); - $export = 0; - $test = 0; + $export = 0; + $test = 0; + $have_arptables = 0; sub validate_boolean( $ ) { my $val = numeric_value( shift ); @@ -754,6 +785,8 @@ sub compiler { emit "}\n"; # End of setup_routing_and_traffic_shaping() } + $have_arptables = process_arprules if $family == F_IPV4; + disable_script; # # N E T F I L T E R @@ -837,7 +870,7 @@ sub compiler { generate_script_2; # # N E T F I L T E R L O A D - # (Produces setup_netfilter(), chainlist_reload() and define_firewall() ) + # (Produces setup_netfilter(), setup_arptables(), chainlist_reload() and define_firewall() ) # generate_script_3( $chains ); # @@ -850,7 +883,7 @@ sub compiler { # S T O P _ F I R E W A L L # (Writes the stop_firewall() function to the compiled script) # - compile_stop_firewall( $test, $export ); + compile_stop_firewall( $test, $export , $have_arptables ); # # U P D O W N # (Writes the updown() function to the compiled script) @@ -898,7 +931,10 @@ sub compiler { generate_script_2 if $debug; - preview_netfilter_load if $preview; + if ( $preview ) { + preview_netfilter_load; + preview_arptables_load if $have_arptables; + } } # # Re-initialize the chain table so that process_routestopped() has the same @@ -908,7 +944,7 @@ sub compiler { initialize_chain_table(0); if ( $debug ) { - compile_stop_firewall( $test, $export ); + compile_stop_firewall( $test, $export, $have_arptables ); disable_script; } else { # diff --git a/Shorewall/Perl/Shorewall/Config.pm b/Shorewall/Perl/Shorewall/Config.pm index 2ca1d83c8..2f4095028 100644 --- a/Shorewall/Perl/Shorewall/Config.pm +++ b/Shorewall/Perl/Shorewall/Config.pm @@ -350,6 +350,7 @@ our %capdesc = ( NAT_ENABLED => 'NAT', RPFILTER_MATCH => 'RPFilter Match', NFACCT_MATCH => 'NFAcct Match', CHECKSUM_TARGET => 'Checksum Target', + ARPTABLESJF => 'Arptables JF', AMANDA_HELPER => 'Amanda Helper', FTP_HELPER => 'FTP Helper', FTP0_HELPER => 'FTP-0 Helper', @@ -636,7 +637,7 @@ sub initialize( $;$$) { KLUDGEFREE => '', STATEMATCH => '-m state --state', VERSION => "4.5.12-Beta3", - CAPVERSION => 40509 , + CAPVERSION => 40512 , ); # # From shorewall.conf file @@ -722,6 +723,7 @@ sub initialize( $;$$) { MACLIST_TABLE => undef, MACLIST_TTL => undef, SAVE_IPSETS => undef, + SAVE_ARPTABLES => undef, MAPOLDACTIONS => undef, FASTACCEPT => undef, IMPLICIT_CONTINUE => undef, @@ -880,6 +882,7 @@ sub initialize( $;$$) { RPFILTER_MATCH => undef, NFACCT_MATCH => undef, CHECKSUM_TARGET => undef, + ARPTABLESJF => undef, AMANDA_HELPER => undef, FTP_HELPER => undef, @@ -1015,12 +1018,14 @@ sub initialize( $;$$) { $globals{CONFDIR} = "$shorewallrc{CONFDIR}/shorewall"; $globals{PRODUCT} = 'shorewall'; $config{IPTABLES} = undef; + $config{ARPTABLES} = undef; $validlevels{ULOG} = 'ULOG'; } else { $globals{SHAREDIR} = "$shorewallrc{SHAREDIR}/shorewall6"; $globals{CONFDIR} = "$shorewallrc{CONFDIR}/shorewall6"; $globals{PRODUCT} = 'shorewall6'; $config{IP6TABLES} = undef; + delete $config{ARPTABLES}; } %shorewallrc1 = %shorewallrc unless $shorewallrc1; @@ -3911,9 +3916,21 @@ sub Checksum_Target() { have_capability 'MANGLE_ENABLED' && qt1( "$iptables -t mangle -A $sillyname -j CHECKSUM --checksum-fill" ); } +sub Arptables_JF() { + my $arptables = $config{ARPTABLES}; + + $arptables = which( 'arptables' ) unless supplied $arptables; + + if ( $arptables && -f $arptables && -x _ ) { + $config{ARPTABLES} = $arptables; + qt( "$arptables -L OUT" ); + } +} + our %detect_capability = ( ACCOUNT_TARGET =>\&Account_Target, AMANDA_HELPER => \&Amanda_Helper, + ARPTABLESJF => \&Arptables_JF, AUDIT_TARGET => \&Audit_Target, ADDRTYPE => \&Addrtype, BASIC_FILTER => \&Basic_Filter, @@ -4986,6 +5003,7 @@ sub get_configuration( $$$$ ) { unsupported_yes_no_warning 'RFC1918_STRICT'; default_yes_no 'SAVE_IPSETS' , ''; + default_yes_no 'SAVE_ARPTABLES' , ''; default_yes_no 'STARTUP_ENABLED' , 'Yes'; default_yes_no 'DELAYBLACKLISTLOAD' , ''; default_yes_no 'MAPOLDACTIONS' , 'Yes'; @@ -5496,7 +5514,7 @@ sub generate_aux_config() { emit "#\n# Shorewall auxiliary configuration file created by Shorewall version $globals{VERSION} - $date\n#"; - for my $option ( qw(VERBOSITY LOGFILE LOGFORMAT IPTABLES IP6TABLES IP TC IPSET PATH SHOREWALL_SHELL SUBSYSLOCK LOCKFILE RESTOREFILE) ) { + for my $option ( qw(VERBOSITY LOGFILE LOGFORMAT ARPTABLES IPTABLES IP6TABLES IP TC IPSET PATH SHOREWALL_SHELL SUBSYSLOCK LOCKFILE RESTOREFILE) ) { conditionally_add_option $option; } diff --git a/Shorewall/Perl/Shorewall/Misc.pm b/Shorewall/Perl/Shorewall/Misc.pm index e99d37c7c..2fbe49488 100644 --- a/Shorewall/Perl/Shorewall/Misc.pm +++ b/Shorewall/Perl/Shorewall/Misc.pm @@ -2317,8 +2317,8 @@ sub setup_mss( ) { # # Compile the stop_firewall() function # -sub compile_stop_firewall( $$ ) { - my ( $test, $export ) = @_; +sub compile_stop_firewall( $$$ ) { + my ( $test, $export, $have_arptables ) = @_; my $input = $filter_table->{INPUT}; my $output = $filter_table->{OUTPUT}; @@ -2523,6 +2523,8 @@ EOF create_stop_load $test; if ( $family == F_IPV4 ) { + emit( '$ARPTABLES -F', + '' ) if $have_arptables; if ( $config{IP_FORWARDING} eq 'on' ) { emit( 'echo 1 > /proc/sys/net/ipv4/ip_forward', 'progress_message2 IPv4 Forwarding Enabled' ); diff --git a/Shorewall/Samples/Universal/shorewall.conf b/Shorewall/Samples/Universal/shorewall.conf index 910cd391f..93c02f110 100644 --- a/Shorewall/Samples/Universal/shorewall.conf +++ b/Shorewall/Samples/Universal/shorewall.conf @@ -55,6 +55,8 @@ TCP_FLAGS_LOG_LEVEL=info # L O C A T I O N O F F I L E S A N D D I R E C T O R I E S ############################################################################### +ARPTABLES= + CONFIG_PATH=${CONFDIR}/shorewall:${SHAREDIR}/shorewall GEOIPDIR=/usr/share/xt_geoip/LE @@ -194,6 +196,8 @@ RETAIN_ALIASES=No ROUTE_FILTER=No +SAVE_ARPTABLES=No + SAVE_IPSETS=No TC_ENABLED=Internal diff --git a/Shorewall/Samples/one-interface/shorewall.conf b/Shorewall/Samples/one-interface/shorewall.conf index 7c06fcaad..b446ff6f0 100644 --- a/Shorewall/Samples/one-interface/shorewall.conf +++ b/Shorewall/Samples/one-interface/shorewall.conf @@ -66,6 +66,8 @@ TCP_FLAGS_LOG_LEVEL=info # L O C A T I O N O F F I L E S A N D D I R E C T O R I E S ############################################################################### +ARPTABLES= + CONFIG_PATH=${CONFDIR}/shorewall:${SHAREDIR}/shorewall GEOIPDIR=/usr/share/xt_geoip/LE @@ -205,6 +207,8 @@ RETAIN_ALIASES=No ROUTE_FILTER=No +SAVE_ARPTABLES=No + SAVE_IPSETS=No TC_ENABLED=Internal diff --git a/Shorewall/Samples/three-interfaces/shorewall.conf b/Shorewall/Samples/three-interfaces/shorewall.conf index a139d99d0..ddd75d2e3 100644 --- a/Shorewall/Samples/three-interfaces/shorewall.conf +++ b/Shorewall/Samples/three-interfaces/shorewall.conf @@ -64,6 +64,8 @@ TCP_FLAGS_LOG_LEVEL=info # L O C A T I O N O F F I L E S A N D D I R E C T O R I E S ############################################################################### +ARPTABLES= + CONFIG_PATH=${CONFDIR}/shorewall:${SHAREDIR}/shorewall GEOIPDIR=/usr/share/xt_geoip/LE @@ -203,6 +205,8 @@ RETAIN_ALIASES=No ROUTE_FILTER=No +SAVE_ARPTABLES=No + SAVE_IPSETS=No TC_ENABLED=Internal diff --git a/Shorewall/Samples/two-interfaces/shorewall.conf b/Shorewall/Samples/two-interfaces/shorewall.conf index d5d5ed571..5a7f64f26 100644 --- a/Shorewall/Samples/two-interfaces/shorewall.conf +++ b/Shorewall/Samples/two-interfaces/shorewall.conf @@ -67,6 +67,8 @@ TCP_FLAGS_LOG_LEVEL=info # L O C A T I O N O F F I L E S A N D D I R E C T O R I E S ############################################################################### +ARPTABLES= + CONFIG_PATH=${CONFDIR}/shorewall:${SHAREDIR}/shorewall GEOIPDIR=/usr/share/xt_geoip/LE @@ -206,6 +208,8 @@ RETAIN_ALIASES=No ROUTE_FILTER=No +SAVE_ARPTABLES=No + SAVE_IPSETS=No TC_ENABLED=Internal diff --git a/Shorewall/configfiles/arprules b/Shorewall/configfiles/arprules new file mode 100644 index 000000000..705cce458 --- /dev/null +++ b/Shorewall/configfiles/arprules @@ -0,0 +1,8 @@ +# +# Shorewall version 4 - arprules File +# +# For information about entries in this file, type "man shorewall-arprules" +# +############################################################################################################## +#ACTION SOURCE DEST ARP +# OPCODE diff --git a/Shorewall/configfiles/shorewall.conf b/Shorewall/configfiles/shorewall.conf index 3458af366..6ee7b7d45 100644 --- a/Shorewall/configfiles/shorewall.conf +++ b/Shorewall/configfiles/shorewall.conf @@ -55,6 +55,8 @@ TCP_FLAGS_LOG_LEVEL=info # L O C A T I O N O F F I L E S A N D D I R E C T O R I E S ############################################################################### +ARPTABLES= + CONFIG_PATH="${CONFDIR}/shorewall:${SHAREDIR}/shorewall" GEOIPDIR=/usr/share/xt_geoip/LE @@ -194,6 +196,8 @@ RETAIN_ALIASES=No ROUTE_FILTER=No +SAVE_ARPTABLES=No + SAVE_IPSETS=No TC_ENABLED=Internal diff --git a/Shorewall/install.sh b/Shorewall/install.sh index 5c0f5646c..5ce63ca9c 100755 --- a/Shorewall/install.sh +++ b/Shorewall/install.sh @@ -641,6 +641,19 @@ if [ -f masq ]; then echo "Masquerade file installed as ${DESTDIR}${CONFDIR}/$PRODUCT/masq" fi fi + +if [ -f arprules ]; then + # + # Install the ARP rules file + # + run_install $OWNERSHIP -m 0644 arprules ${DESTDIR}${SHAREDIR}/$PRODUCT/configfiles + run_install $OWNERSHIP -m 0644 arprules.annotated ${DESTDIR}${SHAREDIR}/$PRODUCT/configfiles + + if [ -z "$SPARSE" -a ! -f ${DESTDIR}${CONFDIR}/$PRODUCT/arprules ]; then + run_install $OWNERSHIP -m 0600 arprules${suffix} ${DESTDIR}${CONFDIR}/$PRODUCT/arprules + echo "ARP rules file installed as ${DESTDIR}${CONFDIR}/$PRODUCT/arprules" + fi +fi # # Install the Conntrack file # diff --git a/Shorewall/manpages/shorewall-arprules.xml b/Shorewall/manpages/shorewall-arprules.xml new file mode 100644 index 000000000..367dbe3b7 --- /dev/null +++ b/Shorewall/manpages/shorewall-arprules.xml @@ -0,0 +1,378 @@ + + + + + shorewall-arprules + + 5 + + + + arprules + + Shorewall ARP rules file + + + + + /etc/shorewall/arprules + + + + + Description + + This file was added in Shorwall 4.5.12 and is used to describe + low-level rules managed by arptables (8). These rules only affect Address + Resolution Protocol (ARP), Reverse Address Resolution Protocol (RARP) and + Dynamic Reverse Address Resolution Protocol (DRARP) frames. + + The columns in the file are as shown below. MAC addresses are + specified normally (6 hexidecimal numbers separated by colons). + + + + ACTION + + + Describes the action to take when a frame matches the criteria + in the other columns. Possible values are: + + + + ACCEPT + + + This is the default action if no rules matches a frame; + it lets the frame go through. + + + + + DROP + + + Causes the frame to be dropped. + + + + + SNAT:ip-address + + + Modifies the source IP address to the specified + ip-address. + + + + + DNAT:ip-address + + + Modifies the destination IP address to the specified + ip-address. + + + + + SMAT:mac-address + + + Modifies the source MAC address to the specified + mac-address. + + + + + DMAT:mac-address + + + Modifies the destination MAC address to the specified + mac-address. + + + + + SNATC:ip-address + + + Like SNAT except that the frame is then passed to the + next rule. + + + + + DNAC:ip-address + + + Like DNAT except that the frame is then passed to the + next rule. + + + + + SMATC:mac-address + + + Like SMAT except that the frame is then passed to the + next rule. + + + + + DMATC:mac-address + + + Like DMAT except that the frame is then passed to the + next rule. + + + + + + + + SOURCE - [interface[:[!]ipaddress[/ipmask][:[!]macaddress[/macmask]]]] + + + Where + + + + interface + + + Is an interface defined in + shorewall-interfaces(5). + + + + + ipaddress + + + is an IPv4 address. + + + + + ipmask + + + specifies a mask to be applied to + ipaddress. + + + + + macaddress + + + The source MAC address. + + + + + macmask + + + Mask for MAC address; must be specified as 6 hexidecimal + numbers separated by colons. + + + + + When '!' is specified, the test is inverted. + + If not specified, matches only frames originating on the + firewall itself. + + + Either SOURCE or DEST must be specified. + + + + + + DEST - [interface[:[!]ipaddress[/ipmask][:[!]macaddress[/macmask]]]] + + + Where + + + + interface + + + Is an interface defined in + shorewall-interfaces(5). + + + + + ipaddress + + + is an IPv4 address or a MAC address. + + + + + ipmask + + + specifies a mask to be applied to frame + addresses. + + + + + macaddress + + + The destination MAC address. + + + + + macmask + + + Mask for MAC address; must be specified as 6 hexidecimal + numbers separated by colons. + + + + + When '!' is specified, the test is inverted and the rule + matches frames which do not match the specified address/mask. + + If not specified, matches only frames originating on the + firewall itself. + + If both SOURCE and DEST are specified, then both interfaces + must be bridge ports on the same bridge. + + + Either SOURCE or DEST must be specified. + + + + + + ARP OPCODE - [[!]opcode] + + + Optional. Describes the type of frame. Possible + opcode values are: + + + + 1 + + + ARP Request + + + + + 2 + + + ARP Reply + + + + + 3 + + + RARP Request + + + + + 4 + + + RARP Reply + + + + + 5 + + + Dynamic RARP Request + + + + + 6 + + + Dynamic RARP Reply + + + + + 7 + + + Dynamic RARP Error + + + + + 8 + + + InARP Request + + + + + 9 + + + ARP NAK + + + + + When '!' is specified, the test is inverted and the rule + matches frames which do not match the specifed + opcode. + + + + + + + Example + + The eth1 interface has both a pubiic IP address and a private + address (10.1.10.11/24). When sending ARP requests to 10.1.10.0/24, use + the private address as the IP source: + + #ACTION SOURCE DEST ARP OPCODE +SNAT:10.1.10.11 - eth1:10.1.10.0/24 1 + + + + FILES + + /etc/shorewall/arprules + + diff --git a/Shorewall/manpages/shorewall.conf.xml b/Shorewall/manpages/shorewall.conf.xml index 6bc875b62..4a352472f 100644 --- a/Shorewall/manpages/shorewall.conf.xml +++ b/Shorewall/manpages/shorewall.conf.xml @@ -282,16 +282,18 @@ IGNOREUNKNOWNVARIABLES=[Yes|No] + role="bold">ARPTABLES=[pathname] - Added in Shorewall 4.5.11. Normally, if an unknown shell - variable is encountered in a configuration file (except in ?IF and - ?ELSIF directives), the compiler raises a fatal error. If - IGNOREUNKNOWNVARIABLES is set to Yes, then such variables simply expand to an - empty string. Default is No. + Added in Shorewall 4.5.12. This parameter names the arptables + executable to be used by Shorewall. If not specified or if specified + as a null value, then the arptables executable located using the + PATH option is used. + + Regardless of how the arptables utility is located (specified + via arptables= or located via PATH), Shorewall uses the + arptables-restore and arptables-save utilities from that same + directory. @@ -867,6 +869,21 @@ net all DROP infothen the chain name is 'net2all' + + IGNOREUNKNOWNVARIABLES=[Yes|No] + + + Added in Shorewall 4.5.11. Normally, if an unknown shell + variable is encountered in a configuration file (except in ?IF and + ?ELSIF directives), the compiler raises a fatal error. If + IGNOREUNKNOWNVARIABLES is set to Yes, then such variables simply expand to an + empty string. Default is No. + + + IMPLICIT_CONTINUE={Yes|No} @@ -1012,7 +1029,7 @@ net all DROP infothen the chain name is 'net2all' the iptables executable located using the PATH option is used. - Regardless of how the IPTABLES utility is located (specified + Regardless of how the iptables utility is located (specified via IPTABLES= or located via PATH), Shorewall uses the iptables-restore and iptables-save utilities from that same directory. @@ -2058,6 +2075,19 @@ net all DROP infothen the chain name is 'net2all' + + SAVE_ARPTABLES={Yes|No} + + + Added in Shorewall 4.5.12. If SAVE_ARPTABLES=Yes, then the + current arptables contents will be saved by shorewall save command and restored by + shorewall restore command. Default + value is No. + + + SAVE_IPSETS={Yes|No} diff --git a/docs/Manpages.xml b/docs/Manpages.xml index 00c5b836d..8acf20ca3 100644 --- a/docs/Manpages.xml +++ b/docs/Manpages.xml @@ -26,6 +26,12 @@ 2010 + 2011 + + 2012 + + 2013 + Thomas M. Eastep @@ -66,6 +72,9 @@ actions - Declare user-defined actions. + arprules + - (Added in Shorewall 4.5.12) Define arpfilter rules. + blacklist - Static blacklisting.