mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-11-03 05:55:49 +00:00 
			
		
		
		
	Defined in https://tools.ietf.org/html/rfc6762#section-11: All Multicast DNS responses (including responses sent via unicast) SHOULD be sent with IP TTL set to 255
		
			
				
	
	
		
			348 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			348 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
 | 
						|
/*
 | 
						|
 * MDNS Server Networking
 | 
						|
 *
 | 
						|
 */
 | 
						|
#include <string.h>
 | 
						|
#include "mdns_networking.h"
 | 
						|
#include "esp_log.h"
 | 
						|
#include "esp_netif_net_stack.h"
 | 
						|
 | 
						|
 | 
						|
extern mdns_server_t * _mdns_server;
 | 
						|
 | 
						|
/*
 | 
						|
 * MDNS Server Networking
 | 
						|
 *
 | 
						|
 */
 | 
						|
static const char *TAG = "MDNS_Networking";
 | 
						|
 | 
						|
static struct udp_pcb * _pcb_main = NULL;
 | 
						|
 | 
						|
static void _udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip_addr_t *raddr, uint16_t rport);
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief  Low level UDP PCB Initialize
 | 
						|
 */
 | 
						|
static esp_err_t _udp_pcb_main_init(void)
 | 
						|
{
 | 
						|
    if(_pcb_main) {
 | 
						|
        return ESP_OK;
 | 
						|
    }
 | 
						|
    _pcb_main = udp_new();
 | 
						|
    if (!_pcb_main) {
 | 
						|
        return ESP_ERR_NO_MEM;
 | 
						|
    }
 | 
						|
    if (udp_bind(_pcb_main, IP_ANY_TYPE, MDNS_SERVICE_PORT) != 0) {
 | 
						|
        udp_remove(_pcb_main);
 | 
						|
        _pcb_main = NULL;
 | 
						|
        return ESP_ERR_INVALID_STATE;
 | 
						|
    }
 | 
						|
    _pcb_main->mcast_ttl = 255;
 | 
						|
    _pcb_main->remote_port = MDNS_SERVICE_PORT;
 | 
						|
    ip_addr_copy(_pcb_main->remote_ip, *(IP_ANY_TYPE));
 | 
						|
    udp_recv(_pcb_main, &_udp_recv, _mdns_server);
 | 
						|
    return ESP_OK;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief  Low level UDP PCB Free
 | 
						|
 */
 | 
						|
static void _udp_pcb_main_deinit(void)
 | 
						|
{
 | 
						|
    if(_pcb_main){
 | 
						|
        udp_recv(_pcb_main, NULL, NULL);
 | 
						|
        udp_disconnect(_pcb_main);
 | 
						|
        udp_remove(_pcb_main);
 | 
						|
        _pcb_main = NULL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief  Low level UDP Multicast membership control
 | 
						|
 */
 | 
						|
static esp_err_t _udp_join_group(mdns_if_t if_inx, mdns_ip_protocol_t ip_protocol, bool join)
 | 
						|
{
 | 
						|
    struct netif * netif = NULL;
 | 
						|
    esp_netif_t *tcpip_if = _mdns_get_esp_netif(if_inx);
 | 
						|
 | 
						|
    if (!esp_netif_is_netif_up(tcpip_if)) {
 | 
						|
        // Network interface went down before event propagated, skipping IGMP config
 | 
						|
        return ESP_ERR_INVALID_STATE;
 | 
						|
    }
 | 
						|
 | 
						|
    netif = esp_netif_get_netif_impl(tcpip_if);
 | 
						|
    assert(netif);
 | 
						|
 | 
						|
    if (ip_protocol == MDNS_IP_PROTOCOL_V4) {
 | 
						|
        ip4_addr_t multicast_addr;
 | 
						|
        IP4_ADDR(&multicast_addr, 224, 0, 0, 251);
 | 
						|
 | 
						|
        if(join){
 | 
						|
            if (igmp_joingroup_netif(netif, &multicast_addr)) {
 | 
						|
                return ESP_ERR_INVALID_STATE;
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            if (igmp_leavegroup_netif(netif, &multicast_addr)) {
 | 
						|
                return ESP_ERR_INVALID_STATE;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
#if CONFIG_LWIP_IPV6
 | 
						|
    else {
 | 
						|
        ip_addr_t multicast_addr = IPADDR6_INIT(0x000002ff, 0, 0, 0xfb000000);
 | 
						|
 | 
						|
        if(join){
 | 
						|
            if (mld6_joingroup_netif(netif, &(multicast_addr.u_addr.ip6))) {
 | 
						|
                return ESP_ERR_INVALID_STATE;
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            if (mld6_leavegroup_netif(netif, &(multicast_addr.u_addr.ip6))) {
 | 
						|
                return ESP_ERR_INVALID_STATE;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
#endif
 | 
						|
    return ESP_OK;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief  the receive callback of the raw udp api. Packets are received here
 | 
						|
 *
 | 
						|
 */
 | 
						|
static void _udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip_addr_t *raddr, uint16_t rport)
 | 
						|
{
 | 
						|
 | 
						|
    uint8_t i;
 | 
						|
    while (pb != NULL) {
 | 
						|
        struct pbuf * this_pb = pb;
 | 
						|
        pb = pb->next;
 | 
						|
        this_pb->next = NULL;
 | 
						|
 | 
						|
        mdns_rx_packet_t * packet = (mdns_rx_packet_t *)malloc(sizeof(mdns_rx_packet_t));
 | 
						|
        if (!packet) {
 | 
						|
            HOOK_MALLOC_FAILED;
 | 
						|
            //missed packet - no memory
 | 
						|
            pbuf_free(this_pb);
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        packet->tcpip_if = MDNS_IF_MAX;
 | 
						|
        packet->pb = this_pb;
 | 
						|
        packet->src_port = rport;
 | 
						|
#if CONFIG_LWIP_IPV6
 | 
						|
        packet->src.type = raddr->type;
 | 
						|
        memcpy(&packet->src.u_addr, &raddr->u_addr, sizeof(raddr->u_addr));
 | 
						|
#else
 | 
						|
        packet->src.type = IPADDR_TYPE_V4;
 | 
						|
        memcpy(&packet->src.u_addr.ip4, &raddr->addr, sizeof(ip_addr_t));
 | 
						|
#endif
 | 
						|
        packet->dest.type = packet->src.type;
 | 
						|
 | 
						|
        if (packet->src.type == IPADDR_TYPE_V4) {
 | 
						|
            packet->ip_protocol = MDNS_IP_PROTOCOL_V4;
 | 
						|
            struct ip_hdr * iphdr = (struct ip_hdr *)(((uint8_t *)(packet->pb->payload)) - UDP_HLEN - IP_HLEN);
 | 
						|
            packet->dest.u_addr.ip4.addr = iphdr->dest.addr;
 | 
						|
            packet->multicast = ip4_addr_ismulticast(&(packet->dest.u_addr.ip4));
 | 
						|
        }
 | 
						|
#if CONFIG_LWIP_IPV6
 | 
						|
        else {
 | 
						|
            packet->ip_protocol = MDNS_IP_PROTOCOL_V6;
 | 
						|
            struct ip6_hdr * ip6hdr = (struct ip6_hdr *)(((uint8_t *)(packet->pb->payload)) - UDP_HLEN - IP6_HLEN);
 | 
						|
            memcpy(&packet->dest.u_addr.ip6.addr, (uint8_t *)ip6hdr->dest.addr, 16);
 | 
						|
            packet->multicast = ip6_addr_ismulticast(&(packet->dest.u_addr.ip6));
 | 
						|
        }
 | 
						|
#endif
 | 
						|
 | 
						|
        //lwip does not return the proper pcb if you have more than one for the same multicast address (but different interfaces)
 | 
						|
        struct netif * netif = NULL;
 | 
						|
        struct udp_pcb * pcb = NULL;
 | 
						|
        for (i=0; i<MDNS_IF_MAX; i++) {
 | 
						|
            pcb = _mdns_server->interfaces[i].pcbs[packet->ip_protocol].pcb;
 | 
						|
            netif = esp_netif_get_netif_impl(_mdns_get_esp_netif(i));
 | 
						|
            if (pcb && netif && netif == ip_current_input_netif ()) {
 | 
						|
                if (packet->src.type == IPADDR_TYPE_V4) {
 | 
						|
#if CONFIG_LWIP_IPV6
 | 
						|
                    if ((packet->src.u_addr.ip4.addr & netif->netmask.u_addr.ip4.addr) != (netif->ip_addr.u_addr.ip4.addr & netif->netmask.u_addr.ip4.addr)) {
 | 
						|
#else
 | 
						|
                    if ((packet->src.u_addr.ip4.addr & netif->netmask.addr) != (netif->ip_addr.addr & netif->netmask.addr)) {
 | 
						|
#endif                  //packet source is not in the same subnet
 | 
						|
                        pcb = NULL;
 | 
						|
                        break;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                packet->tcpip_if = i;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            pcb = NULL;
 | 
						|
        }
 | 
						|
 | 
						|
        if (!pcb || !_mdns_server || !_mdns_server->action_queue
 | 
						|
          || _mdns_send_rx_action(packet) != ESP_OK) {
 | 
						|
            pbuf_free(this_pb);
 | 
						|
            free(packet);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief  Check if any of the interfaces is up
 | 
						|
 */
 | 
						|
static bool _udp_pcb_is_in_use(void){
 | 
						|
    int i, p;
 | 
						|
    for (i=0; i<MDNS_IF_MAX; i++) {
 | 
						|
        for (p=0; p<MDNS_IP_PROTOCOL_MAX; p++) {
 | 
						|
            if(_mdns_server->interfaces[i].pcbs[p].pcb){
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief  Stop PCB Main code
 | 
						|
 */
 | 
						|
static void _udp_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
 | 
						|
{
 | 
						|
    if (!_mdns_server) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    mdns_pcb_t * _pcb = &_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol];
 | 
						|
    if (_pcb->pcb) {
 | 
						|
        free(_pcb->probe_services);
 | 
						|
        _pcb->state = PCB_OFF;
 | 
						|
        _pcb->pcb = NULL;
 | 
						|
        _pcb->probe_ip = false;
 | 
						|
        _pcb->probe_services = NULL;
 | 
						|
        _pcb->probe_services_len = 0;
 | 
						|
        _pcb->probe_running = false;
 | 
						|
        _pcb->failed_probes = 0;
 | 
						|
        _udp_join_group(tcpip_if, ip_protocol, false);
 | 
						|
        if(!_udp_pcb_is_in_use()) {
 | 
						|
            _udp_pcb_main_deinit();
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief  Start PCB Main code
 | 
						|
 */
 | 
						|
static esp_err_t _udp_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
 | 
						|
{
 | 
						|
    if (!_mdns_server || _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb) {
 | 
						|
        return ESP_ERR_INVALID_STATE;
 | 
						|
    }
 | 
						|
 | 
						|
    esp_err_t err = _udp_join_group(tcpip_if, ip_protocol, true);
 | 
						|
    if(err){
 | 
						|
        return err;
 | 
						|
    }
 | 
						|
 | 
						|
    err = _udp_pcb_main_init();
 | 
						|
    if(err){
 | 
						|
        return err;
 | 
						|
    }
 | 
						|
 | 
						|
    _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb = _pcb_main;
 | 
						|
    _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].failed_probes = 0;
 | 
						|
    return ESP_OK;
 | 
						|
}
 | 
						|
 | 
						|
typedef struct {
 | 
						|
    struct tcpip_api_call_data call;
 | 
						|
    mdns_if_t tcpip_if;
 | 
						|
    mdns_ip_protocol_t ip_protocol;
 | 
						|
    struct pbuf *pbt;
 | 
						|
    const ip_addr_t *ip;
 | 
						|
    uint16_t port;
 | 
						|
    esp_err_t err;
 | 
						|
} mdns_api_call_t;
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief  Start PCB from LwIP thread
 | 
						|
 */
 | 
						|
static err_t _mdns_pcb_init_api(struct tcpip_api_call_data *api_call_msg)
 | 
						|
{
 | 
						|
    mdns_api_call_t * msg = (mdns_api_call_t *)api_call_msg;
 | 
						|
    msg->err = _udp_pcb_init(msg->tcpip_if, msg->ip_protocol);
 | 
						|
    return msg->err;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief  Stop PCB from LwIP thread
 | 
						|
 */
 | 
						|
static err_t _mdns_pcb_deinit_api(struct tcpip_api_call_data *api_call_msg)
 | 
						|
{
 | 
						|
    mdns_api_call_t * msg = (mdns_api_call_t *)api_call_msg;
 | 
						|
    _udp_pcb_deinit(msg->tcpip_if, msg->ip_protocol);
 | 
						|
    msg->err = ESP_OK;
 | 
						|
    return ESP_OK;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Non-static functions below are
 | 
						|
 *  - _mdns prefixed
 | 
						|
 *  - commented in mdns_networking.h header
 | 
						|
 */
 | 
						|
esp_err_t _mdns_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
 | 
						|
{
 | 
						|
    mdns_api_call_t msg = {
 | 
						|
        .tcpip_if = tcpip_if,
 | 
						|
        .ip_protocol = ip_protocol
 | 
						|
    };
 | 
						|
    tcpip_api_call(_mdns_pcb_init_api, &msg.call);
 | 
						|
    return msg.err;
 | 
						|
}
 | 
						|
 | 
						|
esp_err_t _mdns_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
 | 
						|
{
 | 
						|
    mdns_api_call_t msg = {
 | 
						|
        .tcpip_if = tcpip_if,
 | 
						|
        .ip_protocol = ip_protocol
 | 
						|
    };
 | 
						|
    tcpip_api_call(_mdns_pcb_deinit_api, &msg.call);
 | 
						|
    return msg.err;
 | 
						|
}
 | 
						|
 | 
						|
static err_t _mdns_udp_pcb_write_api(struct tcpip_api_call_data *api_call_msg)
 | 
						|
{
 | 
						|
    void * nif = NULL;
 | 
						|
    mdns_api_call_t * msg = (mdns_api_call_t *)api_call_msg;
 | 
						|
    mdns_pcb_t * _pcb = &_mdns_server->interfaces[msg->tcpip_if].pcbs[msg->ip_protocol];
 | 
						|
    nif = esp_netif_get_netif_impl(_mdns_get_esp_netif(msg->tcpip_if));
 | 
						|
    if (!nif) {
 | 
						|
        pbuf_free(msg->pbt);
 | 
						|
        msg->err = ERR_IF;
 | 
						|
        return ERR_IF;
 | 
						|
    }
 | 
						|
    esp_err_t err = udp_sendto_if (_pcb->pcb, msg->pbt, msg->ip, msg->port, (struct netif *)nif);
 | 
						|
    pbuf_free(msg->pbt);
 | 
						|
    msg->err = err;
 | 
						|
    return err;
 | 
						|
}
 | 
						|
 | 
						|
size_t _mdns_udp_pcb_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, const esp_ip_addr_t *ip, uint16_t port, uint8_t * data, size_t len)
 | 
						|
{
 | 
						|
    struct pbuf* pbt = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
 | 
						|
    if (pbt == NULL) {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    memcpy((uint8_t *)pbt->payload, data, len);
 | 
						|
 | 
						|
    mdns_api_call_t msg = {
 | 
						|
        .tcpip_if = tcpip_if,
 | 
						|
        .ip_protocol = ip_protocol,
 | 
						|
        .pbt = pbt,
 | 
						|
        .ip = (ip_addr_t *)ip,
 | 
						|
        .port = port
 | 
						|
    };
 | 
						|
    tcpip_api_call(_mdns_udp_pcb_write_api, &msg.call);
 | 
						|
 | 
						|
    if (msg.err) {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    return len;
 | 
						|
}
 |