]> review.fuel-infra Code Review - puppet-modules/puppetlabs-firewall.git/commitdiff
added hashlimit-module for iptables & ip6tables with simple acceptance test
authorJonas Truestedt <jonas@atix.de>
Thu, 22 Jun 2017 08:00:36 +0000 (10:00 +0200)
committerJonas Truestedt <jonas@atix.de>
Thu, 22 Jun 2017 08:00:36 +0000 (10:00 +0200)
lib/puppet/provider/firewall/ip6tables.rb
lib/puppet/provider/firewall/iptables.rb
lib/puppet/type/firewall.rb
spec/acceptance/hashlimit_spec.rb [new file with mode: 0644]

index c8b3f64344c5b219c018647d8a8b2dd91eedba99..c4b421cd53d2b22f6684bc9431d2fcfffedf8f48 100644 (file)
@@ -70,93 +70,105 @@ Puppet::Type.type(:firewall).provide :ip6tables, :parent => :iptables, :source =
   @protocol = "IPv6"
 
   @resource_map = {
-    :burst              => "--limit-burst",
-    :checksum_fill      => "--checksum-fill",
-    :clamp_mss_to_pmtu  => "--clamp-mss-to-pmtu",
-    :connlimit_above    => "-m connlimit --connlimit-above",
-    :connlimit_mask     => "--connlimit-mask",
-    :connmark           => "-m connmark --mark",
-    :ctstate            => "-m conntrack --ctstate",
-    :destination        => "-d",
-    :dport              => ["-m multiport --dports", "--dport"],
-    :dst_range          => '--dst-range',
-    :dst_type           => "--dst-type",
-    :gateway            => "--gateway",
-    :gid                => "--gid-owner",
-    :hop_limit          => "-m hl --hl-eq",
-    :icmp               => "-m icmp6 --icmpv6-type",
-    :iniface            => "-i",
-    :ipsec_dir          => "-m policy --dir",
-    :ipsec_policy       => "--pol",
-    :ipset              => "-m set --match-set",
-    :isfirstfrag        => "-m frag --fragid 0 --fragfirst",
-    :ishasmorefrags     => "-m frag --fragid 0 --fragmore",
-    :islastfrag         => "-m frag --fragid 0 --fraglast",
-    :jump               => "-j",
-    :length             => "-m length --length",
-    :limit              => "-m limit --limit",
-    :log_level          => "--log-level",
-    :log_prefix         => "--log-prefix",
-    :log_uid            => "--log-uid",
-    :mask               => "--mask",
-    :match_mark         => "-m mark --mark",
-    :name               => "-m comment --comment",
-    :mac_source         => ["-m mac --mac-source", "--mac-source"],
-    :mss                => "-m tcpmss --mss",
-    :outiface           => "-o",
-    :pkttype            => "-m pkttype --pkt-type",
-    :port               => '-m multiport --ports',
-    :proto              => "-p",
-    :queue_num          => "--queue-num",
-    :queue_bypass       => "--queue-bypass",
-    :rdest              => "--rdest",
-    :reap               => "--reap",
-    :recent             => "-m recent",
-    :reject             => "--reject-with",
-    :rhitcount          => "--hitcount",
-    :rname              => "--name",
-    :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",
-    :source             => "-s",
-    :sport              => ["-m multiport --sports", "--sport"],
-    :src_range          => '--src-range',
-    :src_type           => "--src-type",
-    :stat_every         => '--every',
-    :stat_mode          => "-m statistic --mode",
-    :stat_packet        => '--packet',
-    :stat_probability   => '--probability',
-    :state              => "-m state --state",
-    :string             => "-m string --string",
-    :string_algo        => "--algo",
-    :string_from        => "--from",
-    :string_to          => "--to",
-    :table              => "-t",
-    :tcp_flags          => "-m tcp --tcp-flags",
-    :todest             => "--to-destination",
-    :toports            => "--to-ports",
-    :tosource           => "--to-source",
-    :uid                => "--uid-owner",
-    :physdev_in         => "--physdev-in",
-    :physdev_out        => "--physdev-out",
-    :physdev_is_bridged => "--physdev-is-bridged",
-    :physdev_is_in      => "--physdev-is-in",
-    :physdev_is_out     => "--physdev-is-out",
-    :date_start         => "--datestart",
-    :date_stop          => "--datestop",
-    :time_start         => "--timestart",
-    :time_stop          => "--timestop",
-    :month_days         => "--monthdays",
-    :week_days          => "--weekdays",
-    :time_contiguous    => "--contiguous",
-    :kernel_timezone    => "--kerneltz",
-    :src_cc             => "--source-country",
-    :dst_cc             => "--destination-country",
+    :burst                       => "--limit-burst",
+    :checksum_fill               => "--checksum-fill",
+    :clamp_mss_to_pmtu           => "--clamp-mss-to-pmtu",
+    :connlimit_above             => "-m connlimit --connlimit-above",
+    :connlimit_mask              => "--connlimit-mask",
+    :connmark                    => "-m connmark --mark",
+    :ctstate                     => "-m conntrack --ctstate",
+    :destination                 => "-d",
+    :dport                       => ["-m multiport --dports", "--dport"],
+    :dst_range                   => '--dst-range',
+    :dst_type                    => "--dst-type",
+    :gateway                     => "--gateway",
+    :gid                         => "--gid-owner",
+    :hop_limit                   => "-m hl --hl-eq",
+    :icmp                        => "-m icmp6 --icmpv6-type",
+    :iniface                     => "-i",
+    :ipsec_dir                   => "-m policy --dir",
+    :ipsec_policy                => "--pol",
+    :ipset                       => "-m set --match-set",
+    :isfirstfrag                 => "-m frag --fragid 0 --fragfirst",
+    :ishasmorefrags              => "-m frag --fragid 0 --fragmore",
+    :islastfrag                  => "-m frag --fragid 0 --fraglast",
+    :jump                        => "-j",
+    :length                      => "-m length --length",
+    :limit                       => "-m limit --limit",
+    :log_level                   => "--log-level",
+    :log_prefix                  => "--log-prefix",
+    :log_uid                     => "--log-uid",
+    :mask                        => "--mask",
+    :match_mark                  => "-m mark --mark",
+    :name                        => "-m comment --comment",
+    :mac_source                  => ["-m mac --mac-source", "--mac-source"],
+    :mss                         => "-m tcpmss --mss",
+    :outiface                    => "-o",
+    :pkttype                     => "-m pkttype --pkt-type",
+    :port                        => '-m multiport --ports',
+    :proto                       => "-p",
+    :queue_num                   => "--queue-num",
+    :queue_bypass                => "--queue-bypass",
+    :rdest                       => "--rdest",
+    :reap                        => "--reap",
+    :recent                      => "-m recent",
+    :reject                      => "--reject-with",
+    :rhitcount                   => "--hitcount",
+    :rname                       => "--name",
+    :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",
+    :source                      => "-s",
+    :sport                       => ["-m multiport --sports", "--sport"],
+    :src_range                   => '--src-range',
+    :src_type                    => "--src-type",
+    :stat_every                  => '--every',
+    :stat_mode                   => "-m statistic --mode",
+    :stat_packet                 => '--packet',
+    :stat_probability            => '--probability',
+    :state                       => "-m state --state",
+    :string                      => "-m string --string",
+    :string_algo                 => "--algo",
+    :string_from                 => "--from",
+    :string_to                   => "--to",
+    :table                       => "-t",
+    :tcp_flags                   => "-m tcp --tcp-flags",
+    :todest                      => "--to-destination",
+    :toports                     => "--to-ports",
+    :tosource                    => "--to-source",
+    :uid                         => "--uid-owner",
+    :physdev_in                  => "--physdev-in",
+    :physdev_out                 => "--physdev-out",
+    :physdev_is_bridged          => "--physdev-is-bridged",
+    :physdev_is_in               => "--physdev-is-in",
+    :physdev_is_out              => "--physdev-is-out",
+    :date_start                  => "--datestart",
+    :date_stop                   => "--datestop",
+    :time_start                  => "--timestart",
+    :time_stop                   => "--timestop",
+    :month_days                  => "--monthdays",
+    :week_days                   => "--weekdays",
+    :time_contiguous             => "--contiguous",
+    :kernel_timezone             => "--kerneltz",
+    :src_cc                      => "--source-country",
+    :dst_cc                      => "--destination-country",
+    :hashlimit_name              => "--hashlimit-name",
+    :hashlimit_upto              => "--hashlimit-upto",
+    :hashlimit_above             => "--hashlimit-above",
+    :hashlimit_burst             => "--hashlimit-burst",
+    :hashlimit_mode              => "--hashlimit-mode",
+    :hashlimit_srcmask           => "--hashlimit-srcmask",
+    :hashlimit_dstmask           => "--hashlimit-dstmask",
+    :hashlimit_htable_size       => "--hashlimit-htable-size",
+    :hashlimit_htable_max        => "--hashlimit-htable-max",
+    :hashlimit_htable_expire     => "--hashlimit-htable-expire",
+    :hashlimit_htable_gcinterval => "--hashlimit-htable-gcinterval",
+
   }
 
   # These are known booleans that do not take a value, but we want to munge
@@ -199,7 +211,10 @@ Puppet::Type.type(:firewall).provide :ip6tables, :parent => :iptables, :source =
     :iprange   => [:src_range, :dst_range],
     :owner     => [:uid, :gid],
     :time      => [:time_start, :time_stop, :month_days, :week_days, :date_start, :date_stop, :time_contiguous, :kernel_timezone],
-    :geoip     => [:src_cc, :dst_cc]
+    :geoip     => [:src_cc, :dst_cc],
+    :hashlimit => [:hashlimit_upto, :hashlimit_above, :hashlimit_name, :hashlimit_burst, :hashlimit_mode, :hashlimit_srcmask, :hashlimit_dstmask,
+                    :hashlimit_htable_size, :hashlimit_htable_max, :hashlimit_htable_expire, :hashlimit_htable_gcinterval],
+
   }
 
   # Create property methods dynamically
@@ -246,6 +261,8 @@ Puppet::Type.type(:firewall).provide :ip6tables, :parent => :iptables, :source =
     :string_from, :string_to, :jump, :clamp_mss_to_pmtu, :gateway, :todest,
     :tosource, :toports, :checksum_fill, :log_level, :log_prefix, :log_uid, :reject, :set_mss, :set_dscp, :set_dscp_class, :mss, :queue_num, :queue_bypass,
     :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,
