net: dns: enable dns service discovery for mdns responder
This change enables support for DNS service discovery (RFC 6763) in the mdns_responder service and sample app. Fixes #29429 Signed-off-by: Christopher Friedt <chrisfriedt@gmail.com>
This commit is contained in:
parent
0ddce66d86
commit
0fc80cf79f
10 changed files with 313 additions and 7 deletions
|
@ -30,7 +30,7 @@ Build and run the mdns-responder sample application like this:
|
|||
:goals: build
|
||||
:compact:
|
||||
|
||||
After the mdns-responder sample application is started, it will wait queries
|
||||
After the mdns-responder sample application is started, it will await queries
|
||||
from the network.
|
||||
|
||||
Open a terminal window in your host and type:
|
||||
|
@ -56,3 +56,20 @@ If the query is successful, then following information is printed:
|
|||
.. code-block:: console
|
||||
|
||||
zephyr.local 2001:db8::1
|
||||
|
||||
Lastly, resolve services using DNS Service Discovery:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ avahi-browse -t -r _zephyr._tcp
|
||||
|
||||
If the query is successful, then the following information is printed:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
+ zeth IPv6 zephyr _zephyr._tcp local
|
||||
= zeth IPv6 zephyr _zephyr._tcp local
|
||||
hostname = [zephyr.local]
|
||||
address = [192.0.2.1]
|
||||
port = [4242]
|
||||
txt = []
|
||||
|
|
11
samples/net/mdns_responder/overlay-802154.conf
Normal file
11
samples/net/mdns_responder/overlay-802154.conf
Normal file
|
@ -0,0 +1,11 @@
|
|||
CONFIG_NET_IPV4=n
|
||||
CONFIG_NET_CONFIG_NEED_IPV4=n
|
||||
CONFIG_NET_CONFIG_MY_IPV4_ADDR=""
|
||||
CONFIG_NET_CONFIG_PEER_IPV4_ADDR=""
|
||||
CONFIG_NET_CONFIG_MY_IPV4_GW=""
|
||||
|
||||
CONFIG_NET_L2_IEEE802154=y
|
||||
CONFIG_NET_L2_IEEE802154_SHELL=y
|
||||
CONFIG_NET_L2_IEEE802154_LOG_LEVEL_INF=y
|
||||
|
||||
CONFIG_NET_CONFIG_IEEE802154_CHANNEL=26
|
17
samples/net/mdns_responder/overlay-bt.conf
Normal file
17
samples/net/mdns_responder/overlay-bt.conf
Normal file
|
@ -0,0 +1,17 @@
|
|||
CONFIG_BT=y
|
||||
CONFIG_BT_DEBUG_LOG=y
|
||||
CONFIG_BT_SMP=y
|
||||
CONFIG_BT_PERIPHERAL=y
|
||||
CONFIG_BT_CENTRAL=y
|
||||
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
|
||||
CONFIG_BT_DEVICE_NAME="Zephyr Echo Server"
|
||||
CONFIG_NET_L2_BT=y
|
||||
CONFIG_NET_IPV4=n
|
||||
CONFIG_NET_IPV6=y
|
||||
CONFIG_NET_CONFIG_BT_NODE=y
|
||||
CONFIG_NET_CONFIG_NEED_IPV6=y
|
||||
CONFIG_NET_IPV4=n
|
||||
CONFIG_NET_CONFIG_NEED_IPV4=n
|
||||
CONFIG_NET_CONFIG_MY_IPV4_ADDR=""
|
||||
CONFIG_NET_CONFIG_PEER_IPV4_ADDR=""
|
||||
CONFIG_NET_CONFIG_MY_IPV4_GW=""
|
|
@ -0,0 +1,7 @@
|
|||
CONFIG_NET_L2_ETHERNET=y
|
||||
CONFIG_NET_QEMU_ETHERNET=y
|
||||
|
||||
CONFIG_ETH_STELLARIS=y
|
||||
|
||||
CONFIG_NET_SLIP_TAP=n
|
||||
CONFIG_SLIP=n
|
|
@ -10,6 +10,8 @@ CONFIG_NET_HOSTNAME_UNIQUE=n
|
|||
CONFIG_NET_HOSTNAME="zephyr"
|
||||
|
||||
CONFIG_MDNS_RESPONDER=y
|
||||
CONFIG_DNS_SD=y
|
||||
CONFIG_MDNS_RESPONDER_DNS_SD=y
|
||||
|
||||
CONFIG_ENTROPY_GENERATOR=y
|
||||
CONFIG_TEST_RANDOM_GENERATOR=y
|
||||
|
@ -36,3 +38,6 @@ CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::2"
|
|||
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
|
||||
CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.0.2.2"
|
||||
CONFIG_NET_CONFIG_MY_IPV4_GW="192.0.2.2"
|
||||
|
||||
CONFIG_NET_SOCKETS=y
|
||||
CONFIG_NET_SOCKETS_POSIX_NAMES=y
|
||||
|
|
|
@ -12,10 +12,13 @@ LOG_MODULE_REGISTER(net_mdns_responder_sample, LOG_LEVEL_DBG);
|
|||
#include <zephyr.h>
|
||||
#include <net/net_core.h>
|
||||
|
||||
extern void service(void);
|
||||
|
||||
/* Note that this application does not do anything itself.
|
||||
* It is just a placeholder for waiting mDNS queries.
|
||||
*/
|
||||
void main(void)
|
||||
{
|
||||
LOG_INF("Waiting mDNS queries...");
|
||||
service();
|
||||
}
|
||||
|
|
144
samples/net/mdns_responder/src/service.c
Normal file
144
samples/net/mdns_responder/src/service.c
Normal file
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Friedt Professional Engineering Services, Inc
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <net/dns_sd.h>
|
||||
#include <net/socket.h>
|
||||
#include <posix/netinet/in.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <zephyr.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(mdns_echo_service, LOG_LEVEL_DBG);
|
||||
|
||||
/* A default port of 0 causes bind(2) to request an ephemeral port */
|
||||
#define DEFAULT_PORT 0
|
||||
|
||||
static void welcome(int fd)
|
||||
{
|
||||
static const char msg[] = "Bonjour, Zephyr world!\n";
|
||||
|
||||
send(fd, msg, sizeof(msg), 0);
|
||||
}
|
||||
|
||||
/* This is mainly here to bind to a port to get service advertisement
|
||||
* to work.. but since we're already here we might as well do something
|
||||
* useful.
|
||||
*/
|
||||
void service(void)
|
||||
{
|
||||
int r;
|
||||
int server_fd;
|
||||
int client_fd;
|
||||
socklen_t len;
|
||||
void *addrp;
|
||||
uint16_t *portp;
|
||||
struct sockaddr client_addr;
|
||||
char addrstr[INET6_ADDRSTRLEN];
|
||||
uint8_t line[64];
|
||||
|
||||
static struct sockaddr server_addr;
|
||||
|
||||
#if DEFAULT_PORT == 0
|
||||
/* The advanced use case: ephemeral port */
|
||||
#if defined(CONFIG_NET_IPV6)
|
||||
DNS_SD_REGISTER_SERVICE(zephyr, CONFIG_NET_HOSTNAME,
|
||||
"_zephyr", "_tcp", "local", DNS_SD_EMPTY_TXT,
|
||||
&((struct sockaddr_in6 *)&server_addr)->sin6_port);
|
||||
#elif defined(CONFIG_NET_IPV4)
|
||||
DNS_SD_REGISTER_SERVICE(zephyr, CONFIG_NET_HOSTNAME,
|
||||
"_zephyr", "_tcp", "local", DNS_SD_EMPTY_TXT,
|
||||
&((struct sockaddr_in *)&server_addr)->sin_port);
|
||||
#endif
|
||||
#else
|
||||
/* The simple use case: fixed port */
|
||||
DNS_SD_REGISTER_TCP_SERVICE(zephyr, CONFIG_NET_HOSTNAME,
|
||||
"_zephyr", "local", DNS_SD_EMPTY_TXT, DEFAULT_PORT);
|
||||
#endif
|
||||
|
||||
if (IS_ENABLED(CONFIG_NET_IPV6)) {
|
||||
net_sin6(&server_addr)->sin6_family = AF_INET6;
|
||||
net_sin6(&server_addr)->sin6_addr = in6addr_any;
|
||||
net_sin6(&server_addr)->sin6_port = sys_cpu_to_be16(DEFAULT_PORT);
|
||||
} else if (IS_ENABLED(CONFIG_NET_IPV4)) {
|
||||
net_sin(&server_addr)->sin_family = AF_INET;
|
||||
net_sin(&server_addr)->sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
net_sin(&server_addr)->sin_port = sys_cpu_to_be16(DEFAULT_PORT);
|
||||
} else {
|
||||
__ASSERT(false, "Neither IPv6 nor IPv4 are enabled");
|
||||
}
|
||||
|
||||
r = socket(server_addr.sa_family, SOCK_STREAM, 0);
|
||||
if (r == -1) {
|
||||
NET_DBG("socket() failed (%d)", errno);
|
||||
return;
|
||||
}
|
||||
|
||||
server_fd = r;
|
||||
NET_DBG("server_fd is %d", server_fd);
|
||||
|
||||
r = bind(server_fd, &server_addr, sizeof(server_addr));
|
||||
if (r == -1) {
|
||||
NET_DBG("bind() failed (%d)", errno);
|
||||
return;
|
||||
}
|
||||
|
||||
if (server_addr.sa_family == AF_INET6) {
|
||||
addrp = &net_sin6(&server_addr)->sin6_addr;
|
||||
portp = &net_sin6(&server_addr)->sin6_port;
|
||||
} else {
|
||||
addrp = &net_sin(&server_addr)->sin_addr;
|
||||
portp = &net_sin(&server_addr)->sin_port;
|
||||
}
|
||||
|
||||
inet_ntop(server_addr.sa_family, addrp, addrstr, sizeof(addrstr));
|
||||
NET_DBG("bound to [%s]:%u",
|
||||
log_strdup(addrstr), ntohs(*portp));
|
||||
|
||||
r = listen(server_fd, 1);
|
||||
if (r == -1) {
|
||||
NET_DBG("listen() failed (%d)", errno);
|
||||
return;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
len = sizeof(client_addr);
|
||||
r = accept(server_fd, (struct sockaddr *)&client_addr, &len);
|
||||
if (r == -1) {
|
||||
NET_DBG("accept() failed (%d)", errno);
|
||||
continue;
|
||||
}
|
||||
|
||||
client_fd = r;
|
||||
|
||||
inet_ntop(server_addr.sa_family, addrp, addrstr, sizeof(addrstr));
|
||||
NET_DBG("accepted connection from [%s]:%u",
|
||||
log_strdup(addrstr), ntohs(*portp));
|
||||
|
||||
/* send a banner */
|
||||
welcome(client_fd);
|
||||
|
||||
for (;;) {
|
||||
/* echo 1 line at a time */
|
||||
r = recv(client_fd, line, sizeof(line), 0);
|
||||
if (r == -1) {
|
||||
NET_DBG("recv() failed (%d)", errno);
|
||||
close(client_fd);
|
||||
break;
|
||||
}
|
||||
len = r;
|
||||
|
||||
r = send(client_fd, line, len, 0);
|
||||
if (r == -1) {
|
||||
NET_DBG("send() failed (%d)", errno);
|
||||
close(client_fd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -140,6 +140,16 @@ config MDNS_RESPONDER_INIT_PRIO
|
|||
Note that if NET_CONFIG_AUTO_INIT is enabled, then this value
|
||||
should be bigger than its value.
|
||||
|
||||
config MDNS_RESPONDER_DNS_SD
|
||||
bool "Enable DNS Service Discovery via mDNS"
|
||||
default y
|
||||
depends on DNS_SD
|
||||
help
|
||||
Selecting this option ensures that the MDNS Responder
|
||||
processes PTR, SRV, and TXT records according to RFC 6763.
|
||||
By doing so, Zephyr network services are discoverable
|
||||
using e.g. 'avahi-browse -t -r _greybus._tcp'.
|
||||
|
||||
module = MDNS_RESPONDER
|
||||
module-dep = NET_LOG
|
||||
module-str = Log level for mDNS responder
|
||||
|
|
|
@ -557,7 +557,10 @@ int dns_unpack_query(struct dns_msg_t *dns_msg, struct net_buf *buf,
|
|||
}
|
||||
|
||||
query_type = dns_unpack_query_qtype(end_of_label);
|
||||
if (query_type != DNS_RR_TYPE_A && query_type != DNS_RR_TYPE_AAAA) {
|
||||
if (query_type != DNS_RR_TYPE_A && query_type != DNS_RR_TYPE_AAAA
|
||||
&& query_type != DNS_RR_TYPE_PTR
|
||||
&& query_type != DNS_RR_TYPE_SRV
|
||||
&& query_type != DNS_RR_TYPE_TXT) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2017 Intel Corporation
|
||||
* Copyright (c) 2020 Friedt Professional Engineering Services, Inc
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
@ -20,10 +21,12 @@ LOG_MODULE_REGISTER(net_mdns_responder, CONFIG_MDNS_RESPONDER_LOG_LEVEL);
|
|||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <net/net_core.h>
|
||||
#include <net/net_ip.h>
|
||||
#include <net/net_pkt.h>
|
||||
#include <net/dns_resolve.h>
|
||||
|
||||
#include "dns_sd.h"
|
||||
#include "dns_pack.h"
|
||||
#include "ipv6.h"
|
||||
|
||||
|
@ -51,7 +54,6 @@ static struct net_context *ipv6;
|
|||
NET_BUF_POOL_DEFINE(mdns_msg_pool, DNS_RESOLVER_BUF_CTR,
|
||||
DNS_RESOLVER_MAX_BUF_SIZE, 0, NULL);
|
||||
|
||||
#if defined(CONFIG_NET_IPV6)
|
||||
static void create_ipv6_addr(struct sockaddr_in6 *addr)
|
||||
{
|
||||
addr->sin6_family = AF_INET6;
|
||||
|
@ -61,9 +63,7 @@ static void create_ipv6_addr(struct sockaddr_in6 *addr)
|
|||
net_ipv6_addr_create(&addr->sin6_addr,
|
||||
0xff02, 0, 0, 0, 0, 0, 0, 0x00fb);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_NET_IPV4)
|
||||
static void create_ipv4_addr(struct sockaddr_in *addr)
|
||||
{
|
||||
addr->sin_family = AF_INET;
|
||||
|
@ -72,7 +72,6 @@ static void create_ipv4_addr(struct sockaddr_in *addr)
|
|||
/* Well known IPv4 224.0.0.251 address */
|
||||
addr->sin_addr.s_addr = htonl(0xE00000FB);
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct net_context *get_ctx(sa_family_t family)
|
||||
{
|
||||
|
@ -274,6 +273,92 @@ static int send_response(struct net_context *ctx,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static const char *qtype_to_string(int qtype)
|
||||
{
|
||||
switch (qtype) {
|
||||
case DNS_RR_TYPE_A: return "A";
|
||||
case DNS_RR_TYPE_CNAME: return "CNAME";
|
||||
case DNS_RR_TYPE_PTR: return "PTR";
|
||||
case DNS_RR_TYPE_TXT: return "TXT";
|
||||
case DNS_RR_TYPE_AAAA: return "AAAA";
|
||||
case DNS_RR_TYPE_SRV: return "SRV";
|
||||
default: return "<unknown type>";
|
||||
}
|
||||
}
|
||||
|
||||
static void send_sd_response(struct net_context *ctx,
|
||||
struct net_pkt *pkt, union net_ip_header *ip_hdr,
|
||||
struct dns_msg_t *dns_msg, struct net_buf *result)
|
||||
{
|
||||
int ret;
|
||||
const struct dns_sd_rec *record;
|
||||
struct dns_sd_rec filter;
|
||||
struct sockaddr dst;
|
||||
const struct in6_addr *addr6 = NULL;
|
||||
const struct in_addr *addr4 = NULL;
|
||||
char service_buf[DNS_SD_SERVICE_MAX_SIZE + 1];
|
||||
char proto_buf[DNS_SD_PROTO_SIZE + 1];
|
||||
char domain_buf[DNS_SD_DOMAIN_MAX_SIZE + 1];
|
||||
|
||||
/* This actually is used but the compiler doesn't see that */
|
||||
ARG_UNUSED(record);
|
||||
|
||||
if (IS_ENABLED(CONFIG_NET_IPV4)) {
|
||||
/* Look up the local IPv4 address */
|
||||
addr4 = net_if_ipv4_select_src_addr(net_pkt_iface(pkt),
|
||||
&ip_hdr->ipv4->src);
|
||||
create_ipv4_addr(net_sin(&dst));
|
||||
net_context_set_ipv4_ttl(ctx, 255);
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_NET_IPV6)) {
|
||||
/* Look up the local IPv6 address */
|
||||
addr6 = net_if_ipv6_select_src_addr(net_pkt_iface(pkt),
|
||||
&ip_hdr->ipv6->src);
|
||||
create_ipv6_addr(net_sin6(&dst));
|
||||
net_context_set_ipv6_hop_limit(ctx, 255);
|
||||
}
|
||||
|
||||
ret = dns_sd_extract_service_proto_domain(dns_msg->msg,
|
||||
dns_msg->msg_size, &filter, service_buf, sizeof(service_buf),
|
||||
proto_buf, sizeof(proto_buf), domain_buf, sizeof(domain_buf));
|
||||
if (ret < 0) {
|
||||
NET_DBG("unable to extract service.proto.domain (%d)", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
DNS_SD_FOREACH(record) {
|
||||
/* Checks validity and then compare */
|
||||
if (dns_sd_rec_match(record, &filter)) {
|
||||
NET_DBG("matched query: %s.%s.%s.%s port: %u",
|
||||
record->instance, record->service,
|
||||
record->proto, record->domain,
|
||||
ntohs(*(record->port)));
|
||||
|
||||
/* Construct the response */
|
||||
ret = dns_sd_handle_ptr_query(record,
|
||||
addr4, addr6,
|
||||
result->data, result->size);
|
||||
if (ret < 0) {
|
||||
NET_DBG("dns_sd_handle_ptr_query() failed (%d)",
|
||||
ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
result->len = ret;
|
||||
|
||||
/* Send the response */
|
||||
ret = net_context_sendto(ctx, result->data,
|
||||
result->len, &dst, sizeof(dst), NULL,
|
||||
K_NO_WAIT, NULL);
|
||||
if (ret < 0) {
|
||||
NET_DBG("Cannot send mDNS reply (%d)", ret);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int dns_read(struct net_context *ctx,
|
||||
struct net_pkt *pkt,
|
||||
union net_ip_header *ip_hdr,
|
||||
|
@ -344,7 +429,7 @@ static int dns_read(struct net_context *ctx,
|
|||
}
|
||||
|
||||
NET_DBG("[%d] query %s/%s label %s (%d bytes)", queries,
|
||||
qtype == DNS_RR_TYPE_A ? "A" : "AAAA", "IN",
|
||||
qtype_to_string(qtype), "IN",
|
||||
log_strdup(result->data), ret);
|
||||
|
||||
/* If the query matches to our hostname, then send reply.
|
||||
|
@ -357,7 +442,11 @@ static int dns_read(struct net_context *ctx,
|
|||
NET_DBG("mDNS query to our hostname %s.local",
|
||||
hostname);
|
||||
send_response(ctx, pkt, ip_hdr, result, qtype);
|
||||
} else if (IS_ENABLED(CONFIG_MDNS_RESPONDER_DNS_SD)
|
||||
&& qtype == DNS_RR_TYPE_PTR) {
|
||||
send_sd_response(ctx, pkt, ip_hdr, &dns_msg, result);
|
||||
}
|
||||
|
||||
} while (--queries);
|
||||
|
||||
ret = 0;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue