From 19cbf2a6526ef0d83f70f0e32d6a089ee44cdef7 Mon Sep 17 00:00:00 2001 From: Jasper Verschueren Date: Mon, 19 Nov 2018 10:59:16 +0100 Subject: [PATCH] mDNS: probe rate limiting if more then 15 conflicts occur in 10s According to RFC6762 section 8.1: If fifteen conflicts occur within any ten-second period, then the host MUST wait at least five seconds before each successive additional probe attempt. If the host restarts mDNS, instead of a probe wait timeout randomly chosen between 0 and 250ms, we wait 5s if more then 15 conflicts occured in 10seconds. This flag is reset from the moment probing succeeded. --- src/apps/mdns/mdns.c | 68 ++++++++++++++++++++++++++++--- src/include/lwip/apps/mdns_priv.h | 16 ++++++++ 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/src/apps/mdns/mdns.c b/src/apps/mdns/mdns.c index be901a27..90ffd7dd 100644 --- a/src/apps/mdns/mdns.c +++ b/src/apps/mdns/mdns.c @@ -65,6 +65,7 @@ #include "lwip/prot/dns.h" #include "lwip/prot/iana.h" #include "lwip/timeouts.h" +#include "lwip/sys.h" #include @@ -1629,6 +1630,53 @@ mdns_handle_question(struct mdns_packet *pkt, struct netif *netif) } } +/** + * Handle a probe conflict: + * - Check if we exceeded the maximum of 15 conflicts in 10seconds. + * - Let the user know there is a conflict. + * + * @param netif network interface on which the conflict occured. + */ +static void +mdns_probe_conflict(struct netif *netif) +{ + struct mdns_host* mdns = NETIF_TO_HOST(netif); + int i; + u32_t diff; + u8_t index2; + + /* Disable currently running probe / announce timer */ + sys_untimeout(mdns_probe_and_announce, netif); + + /* Increase the number of conflicts occured */ + mdns->num_conflicts++; + mdns->conflict_time[mdns->index] = sys_now(); + /* Print timestamp list */ + LWIP_DEBUGF(MDNS_DEBUG, ("mDNS: conflict timestamp list, insert index = %d\n", mdns->index)); + for(i = 0; i < MDNS_PROBE_MAX_CONFLICTS_BEFORE_RATE_LIMIT; i++) { + LWIP_DEBUGF(MDNS_DEBUG, ("mDNS: time no. %d = %d\n", i, mdns->conflict_time[i])); + } + /* Check if we had enough conflicts, minimum 15 */ + if (mdns->num_conflicts >= MDNS_PROBE_MAX_CONFLICTS_BEFORE_RATE_LIMIT) { + /* Get the index to the oldest timestamp */ + index2 = (mdns->index + 1) % MDNS_PROBE_MAX_CONFLICTS_BEFORE_RATE_LIMIT; + /* Compare the oldest vs newest time stamp */ + diff = mdns->conflict_time[mdns->index] - mdns->conflict_time[index2]; + /* If they are less then 10 seconds apart, initiate rate limit */ + if (diff < MDNS_PROBE_MAX_CONFLICTS_TIME_WINDOW) { + LWIP_DEBUGF(MDNS_DEBUG, ("mDNS: probe rate limit enabled\n")); + mdns->rate_limit_activated = 1; + } + } + /* Increase index */ + mdns->index = (mdns->index + 1) % MDNS_PROBE_MAX_CONFLICTS_BEFORE_RATE_LIMIT; + + /* Inform the host on the conflict, if a callback is set */ + if (mdns_name_result_cb != NULL) { + mdns_name_result_cb(netif, MDNS_PROBING_CONFLICT); + } +} + /** * Handle response MDNS packet * Only prints debug for now. Will need more code to do conflict resolution. @@ -1698,10 +1746,7 @@ mdns_handle_response(struct mdns_packet *pkt, struct netif *netif) } if (conflict != 0) { - sys_untimeout(mdns_probe_and_announce, netif); - if (mdns_name_result_cb != NULL) { - mdns_name_result_cb(netif, MDNS_PROBING_CONFLICT); - } + mdns_probe_conflict(netif); } } } @@ -1921,6 +1966,8 @@ mdns_probe_and_announce(void* arg) if (mdns->sent_num == 0) { /* probing was succesful, announce all records */ mdns->state = MDNS_STATE_ANNOUNCING; + /* Reset rate limit max probe conflict timeout flag */ + mdns->rate_limit_activated = 0; /* Let the client know probing was successful */ if (mdns_name_result_cb != NULL) { mdns_name_result_cb(netif, MDNS_PROBING_SUCCESSFUL); @@ -2279,10 +2326,19 @@ mdns_resp_restart(struct netif *netif) /* Make sure timer is not running */ sys_untimeout(mdns_probe_and_announce, netif); - /* @todo if we've failed 15 times within a 10 second period we MUST wait 5 seconds (or wait 5 seconds every time except first)*/ mdns->sent_num = 0; mdns->state = MDNS_STATE_PROBE_WAIT; - sys_timeout(MDNS_INITIAL_PROBE_DELAY_MS, mdns_probe_and_announce, netif); + + /* RFC6762 section 8.1: If fifteen conflicts occur within any ten-second period, + * then the host MUST wait at least five seconds before each successive + * additional probe attempt. + */ + if (mdns->rate_limit_activated == 1) { + sys_timeout(MDNS_PROBE_MAX_CONFLICTS_TIMEOUT, mdns_probe_and_announce, netif); + } + else { + sys_timeout(MDNS_INITIAL_PROBE_DELAY_MS, mdns_probe_and_announce, netif); + } } /** diff --git a/src/include/lwip/apps/mdns_priv.h b/src/include/lwip/apps/mdns_priv.h index df3a0973..778ef38a 100644 --- a/src/include/lwip/apps/mdns_priv.h +++ b/src/include/lwip/apps/mdns_priv.h @@ -64,6 +64,14 @@ extern "C" { #define MDNS_TTL_120 120 #define MDNS_TTL_4500 4500 +/* RFC6762 section 8.1: If fifteen conflicts occur within any ten-second period, + * then the host MUST wait at least five seconds before each successive + * additional probe attempt. + */ +#define MDNS_PROBE_MAX_CONFLICTS_BEFORE_RATE_LIMIT 15 +#define MDNS_PROBE_MAX_CONFLICTS_TIME_WINDOW 10000 +#define MDNS_PROBE_MAX_CONFLICTS_TIMEOUT 5000 + /* Domain structs - also visible for unit tests */ struct mdns_domain { @@ -196,6 +204,14 @@ struct mdns_host { /** delayed msg struct for IPv6 */ struct mdns_delayed_msg ipv6; #endif + /** Timestamp of probe conflict saved in list */ + u32_t conflict_time[MDNS_PROBE_MAX_CONFLICTS_BEFORE_RATE_LIMIT]; + /** Rate limit flag */ + u8_t rate_limit_activated; + /** List index for timestamps */ + u8_t index; + /** number of conflicts since startup */ + u8_t num_conflicts; }; struct mdns_host* netif_mdns_data(struct netif *netif);