-    :src_cc, :dst_cc, :name]
+    :src_cc, :dst_cc, :hashlimit_upto, :hashlimit_above, :hashlimit_name, :hashlimit_burst,
+    :hashlimit_mode, :hashlimit_srcmask, :hashlimit_dstmask, :hashlimit_htable_size,
+    :hashlimit_htable_max, :hashlimit_htable_expire, :hashlimit_htable_gcinterval, :name]
 
 end
index b05ba43fe3a220dd3d255ad6e488e891c59f88ea..1c847d7d73edb0b400316e1cb312c03df784ef99 100644 (file)
@@ -61,103 +61,114 @@ Puppet::Type.type(:firewall).provide :iptables, :parent => Puppet::Provider::Fir
   @protocol = "IPv4"
 
   @resource_map = {
-    :burst                 => "--limit-burst",
-    :checksum_fill         => "--checksum-fill",
-    :clamp_mss_to_pmtu     => "--clamp-mss-to-pmtu",
-    :connlimit_above       => "-m connlimit --connlimit-above",
-    :connlimit_mask        => "--connlimit-mask",
-    :connmark              => "-m connmark --mark",
-    :ctstate               => "-m conntrack --ctstate",
-    :destination           => "-d",
-    :dport                 => ["-m multiport --dports", "--dport"],
-    :dst_range             => "--dst-range",
-    :dst_type              => "--dst-type",
-    :gateway               => "--gateway",
-    :gid                   => "--gid-owner",
-    :icmp                  => "-m icmp --icmp-type",
-    :iniface               => "-i",
-    :ipsec_dir             => "-m policy --dir",
-    :ipsec_policy          => "--pol",
-    :ipset                 => "-m set --match-set",
-    :isfragment            => "-f",
-    :jump                  => "-j",
-    :goto                  => "-g",
-    :length                => "-m length --length",
-    :limit                 => "-m limit --limit",
-    :log_level             => "--log-level",
-    :log_prefix            => "--log-prefix",
-    :log_uid               => "--log-uid",
-    :mac_source            => ["-m mac --mac-source", "--mac-source"],
-    :mask                  => '--mask',
-    :match_mark            => "-m mark --mark",
-    :mss                   => '-m tcpmss --mss',
-    :name                  => "-m comment --comment",
-    :nflog_group           => "--nflog-group",
-    :nflog_prefix          => "--nflog-prefix",
-    :nflog_range           => "--nflog-range",
-    :nflog_threshold       => "--nflog-threshold",
-    :outiface              => "-o",
-    :pkttype               => "-m pkttype --pkt-type",
-    :port                  => '-m multiport --ports',
-    :proto                 => "-p",
-    :queue_num             => "--queue-num",
-    :queue_bypass          => "--queue-bypass",
-    :random                => "--random",
-    :rdest                 => "--rdest",
-    :reap                  => "--reap",
-    :recent                => "-m recent",
-    :reject                => "--reject-with",
-    :rhitcount             => "--hitcount",
-    :rname                 => "--name",
-    :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",
-    :source                => "-s",
-    :sport                 => ["-m multiport --sports", "--sport"],
-    :src_range             => "--src-range",
-    :src_type              => "--src-type",
-    :stat_every            => '--every',
-    :stat_mode             => "-m statistic --mode",
-    :stat_packet           => '--packet',
-    :stat_probability      => '--probability',
-    :state                 => "-m state --state",
-    :string                => "-m string --string",
-    :string_algo           => "--algo",
-    :string_from           => "--from",
-    :string_to             => "--to",
-    :table                 => "-t",
-    :tcp_flags             => "-m tcp --tcp-flags",
-    :todest                => "--to-destination",
-    :toports               => "--to-ports",
-    :tosource              => "--to-source",
-    :to                    => "--to",
-    :uid                   => "--uid-owner",
-    :physdev_in            => "--physdev-in",
-    :physdev_out           => "--physdev-out",
-    :physdev_is_bridged    => "--physdev-is-bridged",
-    :physdev_is_in         => "--physdev-is-in",
-    :physdev_is_out        => "--physdev-is-out",
-    :date_start            => "--datestart",
-    :date_stop             => "--datestop",
-    :time_start            => "--timestart",
-    :time_stop             => "--timestop",
-    :month_days            => "--monthdays",
-    :week_days             => "--weekdays",
-    :time_contiguous       => "--contiguous",
-    :kernel_timezone       => "--kerneltz",
-    :clusterip_new         => "--new",
-    :clusterip_hashmode    => "--hashmode",
-    :clusterip_clustermac  => "--clustermac",
-    :clusterip_total_nodes => "--total-nodes",
-    :clusterip_local_node  => "--local-node",
-    :clusterip_hash_init   => "--hash-init",
-    :src_cc                => "--source-country",
-    :dst_cc                => "--destination-country",
+    :burst                       => "--limit-burst",
+    :checksum_fill               => "--checksum-fill",
+    :clamp_mss_to_pmtu           => "--clamp-mss-to-pmtu",
+    :connlimit_above             => "-m connlimit --connlimit-above",
+    :connlimit_mask              => "--connlimit-mask",
+    :connmark                    => "-m connmark --mark",
+    :ctstate                     => "-m conntrack --ctstate",
+    :destination                 => "-d",
+    :dport                       => ["-m multiport --dports", "--dport"],
+    :dst_range                   => "--dst-range",
+    :dst_type                    => "--dst-type",
+    :gateway                     => "--gateway",
+    :gid                         => "--gid-owner",
+    :icmp                        => "-m icmp --icmp-type",
+    :iniface                     => "-i",
+    :ipsec_dir                   => "-m policy --dir",
+    :ipsec_policy                => "--pol",
+    :ipset                       => "-m set --match-set",
+    :isfragment                  => "-f",
+    :jump                        => "-j",
+    :goto                        => "-g",
+    :length                      => "-m length --length",
+    :limit                       => "-m limit --limit",
+    :log_level                   => "--log-level",
+    :log_prefix                  => "--log-prefix",
+    :log_uid                     => "--log-uid",
+    :mac_source                  => ["-m mac --mac-source", "--mac-source"],
+    :mask                        => '--mask',
+    :match_mark                  => "-m mark --mark",
+    :mss                         => '-m tcpmss --mss',
+    :name                        => "-m comment --comment",
+    :nflog_group                 => "--nflog-group",
+    :nflog_prefix                => "--nflog-prefix",
+    :nflog_range                 => "--nflog-range",
+    :nflog_threshold             => "--nflog-threshold",
+    :outiface                    => "-o",
+    :pkttype                     => "-m pkttype --pkt-type",
+    :port                        => '-m multiport --ports',
+    :proto                       => "-p",
+    :queue_num                   => "--queue-num",
+    :queue_bypass                => "--queue-bypass",
+    :random                      => "--random",
+    :rdest                       => "--rdest",
+    :reap                        => "--reap",
+    :recent                      => "-m recent",
+    :reject                      => "--reject-with",
+    :rhitcount                   => "--hitcount",
+    :rname                       => "--name",
+    :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",
+    :source                      => "-s",
+    :sport                       => ["-m multiport --sports", "--sport"],
+    :src_range                   => "--src-range",
+    :src_type                    => "--src-type",
+    :stat_every                  => '--every',
+    :stat_mode                   => "-m statistic --mode",
+    :stat_packet                 => '--packet',
+    :stat_probability            => '--probability',
+    :state                       => "-m state --state",
+    :string                      => "-m string --string",
+    :string_algo                 => "--algo",
+    :string_from                 => "--from",
+    :string_to                   => "--to",
+    :table                       => "-t",
+    :tcp_flags                   => "-m tcp --tcp-flags",
+    :todest                      => "--to-destination",
+    :toports                     => "--to-ports",
+    :tosource                    => "--to-source",
+    :to                          => "--to",
+    :uid                         => "--uid-owner",
+    :physdev_in                  => "--physdev-in",
+    :physdev_out                 => "--physdev-out",
+    :physdev_is_bridged          => "--physdev-is-bridged",
+    :physdev_is_in               => "--physdev-is-in",
+    :physdev_is_out              => "--physdev-is-out",
+    :date_start                  => "--datestart",
+    :date_stop                   => "--datestop",
+    :time_start                  => "--timestart",
+    :time_stop                   => "--timestop",
+    :month_days                  => "--monthdays",
+    :week_days                   => "--weekdays",
+    :time_contiguous             => "--contiguous",
+    :kernel_timezone             => "--kerneltz",
+    :clusterip_new               => "--new",
+    :clusterip_hashmode          => "--hashmode",
+    :clusterip_clustermac        => "--clustermac",
+    :clusterip_total_nodes       => "--total-nodes",
+    :clusterip_local_node        => "--local-node",
+    :clusterip_hash_init         => "--hash-init",
+    :src_cc                      => "--source-country",
+    :dst_cc                      => "--destination-country",
+    :hashlimit_name              => "--hashlimit-name",
+    :hashlimit_upto              => "--hashlimit-upto",
+    :hashlimit_above             => "--hashlimit-above",
+    :hashlimit_burst             => "--hashlimit-burst",
+    :hashlimit_mode              => "--hashlimit-mode",
+    :hashlimit_srcmask           => "--hashlimit-srcmask",
+    :hashlimit_dstmask           => "--hashlimit-dstmask",
+    :hashlimit_htable_size       => "--hashlimit-htable-size",
+    :hashlimit_htable_max        => "--hashlimit-htable-max",
+    :hashlimit_htable_expire     => "--hashlimit-htable-expire",
+    :hashlimit_htable_gcinterval => "--hashlimit-htable-gcinterval",
   }
 
   # These are known booleans that do not take a value, but we want to munge
@@ -200,7 +211,9 @@ Puppet::Type.type(:firewall).provide :iptables, :parent => Puppet::Provider::Fir
     :iprange   => [:src_range, :dst_range],
     :owner     => [:uid, :gid],
     :time      => [:time_start, :time_stop, :month_days, :week_days, :date_start, :date_stop, :time_contiguous, :kernel_timezone],
-    :geoip     => [:src_cc, :dst_cc]
+    :geoip     => [:src_cc, :dst_cc],
+    :hashlimit => [:hashlimit_upto, :hashlimit_above, :hashlimit_name, :hashlimit_burst, :hashlimit_mode, :hashlimit_srcmask, :hashlimit_dstmask,
+                    :hashlimit_htable_size, :hashlimit_htable_max, :hashlimit_htable_expire, :hashlimit_htable_gcinterval],
   }
 
   def self.munge_resource_map_from_existing_values(resource_map_original, compare)
@@ -291,7 +304,9 @@ Puppet::Type.type(:firewall).provide :iptables, :parent => Puppet::Provider::Fir
     :set_mss, :set_dscp, :set_dscp_class, :todest, :tosource, :toports, :to, :checksum_fill, :random, :log_prefix,
     :log_level, :log_uid, :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,
-    :src_cc, :dst_cc, :name]
+    :src_cc, :dst_cc, :hashlimit_upto, :hashlimit_above, :hashlimit_name, :hashlimit_burst,
+    :hashlimit_mode, :hashlimit_srcmask, :hashlimit_dstmask, :hashlimit_htable_size,
+    :hashlimit_htable_max, :hashlimit_htable_expire, :hashlimit_htable_gcinterval, :name]
 
   def insert
     debug 'Inserting rule %s' % resource[:name]
