From d7b03753f10d93d278091d39b95adf3b18a2770c Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Tue, 29 Jun 2010 02:24:02 +0200
Subject: [PATCH] core: implement packet rate limiting and enable it by default

---
 avahi-core/core.h              |  3 +++
 avahi-core/iface.c             | 24 ++++++++++++++++++++
 avahi-core/iface.h             |  4 ++++
 avahi-core/internal.h          |  1 +
 avahi-core/server.c            |  2 ++
 avahi-daemon/avahi-daemon.conf |  2 ++
 avahi-daemon/main.c            | 40 ++++++++++++++++++++++++++++++++++
 man/avahi-daemon.conf.5.xml.in | 22 ++++++++++++++++---
 8 files changed, 95 insertions(+), 3 deletions(-)

diff --git a/avahi-core/core.h b/avahi-core/core.h
index 1f50126..f50c612 100644
--- a/avahi-core/core.h
+++ b/avahi-core/core.h
@@ -29,6 +29,7 @@ typedef struct AvahiServer AvahiServer;
 #include <avahi-common/address.h>
 #include <avahi-common/defs.h>
 #include <avahi-common/watch.h>
+#include <avahi-common/timeval.h>
 #include <avahi-core/rr.h>
 
 AVAHI_C_DECL_BEGIN
@@ -66,6 +67,8 @@ typedef struct AvahiServerConfig {
     int publish_a_on_ipv6;            /**< Publish an IPv4 A RR on IPv6 sockets */
     int publish_aaaa_on_ipv4;         /**< Publish an IPv4 A RR on IPv6 sockets */
     unsigned n_cache_entries_max;     /**< Maximum number of cache entries per interface */
+    AvahiUsec ratelimit_interval;     /**< If non-zero, rate-limiting interval parameter. */
+    unsigned ratelimit_burst;         /**< If ratelimit_interval is non-zero, rate-limiting burst parameter. */
 } AvahiServerConfig;
 
 /** Allocate a new mDNS responder object. */
diff --git a/avahi-core/iface.c b/avahi-core/iface.c
index ad83fe4..39a860a 100644
--- a/avahi-core/iface.c
+++ b/avahi-core/iface.c
@@ -390,6 +390,9 @@ AvahiHwInterface *avahi_hw_interface_new(AvahiInterfaceMonitor *m, AvahiIfIndex
     hw->index = idx;
     hw->mac_address_size = 0;
     hw->entry_group = NULL;
+    hw->ratelimit_begin.tv_sec = 0;
+    hw->ratelimit_begin.tv_usec = 0;
+    hw->ratelimit_counter = 0;
 
     AVAHI_LLIST_HEAD_INIT(AvahiInterface, hw->interfaces);
     AVAHI_LLIST_PREPEND(AvahiHwInterface, hardware, m->hw_interfaces, hw);
@@ -571,6 +574,27 @@ void avahi_interface_send_packet_unicast(AvahiInterface *i, AvahiDnsPacket *p, c
 
     assert(!a || a->proto == i->protocol);
 
+    if (i->monitor->server->config.ratelimit_interval > 0) {
+        struct timeval now, end;
+
+        gettimeofday(&now, NULL);
+
+        end = i->hardware->ratelimit_begin;
+        avahi_timeval_add(&end, i->monitor->server->config.ratelimit_interval);
+
+        if (i->hardware->ratelimit_begin.tv_sec <= 0 ||
+            avahi_timeval_compare(&end, &now) < 0) {
+
+            i->hardware->ratelimit_begin = now;
+            i->hardware->ratelimit_counter = 0;
+        }
+
+        if (i->hardware->ratelimit_counter > i->monitor->server->config.ratelimit_burst)
+            return;
+
+        i->hardware->ratelimit_counter++;
+    }
+
     if (i->protocol == AVAHI_PROTO_INET && i->monitor->server->fd_ipv4 >= 0)
         avahi_send_dns_packet_ipv4(i->monitor->server->fd_ipv4, i->hardware->index, p, i->mcast_joined ? &i->local_mcast_address.data.ipv4 : NULL, a ? &a->data.ipv4 : NULL, port);
     else if (i->protocol == AVAHI_PROTO_INET6 && i->monitor->server->fd_ipv6 >= 0)
diff --git a/avahi-core/iface.h b/avahi-core/iface.h
index b38578c..c3f24af 100644
--- a/avahi-core/iface.h
+++ b/avahi-core/iface.h
@@ -85,6 +85,10 @@ struct AvahiHwInterface {
 
     AvahiSEntryGroup *entry_group;
 
+    /* Packet rate limiting */
+    struct timeval ratelimit_begin;
+    unsigned ratelimit_counter;
+
     AVAHI_LLIST_HEAD(AvahiInterface, interfaces);
 };
 
diff --git a/avahi-core/internal.h b/avahi-core/internal.h
index eb7f146..b8f9302 100644
--- a/avahi-core/internal.h
+++ b/avahi-core/internal.h
@@ -25,6 +25,7 @@ typedef struct AvahiEntry AvahiEntry;
 
 #include <avahi-common/llist.h>
 #include <avahi-common/watch.h>
+#include <avahi-common/timeval.h>
 
 #include "core.h"
 #include "iface.h"
diff --git a/avahi-core/server.c b/avahi-core/server.c
index d7fd71b..2b8f95f 100644
--- a/avahi-core/server.c
+++ b/avahi-core/server.c
@@ -1591,6 +1591,8 @@ AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
     c->publish_aaaa_on_ipv4 = 1;
     c->publish_a_on_ipv6 = 0;
     c->n_cache_entries_max = AVAHI_DEFAULT_CACHE_ENTRIES_MAX;
+    c->ratelimit_interval = 0;
+    c->ratelimit_burst = 0;
 
     return c;
 }
diff --git a/avahi-daemon/avahi-daemon.conf b/avahi-daemon/avahi-daemon.conf
index 898cee9..c992842 100644
--- a/avahi-daemon/avahi-daemon.conf
+++ b/avahi-daemon/avahi-daemon.conf
@@ -35,6 +35,8 @@ use-ipv6=no
 #clients-max=4096
 #objects-per-client-max=1024
 #entries-per-entry-group-max=32
+ratelimit-interval-usec=1000000
+ratelimit-burst=1000
 
 [wide-area]
 enable-wide-area=yes
diff --git a/avahi-daemon/main.c b/avahi-daemon/main.c
index ca1a854..8bd856e 100644
--- a/avahi-daemon/main.c
+++ b/avahi-daemon/main.c
@@ -551,6 +551,26 @@ static int parse_unsigned(const char *s, unsigned *u) {
     return 0;
 }
 
+static int parse_usec(const char *s, AvahiUsec *u) {
+    char *e = NULL;
+    unsigned long long ull;
+    AvahiUsec k;
+
+    errno = 0;
+    ull = strtoull(s, &e, 0);
+
+    if (!e || *e || errno != 0)
+        return -1;
+
+    k = (AvahiUsec) ull;
+
+    if ((unsigned long long) k != ull)
+        return -1;
+
+    *u = k;
+    return 0;
+}
+
 static int load_config_file(DaemonConfig *c) {
     int r = -1;
     AvahiIniFile *f;
@@ -642,6 +662,26 @@ static int load_config_file(DaemonConfig *c) {
                         c->server_config.deny_interfaces = avahi_string_list_add(c->server_config.deny_interfaces, *t);
 
                     avahi_strfreev(e);
+                } else if (strcasecmp(p->key, "ratelimit-interval-usec") == 0) {
+                    AvahiUsec k;
+
+                    if (parse_usec(p->value, &k) < 0) {
+                        avahi_log_error("Invalid ratelimit-interval-usec setting %s", p->value);
+                        goto finish;
+                    }
+
+                    c->server_config.ratelimit_interval = k;
+
+                } else if (strcasecmp(p->key, "ratelimit-burst") == 0) {
+                    unsigned k;
+
+                    if (parse_unsigned(p->value, &k) < 0) {
+                        avahi_log_error("Invalid ratelimit-burst setting %s", p->value);
+                        goto finish;
+                    }
+
+                    c->server_config.ratelimit_burst = k;
+
                 } else if (strcasecmp(p->key, "cache-entries-max") == 0) {
                     unsigned k;
 
diff --git a/man/avahi-daemon.conf.5.xml.in b/man/avahi-daemon.conf.5.xml.in
index 62f3a9d..487645b 100644
--- a/man/avahi-daemon.conf.5.xml.in
+++ b/man/avahi-daemon.conf.5.xml.in
@@ -159,7 +159,7 @@
     <option>
       <p><opt>objects-per-client-max=</opt> Takes an unsigned
       integer. The maximum number of objects (entry groups, browsers,
-      resolvers) that may be registered per client at a time. If the
+      resolvers) that may be registered per D-Bus client at a time. If the
       maximum number is reached further object creation will be
       refused until at least one object is freed.</p>
     </option>
@@ -167,10 +167,26 @@
     <option>
       <p><opt>entries-per-entry-group-max=</opt> Takes an unsigned
       integer. The maximum number of entries (resource records) per
-      entry group at a time. If the maximum number is reached further
-      resource records may not be added to an entry group.</p>
+      entry group registered by a D-Bus client at a time. If the
+      maximum number is reached further resource records may not be
+      added to an entry group.</p>
     </option>
 
+    <option>
+      <p><opt>ratelimit-interval-usec=</opt> Takes an unsigned
+      integer. Sets the per-interface packet rate-limiting interval
+      parameter. Together with <opt>ratelimit-burst=</opt> this may be
+      used to control the maximum number of packets Avahi will
+      generated in a specific period of time on an interface.</p>
+    </option>
+
+    <option>
+      <p><opt>ratelimit-burst=</opt> Takes an unsigned
+      integer. Sets the per-interface packet rate-limiting burst
+      parameter. Together with <opt>ratelimit-interval-usec=</opt> this may be
+      used to control the maximum number of packets Avahi will
+      generated in a specific period of time on an interface.</p>
+    </option>
   </section>
 
   <section name="Section [wide-area]">
-- 
2.39.5