From 932d0d900644f285966ca405bdeb6f5012ed040e Mon Sep 17 00:00:00 2001 From: Eric Stonfer Date: Wed, 15 Jul 2015 13:16:06 -0400 Subject: [PATCH] add set_dscp and set_dscp_class --- README.markdown | 6 +- lib/puppet/provider/firewall/ip6tables.rb | 4 +- lib/puppet/provider/firewall/iptables.rb | 36 ++++++- lib/puppet/type/firewall.rb | 30 ++++++ spec/acceptance/firewall_dscp_spec.rb | 118 +++++++++++++++++++++ spec/fixtures/iptables/conversion_hash.rb | 11 ++ spec/unit/puppet/provider/iptables_spec.rb | 1 - 7 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 spec/acceptance/firewall_dscp_spec.rb diff --git a/README.markdown b/README.markdown index 40fe873..c770f3c 100644 --- a/README.markdown +++ b/README.markdown @@ -574,7 +574,7 @@ If Puppet is managing the iptables or iptables-persistent packages, and the prov * `islastfrag`: If true, matches when the packet is the last fragment of a fragmented ipv6 packet. Supported by ipv6 only. Valid values are 'true', 'false'. Requires the `islastfrag`. -* `jump`: The value for the iptables `--jump` parameter. Any valid chain name is allowed, but normal values are: 'QUEUE', 'RETURN', 'DNAT', 'SNAT', 'LOG', 'MASQUERADE', 'REDIRECT', 'MARK', 'TCPMSS'. +* `jump`: The value for the iptables `--jump` parameter. Any valid chain name is allowed, but normal values are: 'QUEUE', 'RETURN', 'DNAT', 'SNAT', 'LOG', 'MASQUERADE', 'REDIRECT', 'MARK', 'TCPMSS', 'DSCP'. For the values 'ACCEPT', 'DROP', and 'REJECT', you must use the generic `action` parameter. This is to enforce the use of generic parameters where possible for maximum cross-platform modeling. @@ -687,6 +687,10 @@ firewall { '101 blacklist strange traffic': * `rttl`: May only be used in conjunction with `recent => 'rcheck'` or `recent => 'update'`. If boolean 'true', this will narrow the match to happen only when the address is in the list and the TTL of the current packet matches that of the packet that hit the `recent => 'set'` rule. If you have problems with DoS attacks via bogus packets from fake source addresses, this parameter may help. Valid values are 'true', 'false'. Requires the `recent_limiting` feature and the `recent` parameter. +* `set_dscp`: When combined with `jump => 'DSCP'` specifies the dscp marking associated with the packet. + +* `set_dscp_class`: When combined with `jump => 'DSCP'` specifies the class associated with the packet (valid values found here: http://www.cisco.com/c/en/us/support/docs/quality-of-service-qos/qos-packet-marking/10103-dscpvalues.html#packetclassification). + * `set_mark`: Set the Netfilter mark value associated with the packet. Accepts either 'mark/mask' or 'mark'. These will be converted to hex if they are not already. Requires the `mark` feature. * `set_mss`: When combined with `jump => 'TCPMSS'` specifies the value of the MSS field. diff --git a/lib/puppet/provider/firewall/ip6tables.rb b/lib/puppet/provider/firewall/ip6tables.rb index 2a05dcf..585f855 100644 --- a/lib/puppet/provider/firewall/ip6tables.rb +++ b/lib/puppet/provider/firewall/ip6tables.rb @@ -109,6 +109,8 @@ Puppet::Type.type(:firewall).provide :ip6tables, :parent => :iptables, :source = :rseconds => "--seconds", :rsource => "--rsource", :rttl => "--rttl", + :set_dscp => '--set-dscp', + :set_dscp_class => '--set-dscp-class', :set_mark => mark_flag, :set_mss => '--set-mss', :socket => "-m socket", @@ -218,7 +220,7 @@ Puppet::Type.type(:firewall).provide :ip6tables, :parent => :iptables, :source = :dst_type, :socket, :pkttype, :name, :ipsec_dir, :ipsec_policy, :state, :ctstate, :icmp, :hop_limit, :limit, :burst, :recent, :rseconds, :reap, :rhitcount, :rttl, :rname, :mask, :rsource, :rdest, :ipset, :jump, :clamp_mss_to_pmtu, :gateway, :todest, - :tosource, :toports, :checksum_fill, :log_level, :log_prefix, :reject, :set_mss, :mss, + :tosource, :toports, :checksum_fill, :log_level, :log_prefix, :reject, :set_mss, :set_dscp, :set_dscp_class, :mss, :set_mark, :match_mark, :connlimit_above, :connlimit_mask, :connmark, :time_start, :time_stop, :month_days, :week_days, :date_start, :date_stop, :time_contiguous, :kernel_timezone] end diff --git a/lib/puppet/provider/firewall/iptables.rb b/lib/puppet/provider/firewall/iptables.rb index 3c1bec4..52a7a2d 100644 --- a/lib/puppet/provider/firewall/iptables.rb +++ b/lib/puppet/provider/firewall/iptables.rb @@ -94,6 +94,8 @@ Puppet::Type.type(:firewall).provide :iptables, :parent => Puppet::Provider::Fir :rseconds => "--seconds", :rsource => "--rsource", :rttl => "--rttl", + :set_dscp => '--set-dscp', + :set_dscp_class => '--set-dscp-class', :set_mark => mark_flag, :set_mss => '--set-mss', :socket => "-m socket", @@ -252,7 +254,7 @@ Puppet::Type.type(:firewall).provide :iptables, :parent => Puppet::Provider::Fir :state, :ctstate, :icmp, :limit, :burst, :recent, :rseconds, :reap, :rhitcount, :rttl, :rname, :mask, :rsource, :rdest, :ipset, :jump, :clusterip_new, :clusterip_hashmode, :clusterip_clustermac, :clusterip_total_nodes, :clusterip_local_node, :clusterip_hash_init, - :clamp_mss_to_pmtu, :gateway, :set_mss, :todest, :tosource, :toports, :to, :checksum_fill, :random, :log_prefix, + :clamp_mss_to_pmtu, :gateway, :set_mss, :set_dscp, :set_dscp_class, :todest, :tosource, :toports, :to, :checksum_fill, :random, :log_prefix, :log_level, :reject, :set_mark, :match_mark, :mss, :connlimit_above, :connlimit_mask, :connmark, :time_start, :time_stop, :month_days, :week_days, :date_start, :date_stop, :time_contiguous, :kernel_timezone ] @@ -412,6 +414,37 @@ Puppet::Type.type(:firewall).provide :iptables, :parent => Puppet::Provider::Fir [:dport, :sport, :port, :state, :ctstate].each do |prop| hash[prop] = hash[prop].split(',') if ! hash[prop].nil? end + + ## clean up DSCP class to HEX mappings + valid_dscp_classes = { + '0x0a' => 'af11', + '0x0c' => 'af12', + '0x0e' => 'af13', + '0x12' => 'af21', + '0x14' => 'af22', + '0x16' => 'af23', + '0x1a' => 'af31', + '0x1c' => 'af32', + '0x1e' => 'af33', + '0x22' => 'af41', + '0x24' => 'af42', + '0x26' => 'af43', + '0x08' => 'cs1', + '0x10' => 'cs2', + '0x18' => 'cs3', + '0x20' => 'cs4', + '0x28' => 'cs5', + '0x30' => 'cs6', + '0x38' => 'cs7', + '0x2e' => 'ef' + } + [:set_dscp_class].each do |prop| + [:set_dscp].each do |dmark| + next unless hash[dmark] + hash[prop] = valid_dscp_classes[hash[dmark]] + end + end + # Convert booleans removing the previous cludge we did @known_booleans.each do |bool| @@ -515,7 +548,6 @@ Puppet::Type.type(:firewall).provide :iptables, :parent => Puppet::Provider::Fir hash[:action] = hash[:jump].downcase hash.delete(:jump) end - hash end diff --git a/lib/puppet/type/firewall.rb b/lib/puppet/type/firewall.rb index 35c699b..171c2e6 100644 --- a/lib/puppet/type/firewall.rb +++ b/lib/puppet/type/firewall.rb @@ -873,6 +873,30 @@ Puppet::Type.newtype(:firewall) do newvalues(:true, :false) end + newproperty(:set_dscp, :required_features => :iptables) do + desc <<-EOS + Set DSCP Markings. + EOS + end + + newproperty(:set_dscp_class, :required_features => :iptables) do + desc <<-EOS + This sets the DSCP field according to a predefined DiffServ class. + EOS + # iptables uses the cisco DSCP classes as the basis for this flag. Values may be found here: + # 'http://www.cisco.com/c/en/us/support/docs/quality-of-service-qos/qos-packet-marking/10103-dscpvalues.html' + valid_codes = [ + 'af11','af12','af13','af21','af22','af23','af31','af32','af33','af41', + 'af42','af43','cs1','cs2','cs3','cs4','cs5','cs6','cs7','ef' + ] + munge do |value| + unless valid_codes.include? value.downcase + raise ArgumentError, "#{value} is not a valid DSCP Class" + end + value.downcase + end + end + newproperty(:set_mss, :required_features => :iptables) do desc <<-EOS Sets the TCP MSS value for packets. @@ -1414,6 +1438,12 @@ Puppet::Type.newtype(:firewall) do end end + if value(:jump).to_s == "DSCP" + unless value(:set_dscp) || value(:set_dscp_class) + self.fail "When using jump => DSCP, the set_dscp or set_dscp_class property is required" + end + end + if value(:jump).to_s == "TCPMSS" unless value(:set_mss) || value(:clamp_mss_to_pmtu) self.fail "When using jump => TCPMSS, the set_mss or clamp_mss_to_pmtu property is required" diff --git a/spec/acceptance/firewall_dscp_spec.rb b/spec/acceptance/firewall_dscp_spec.rb new file mode 100644 index 0000000..bbb68ef --- /dev/null +++ b/spec/acceptance/firewall_dscp_spec.rb @@ -0,0 +1,118 @@ +require 'spec_helper_acceptance' + +describe 'firewall type', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do + + before(:all) do + shell('iptables --flush; iptables -t nat --flush; iptables -t mangle --flush') + shell('ip6tables --flush; ip6tables -t nat --flush; ip6tables -t mangle --flush') + end + + describe 'dscp ipv4 tests' do + context 'set_dscp 0x01' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { + '1000 - set_dscp': + proto => 'tcp', + jump => 'DSCP', + set_dscp => '0x01', + port => '997', + chain => 'OUTPUT', + table => 'mangle', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save -t mangle') do |r| + expect(r.stdout).to match(/-A OUTPUT -p tcp -m multiport --ports 997 -m comment --comment "1000 - set_dscp" -j DSCP --set-dscp 0x01/) + end + end + end + + context 'set_dscp_class EF' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { + '1001 EF - set_dscp_class': + proto => 'tcp', + jump => 'DSCP', + port => '997', + set_dscp_class => 'EF', + chain => 'OUTPUT', + table => 'mangle', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A OUTPUT -p tcp -m multiport --ports 997 -m comment --comment "1001 EF - set_dscp_class" -j DSCP --set-dscp 0x2e/) + end + end + end + end + + if default['platform'] !~ /el-5/ + describe 'dscp ipv6 tests' do + context 'set_dscp 0x01' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { + '1002 - set_dscp': + proto => 'tcp', + jump => 'DSCP', + set_dscp => '0x01', + port => '997', + chain => 'OUTPUT', + table => 'mangle', + provider => 'ip6tables', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('ip6tables-save -t mangle') do |r| + expect(r.stdout).to match(/-A OUTPUT -p tcp -m multiport --ports 997 -m comment --comment "1002 - set_dscp" -j DSCP --set-dscp 0x01/) + end + end + end + + context 'set_dscp_class EF' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { + '1003 EF - set_dscp_class': + proto => 'tcp', + jump => 'DSCP', + port => '997', + set_dscp_class => 'EF', + chain => 'OUTPUT', + table => 'mangle', + provider => 'ip6tables', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('ip6tables-save') do |r| + expect(r.stdout).to match(/-A OUTPUT -p tcp -m multiport --ports 997 -m comment --comment "1003 EF - set_dscp_class" -j DSCP --set-dscp 0x2e/) + end + end + end + end + end + +end diff --git a/spec/fixtures/iptables/conversion_hash.rb b/spec/fixtures/iptables/conversion_hash.rb index 28858eb..d6db278 100644 --- a/spec/fixtures/iptables/conversion_hash.rb +++ b/spec/fixtures/iptables/conversion_hash.rb @@ -1106,4 +1106,15 @@ HASH_TO_ARGS = { }, :args => ["-t", :filter, "-p", :tcp, "-m", "tcp", "--tcp-flags", "SYN,RST", "SYN", "-m", "comment", "--comment", "067 change max segment size", "-j", "TCPMSS", "--clamp-mss-to-pmtu"], }, + 'set_dscp_class' => { + :params => { + :name => '068 set dscp class to EF', + :table => 'mangle', + :proto => 'tcp', + :port => '997', + :jump => 'DSCP', + :set_dscp_class => 'ef', + }, + :args => ["-t", :mangle, "-p", :tcp, "-m", "multiport", '--ports', '997', "-m", "comment", "--comment", "068 set dscp class to EF", "-j", "DSCP", "--set-dscp-class", "ef"], + }, } diff --git a/spec/unit/puppet/provider/iptables_spec.rb b/spec/unit/puppet/provider/iptables_spec.rb index e73bf84..0557071 100644 --- a/spec/unit/puppet/provider/iptables_spec.rb +++ b/spec/unit/puppet/provider/iptables_spec.rb @@ -215,7 +215,6 @@ describe 'iptables provider' do ARGS_TO_HASH.each do |test_name,data| describe "for test data '#{test_name}'" do let(:resource) { provider.rule_to_hash(data[:line], data[:table], 0) } - # If this option is enabled, make sure the parameters exactly match if data[:compare_all] then it "the parameter hash keys should be the same as returned by rules_to_hash" do -- 2.45.2