index 6deab2b3ca5bd7203101a931535eb548895f0d02..981fdb90aa6d6993f2d99c5178a55adb9774c260 100644 (file)
@@ -67,6 +67,7 @@ Puppet::Type.newtype(:firewall) do
   feature :string_matching, "String matching features"
   feature :queue_num, "Which NFQUEUE to send packets to"
   feature :queue_bypass, "If nothing is listening on queue_num, allow packets to bypass the queue"
+  feature :hashlimit, "Hashlimit features"
 
   # provider specific features
   feature :iptables, "The provider provides iptables features."
@@ -1589,6 +1590,79 @@ Puppet::Type.newtype(:firewall) do
     newvalues(/^[A-Z]{2}(,[A-Z]{2})*$/)
   end
 
+  newproperty(:hashlimit_name) do
+    desc <<-EOS
+      The name for the /proc/net/ipt_hashlimit/foo entry.
+      This parameter is required.
+    EOS
+  end
+
+  newproperty(:hashlimit_upto) do
+    desc <<-EOS
+      Match if the rate is below or equal to amount/quantum. It is specified either as a number, with an optional time quantum suffix (the default is 3/hour), or as amountb/second (number of bytes per second).
+      This parameter or hashlimit_above is required.
+      Allowed forms are '40','40/second','40/minute','40/hour','40/day'.
+    EOS
+  end
+
+  newproperty(:hashlimit_above) do
+    desc <<-EOS
+      Match if the rate is above amount/quantum.
+      This parameter or hashlimit_upto is required.
+      Allowed forms are '40','40/second','40/minute','40/hour','40/day'.
+    EOS
+  end
+
+  newproperty(:hashlimit_burst) do
+    desc <<-EOS
+      Maximum initial number of packets to match: this number gets recharged by one every time the limit specified above is not reached, up to this number; the default is 5. When byte-based rate matching is requested, this option specifies the amount of bytes that can exceed the given rate. This option should be used with caution -- if the entry expires, the burst value is reset too.
+    EOS
+    newvalue(/^\d+$/)
+  end
+
+  newproperty(:hashlimit_mode) do
+    desc <<-EOS
+      A comma-separated list of objects to take into consideration. If no --hashlimit-mode option is given, hashlimit acts like limit, but at the expensive of doing the hash housekeeping.
+      Allowed values are: srcip, srcport, dstip, dstport
+    EOS
+  end
+
+  newproperty(:hashlimit_srcmask) do
+    desc <<-EOS
+      When --hashlimit-mode srcip is used, all source addresses encountered will be grouped according to the given prefix length and the so-created subnet will be subject to hashlimit. prefix must be between (inclusive) 0 and 32. Note that --hashlimit-srcmask 0 is basically doing the same thing as not specifying srcip for --hashlimit-mode, but is technically more expensive.
+    EOS
+  end
+
+  newproperty(:hashlimit_dstmask) do
+    desc <<-EOS
+      Like --hashlimit-srcmask, but for destination addresses.
+    EOS
+  end
+
+  newproperty(:hashlimit_htable_size) do
+    desc <<-EOS
+      The number of buckets of the hash table
+    EOS
+  end
+
+  newproperty(:hashlimit_htable_max) do
+    desc <<-EOS
+      Maximum entries in the hash.
+    EOS
+  end
+
+  newproperty(:hashlimit_htable_expire) do
+    desc <<-EOS
+      After how many milliseconds do hash entries expire.
+    EOS
+  end
+
+  newproperty(:hashlimit_htable_gcinterval) do
+    desc <<-EOS
+      How many milliseconds between garbage collection intervals.
+    EOS
+  end
+
   autorequire(:firewallchain) do
     reqs = []
     protocol = nil
