mDNS: cleanup probing and announcing sequence

The implementation was not fully to the standard. This commit adds
a nicer state machine implementation and multiple announce messages
with a minimum of 2.
This commit is contained in:
Jasper Verschueren 2018-11-07 11:41:29 +01:00 committed by Dirk Ziegelmeier
parent 65eb36b10d
commit ee7ed8c87d
2 changed files with 103 additions and 46 deletions

View File

@ -115,7 +115,7 @@ static mdns_name_result_cb_t mdns_name_result_cb;
#define MDNS_RESPONSE_DELAY (LWIP_RAND() %(MDNS_RESPONSE_DELAY_MAX - \ #define MDNS_RESPONSE_DELAY (LWIP_RAND() %(MDNS_RESPONSE_DELAY_MAX - \
MDNS_RESPONSE_DELAY_MIN) + MDNS_RESPONSE_DELAY_MIN) MDNS_RESPONSE_DELAY_MIN) + MDNS_RESPONSE_DELAY_MIN)
/** Probing defines */ /** Probing & announcing defines */
#define MDNS_PROBE_DELAY_MS 250 #define MDNS_PROBE_DELAY_MS 250
#define MDNS_PROBE_COUNT 3 #define MDNS_PROBE_COUNT 3
#ifdef LWIP_RAND #ifdef LWIP_RAND
@ -125,9 +125,12 @@ static mdns_name_result_cb_t mdns_name_result_cb;
#define MDNS_INITIAL_PROBE_DELAY_MS MDNS_PROBE_DELAY_MS #define MDNS_INITIAL_PROBE_DELAY_MS MDNS_PROBE_DELAY_MS
#endif #endif
#define MDNS_PROBING_NOT_STARTED 0 /* Delay between successive announcements (RFC6762 section 8.3)
#define MDNS_PROBING_ONGOING 1 * -> increase by a factor 2 with every response sent.
#define MDNS_PROBING_COMPLETE 2 */
#define MDNS_ANNOUNCE_DELAY_MS 1000
/* Minimum 2 announces, may send up to 8 (RFC6762 section 8.3) */
#define MDNS_ANNOUNCE_COUNT 2
/** Information about received packet */ /** Information about received packet */
struct mdns_packet { struct mdns_packet {
@ -187,7 +190,7 @@ struct mdns_answer {
u16_t rd_offset; u16_t rd_offset;
}; };
static void mdns_probe(void* arg); static void mdns_probe_and_announce(void* arg);
/** /**
* Construction to make mdns struct accessible from mdns_out.c * Construction to make mdns struct accessible from mdns_out.c
@ -830,7 +833,8 @@ mdns_handle_question(struct mdns_packet *pkt, struct netif *netif)
int i; int i;
err_t res; err_t res;
if (mdns->probing_state != MDNS_PROBING_COMPLETE) { if ((mdns->state != MDNS_STATE_COMPLETE) &&
(mdns->state != MDNS_STATE_ANNOUNCING)) {
/* Don't answer questions until we've verified our domains via probing */ /* Don't answer questions until we've verified our domains via probing */
/* @todo we should check incoming questions during probing for tiebreaking */ /* @todo we should check incoming questions during probing for tiebreaking */
return; return;
@ -1115,9 +1119,11 @@ mdns_handle_response(struct mdns_packet *pkt, struct netif *netif)
mdns_domain_debug_print(&ans.info.domain); mdns_domain_debug_print(&ans.info.domain);
LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass)); LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass));
/*"Apparently conflicting Multicast DNS responses received *before* the first probe packet is sent MUST /* "Conflicting Multicast DNS responses received *before* the first probe
be silently ignored" so drop answer if we haven't started probing yet*/ * packet is sent MUST be silently ignored" so drop answer if we haven't
if ((mdns->probing_state == MDNS_PROBING_ONGOING) && (mdns->probes_sent > 0)) { * started probing yet. */
if ((mdns->state == MDNS_STATE_PROBING) ||
(mdns->state == MDNS_STATE_ANNOUNCE_WAIT)) {
struct mdns_domain domain; struct mdns_domain domain;
u8_t i; u8_t i;
u8_t conflict = 0; u8_t conflict = 0;
@ -1141,7 +1147,7 @@ mdns_handle_response(struct mdns_packet *pkt, struct netif *netif)
} }
if (conflict != 0) { if (conflict != 0) {
sys_untimeout(mdns_probe, netif); sys_untimeout(mdns_probe_and_announce, netif);
if (mdns_name_result_cb != NULL) { if (mdns_name_result_cb != NULL) {
mdns_name_result_cb(netif, MDNS_PROBING_CONFLICT); mdns_name_result_cb(netif, MDNS_PROBING_CONFLICT);
} }
@ -1322,36 +1328,71 @@ mdns_send_probe(struct netif* netif, const ip_addr_t *destination)
} }
/** /**
* Timer callback for probing network. * Timer callback for probing and announcing on the network.
*/ */
static void static void
mdns_probe(void* arg) mdns_probe_and_announce(void* arg)
{ {
struct netif *netif = (struct netif *)arg; struct netif *netif = (struct netif *)arg;
struct mdns_host* mdns = NETIF_TO_HOST(netif); struct mdns_host* mdns = NETIF_TO_HOST(netif);
u32_t announce_delay;
if(mdns->probes_sent >= MDNS_PROBE_COUNT) {
/* probing successful, announce the new name */ switch (mdns->state) {
mdns->probing_state = MDNS_PROBING_COMPLETE; case MDNS_STATE_OFF:
mdns_resp_announce(netif); case MDNS_STATE_PROBE_WAIT:
if (mdns_name_result_cb != NULL) { case MDNS_STATE_PROBING:
mdns_name_result_cb(netif, MDNS_PROBING_SUCCESSFUL);
}
} else {
#if LWIP_IPV4 #if LWIP_IPV4
/*if ipv4 wait with probing until address is set*/ /*if ipv4 wait with probing until address is set*/
if (!ip4_addr_isany_val(*netif_ip4_addr(netif)) && if (!ip4_addr_isany_val(*netif_ip4_addr(netif)) &&
mdns_send_probe(netif, &v4group) == ERR_OK) mdns_send_probe(netif, &v4group) == ERR_OK)
#endif
{
#if LWIP_IPV6
if (mdns_send_probe(netif, &v6group) == ERR_OK)
#endif #endif
{ {
mdns->probes_sent++; #if LWIP_IPV6
if (mdns_send_probe(netif, &v6group) == ERR_OK)
#endif
{
mdns->state = MDNS_STATE_PROBING;
mdns->sent_num++;
}
} }
}
sys_timeout(MDNS_PROBE_DELAY_MS, mdns_probe, netif); if (mdns->sent_num >= MDNS_PROBE_COUNT) {
mdns->state = MDNS_STATE_ANNOUNCE_WAIT;
mdns->sent_num = 0;
}
sys_timeout(MDNS_PROBE_DELAY_MS, mdns_probe_and_announce, netif);
break;
case MDNS_STATE_ANNOUNCE_WAIT:
case MDNS_STATE_ANNOUNCING:
if (mdns->sent_num == 0) {
/* probing was succesful, announce all records */
mdns->state = MDNS_STATE_ANNOUNCING;
/* Let the client know probing was successful */
if (mdns_name_result_cb != NULL) {
mdns_name_result_cb(netif, MDNS_PROBING_SUCCESSFUL);
}
}
mdns_resp_announce(netif);
mdns->sent_num++;
if (mdns->sent_num >= MDNS_ANNOUNCE_COUNT) {
/* Announcing and probing complete */
mdns->state = MDNS_STATE_COMPLETE;
mdns->sent_num = 0;
}
else {
announce_delay = MDNS_ANNOUNCE_DELAY_MS * (1 << (mdns->sent_num - 1));
sys_timeout(announce_delay, mdns_probe_and_announce, netif);
}
break;
case MDNS_STATE_COMPLETE:
default:
/* Do nothing */
break;
} }
} }
@ -1381,8 +1422,6 @@ mdns_resp_add_netif(struct netif *netif, const char *hostname)
netif_set_client_data(netif, mdns_netif_client_id, mdns); netif_set_client_data(netif, mdns_netif_client_id, mdns);
MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(hostname))); MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(hostname)));
mdns->probes_sent = 0;
mdns->probing_state = MDNS_PROBING_NOT_STARTED;
/* Init delayed message structs with address and port */ /* Init delayed message structs with address and port */
#if LWIP_IPV4 #if LWIP_IPV4
@ -1439,9 +1478,7 @@ mdns_resp_remove_netif(struct netif *netif)
mdns = NETIF_TO_HOST(netif); mdns = NETIF_TO_HOST(netif);
LWIP_ERROR("mdns_resp_remove_netif: Not an active netif", (mdns != NULL), return ERR_VAL); LWIP_ERROR("mdns_resp_remove_netif: Not an active netif", (mdns != NULL), return ERR_VAL);
if (mdns->probing_state == MDNS_PROBING_ONGOING) { sys_untimeout(mdns_probe_and_announce, netif);
sys_untimeout(mdns_probe, netif);
}
for (i = 0; i < MDNS_MAX_SERVICES; i++) { for (i = 0; i < MDNS_MAX_SERVICES; i++) {
struct mdns_service *service = mdns->services[i]; struct mdns_service *service = mdns->services[i];
@ -1643,7 +1680,12 @@ mdns_resp_announce(struct netif *netif)
return; return;
} }
if (mdns->probing_state == MDNS_PROBING_COMPLETE) { /* Do not announce if the mdns responder is off, waiting to probe, probing or
* waiting to announce. */
if (!( (mdns->state == MDNS_STATE_OFF)
|| (mdns->state == MDNS_STATE_PROBE_WAIT)
|| (mdns->state == MDNS_STATE_PROBING)
|| (mdns->state == MDNS_STATE_ANNOUNCE_WAIT))) {
/* Announce on IPv6 and IPv4 */ /* Announce on IPv6 and IPv4 */
#if LWIP_IPV6 #if LWIP_IPV6
mdns_announce(netif, &v6group); mdns_announce(netif, &v6group);
@ -1693,14 +1735,13 @@ mdns_resp_restart(struct netif *netif)
if (mdns == NULL) { if (mdns == NULL) {
return; return;
} }
/* Make sure timer is not running */
sys_untimeout(mdns_probe_and_announce, netif);
if (mdns->probing_state == MDNS_PROBING_ONGOING) {
sys_untimeout(mdns_probe, 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)*/ /* @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->probes_sent = 0; mdns->sent_num = 0;
mdns->probing_state = MDNS_PROBING_ONGOING; mdns->state = MDNS_STATE_PROBE_WAIT;
sys_timeout(MDNS_INITIAL_PROBE_DELAY_MS, mdns_probe, netif); sys_timeout(MDNS_INITIAL_PROBE_DELAY_MS, mdns_probe_and_announce, netif);
} }
/** /**

View File

@ -160,16 +160,32 @@ struct mdns_delayed_msg {
struct mdns_outmsg delayed_msg_unicast; struct mdns_outmsg delayed_msg_unicast;
}; };
/* MDNS states */
typedef enum {
/* MDNS module is off */
MDNS_STATE_OFF,
/* Waiting before probing can be started */
MDNS_STATE_PROBE_WAIT,
/* Probing the unique records */
MDNS_STATE_PROBING,
/* Waiting before announcing the probed unique records */
MDNS_STATE_ANNOUNCE_WAIT,
/* Announcing all records */
MDNS_STATE_ANNOUNCING,
/* Probing and announcing completed */
MDNS_STATE_COMPLETE
} acd_state_enum_t;
/** Description of a host/netif */ /** Description of a host/netif */
struct mdns_host { struct mdns_host {
/** Hostname */ /** Hostname */
char name[MDNS_LABEL_MAXLEN + 1]; char name[MDNS_LABEL_MAXLEN + 1];
/** Pointer to services */ /** Pointer to services */
struct mdns_service *services[MDNS_MAX_SERVICES]; struct mdns_service *services[MDNS_MAX_SERVICES];
/** Number of probes sent for the current name */ /** Number of probes/announces sent for the current name */
u8_t probes_sent; u8_t sent_num;
/** State in probing sequence */ /** State of the mdns responder */
u8_t probing_state; acd_state_enum_t state;
#if LWIP_IPV4 #if LWIP_IPV4
/** delayed msg struct for IPv4 */ /** delayed msg struct for IPv4 */
struct mdns_delayed_msg ipv4; struct mdns_delayed_msg ipv4;