385 lines
11 KiB
C
385 lines
11 KiB
C
|
/*
|
||
|
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||
|
*
|
||
|
* SPDX-License-Identifier: Apache-2.0
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* MDNS Server Networking
|
||
|
*
|
||
|
*/
|
||
|
#include <string.h>
|
||
|
#include "esp_log.h"
|
||
|
#include "lwip/ip_addr.h"
|
||
|
#include "lwip/pbuf.h"
|
||
|
#include "lwip/igmp.h"
|
||
|
#include "lwip/udp.h"
|
||
|
#include "lwip/mld6.h"
|
||
|
#include "lwip/priv/tcpip_priv.h"
|
||
|
#include "esp_system.h"
|
||
|
#include "esp_event.h"
|
||
|
#include "mdns_networking.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_MAX_INTERFACES;
|
||
|
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_MAX_INTERFACES; 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_MAX_INTERFACES; 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);
|
||
|
|
||
|
ip_addr_t ip_add_copy;
|
||
|
#if CONFIG_LWIP_IPV6
|
||
|
ip_add_copy.type = ip->type;
|
||
|
memcpy(&(ip_add_copy.u_addr), &(ip->u_addr), sizeof(ip_add_copy.u_addr));
|
||
|
#else
|
||
|
memcpy(&(ip_add_copy.addr), &(ip->u_addr), sizeof(ip_add_copy.addr));
|
||
|
#endif // CONFIG_LWIP_IPV6
|
||
|
|
||
|
mdns_api_call_t msg = {
|
||
|
.tcpip_if = tcpip_if,
|
||
|
.ip_protocol = ip_protocol,
|
||
|
.pbt = pbt,
|
||
|
.ip = &ip_add_copy,
|
||
|
.port = port
|
||
|
};
|
||
|
tcpip_api_call(_mdns_udp_pcb_write_api, &msg.call);
|
||
|
|
||
|
if (msg.err) {
|
||
|
return 0;
|
||
|
}
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
void *_mdns_get_packet_data(mdns_rx_packet_t *packet)
|
||
|
{
|
||
|
return packet->pb->payload;
|
||
|
}
|
||
|
|
||
|
size_t _mdns_get_packet_len(mdns_rx_packet_t *packet)
|
||
|
{
|
||
|
return packet->pb->len;
|
||
|
}
|
||
|
|
||
|
void _mdns_packet_free(mdns_rx_packet_t *packet)
|
||
|
{
|
||
|
pbuf_free(packet->pb);
|
||
|
free(packet);
|
||
|
}
|