@@ -1794,5 +1868,10 @@ Puppet::Type.newtype(:firewall) do
       end
     end
 
+    if value(:hashlimit_name)
+      unless value(:hashlimit_upto) || value(:hashlimit_above)
+        self.fail "Either hashlimit_upto or hashlimit_above are required"
+      end
+    end
   end
 end
diff --git a/spec/acceptance/hashlimit_spec.rb b/spec/acceptance/hashlimit_spec.rb
new file mode 100644 (file)
index 0000000..303dd81
--- /dev/null
@@ -0,0 +1,121 @@
+
+require 'spec_helper_acceptance'
+
+describe 'hashlimit property' do
+  before :all do
+    iptables_flush_all_tables
+    ip6tables_flush_all_tables
+  end
+
+  describe 'hashlimit_tests' do
+    context 'hashlimit_above' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '800 - hashlimit_above test':
+            chain                       => 'INPUT',
+            proto                       => 'tcp',
+            hashlimit_name              => 'above',
+            hashlimit_above             => '512kb/s',
+            hashlimit_htable_gcinterval => '10',
+            hashlimit_mode              => 'srcip,dstip',
+            action                      => accept,
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+        apply_manifest(pp, :catch_changes => do_catch_changes)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables-save') do |r|
+          expect(r.stdout).to match(/-A INPUT -p tcp -m hashlimit --hashlimit-above 512kb\/s --hashlimit-mode srcip,dstip --hashlimit-name above --hashlimit-htable-gcinterval 10 -m comment --comment "800 - hashlimit_above test" -j ACCEPT/)
+        end
+      end
+    end
+
+    context 'hashlimit_above_ip6' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '801 - hashlimit_above test ipv6':
+            chain                       => 'INPUT',
+            provider                    => 'ip6tables',
+            proto                       => 'tcp',
+            hashlimit_name              => 'above-ip6',
+            hashlimit_above             => '512kb/s',
+            hashlimit_htable_gcinterval => '10',
+            hashlimit_mode              => 'srcip,dstip',
+            action                      => accept,
+          }  
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+        apply_manifest(pp, :catch_changes => do_catch_changes)
+      end
+
+      it 'should contain the rule' do
+        shell('ip6tables-save') do |r|
+          expect(r.stdout).to match(/-A INPUT -p tcp -m hashlimit --hashlimit-above 512kb\/s --hashlimit-mode srcip,dstip --hashlimit-name above-ip6 --hashlimit-htable-gcinterval 10 -m comment --comment "801 - hashlimit_above test ipv6" -j ACCEPT/)
+        end
+      end
+    end
+
+    context 'hashlimit_upto' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '802 - hashlimit_upto test':
+            chain                   => 'INPUT',
+            hashlimit_name          => 'upto',
+            hashlimit_upto          => '16/sec',
+            hashlimit_burst         => '640',
+            hashlimit_htable_size   => '1310000',
+            hashlimit_htable_max    => '320000',
+            hashlimit_htable_expire => '36000000',
+            action                  => accept,
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+        apply_manifest(pp, :catch_changes => do_catch_changes)
+      end
+
+      it 'should contain the rule' do
+        shell('iptables-save') do |r|
+          expect(r.stdout).to match(/-A INPUT -p tcp -m hashlimit --hashlimit-upto 16\/sec --hashlimit-burst 640 --hashlimit-name upto --hashlimit-htable-size 1310000 --hashlimit-htable-max 320000 --hashlimit-htable-expire 36000000 -m comment --comment "802 - hashlimit_upto test" -j ACCEPT/)
+        end
+      end
+    end
+
+    context 'hashlimit_upto_ip6' do
+      it 'applies' do
+        pp = <<-EOS
+          class { '::firewall': }
+          firewall { '803 - hashlimit_upto test ip6':
+            chain                   => 'INPUT',
+            provider                => 'ip6tables',
+            hashlimit_name          => 'upto-ip6',
+            hashlimit_upto          => '16/sec',
+            hashlimit_burst         => '640',
+            hashlimit_htable_size   => '1310000',
+            hashlimit_htable_max    => '320000',
+            hashlimit_htable_expire => '36000000',
+            action                  => accept,
+          }
+        EOS
+
+        apply_manifest(pp, :catch_failures => true)
+        apply_manifest(pp, :catch_changes => do_catch_changes)
+      end
+
+      it 'should contain the rule' do
+        shell('ip6tables-save') do |r|
+          expect(r.stdout).to match(/-A INPUT -p tcp -m hashlimit --hashlimit-upto 16\/sec --hashlimit-burst 640 --hashlimit-name upto-ip6 --hashlimit-htable-size 1310000 --hashlimit-htable-max 320000 --hashlimit-htable-expire 36000000 -m comment --comment "803 - hashlimit_upto test ip6" -j ACCEPT/)
+        end
+      end
+    end
+
+  end
+
+end