1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define LOG_LEVEL LOG_LEVEL_DBG
8 #include <zephyr/logging/log.h>
9 LOG_MODULE_REGISTER(mdns_resp_test);
10 
11 #include <stdbool.h>
12 #include <stdint.h>
13 #include <string.h>
14 
15 #include <ipv6.h>
16 
17 #include <zephyr/net/dns_sd.h>
18 #include <zephyr/net/dummy.h>
19 #include <zephyr/net/ethernet.h>
20 #include <zephyr/net/mdns_responder.h>
21 #include <zephyr/net/net_if.h>
22 #include <zephyr/net/socket.h>
23 #include <zephyr/ztest.h>
24 
25 #define NULL_CHAR_SIZE 1
26 #define EXT_RECORDS_NUM 3
27 #define MAX_RESP_PKTS 8
28 #define MAX_TXT_SIZE 128
29 #define RESPONSE_TIMEOUT (K_MSEC(250))
30 
31 struct service_info {
32 	bool used;
33 	char instance[DNS_SD_INSTANCE_MAX_SIZE + NULL_CHAR_SIZE];
34 	char service[DNS_SD_SERVICE_MAX_SIZE + NULL_CHAR_SIZE];
35 	char proto[DNS_SD_PROTO_SIZE + NULL_CHAR_SIZE];
36 	char domain[DNS_SD_DOMAIN_MAX_SIZE + NULL_CHAR_SIZE];
37 	char text[MAX_TXT_SIZE];
38 	uint16_t port;
39 	struct dns_sd_rec *record;
40 };
41 
42 static struct net_if *iface1;
43 
44 static struct net_if_test {
45 	uint8_t idx; /* not used for anything, just a dummy value */
46 	uint8_t mac_addr[sizeof(struct net_eth_addr)];
47 	struct net_linkaddr ll_addr;
48 } net_iface1_data;
49 
50 static const uint8_t ipv6_hdr_start[] = {
51 0x60, 0x05, 0xe7, 0x00
52 };
53 
54 static const uint8_t ipv6_hdr_rest[] = {
55 0x11, 0xff, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0x74, 0x88,
56 0x9c, 0x1b, 0x44, 0x72, 0x39, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
57 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb
58 };
59 
60 static const uint8_t dns_sd_service_enumeration_query[] = {
61 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
62 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x07, 0x5f, 0x64, 0x6e,
63 0x73, 0x2d, 0x73, 0x64, 0x04, 0x5f, 0x75, 0x64, 0x70, 0x05, 0x6c, 0x6f, 0x63,
64 0x61, 0x6c, 0x00, 0x00, 0x0c, 0x00, 0x01
65 };
66 
67 static const uint8_t service_enum_start[] = {
68 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09,
69 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x07, 0x5f, 0x64, 0x6e,
70 0x73, 0x2d, 0x73, 0x64, 0x04, 0x5f, 0x75, 0x64, 0x70, 0x05, 0x6c, 0x6f, 0x63,
71 0x61, 0x6c, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x11, 0x94
72 };
73 
74 static const uint8_t payload_bar_udp_local[] = {
75 0x00, 0x0c, 0x04, 0x5f, 0x62, 0x61, 0x72, 0x04, 0x5f, 0x75, 0x64, 0x70, 0xc0,
76 0x23
77 };
78 
79 static const uint8_t payload_custom_tcp_local[] = {
80 0x00, 0x0f, 0x07, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x04,
81 0x5f, 0x74, 0x63, 0x70, 0xc0, 0x23
82 };
83 
84 static const uint8_t payload_foo_tcp_local[] = {
85 0x00, 0x0c, 0x04, 0x5f, 0x66, 0x6f, 0x6f, 0x04, 0x5f, 0x74, 0x63, 0x70, 0xc0,
86 0x23
87 };
88 
89 static const uint8_t payload_foo_udp_local[] = {
90 0x00, 0x0c, 0x04, 0x5f, 0x66, 0x6f, 0x6f, 0x04, 0x5f, 0x75, 0x64, 0x70, 0xc0,
91 0x23
92 };
93 
94 static uint8_t mdns_server_ipv6_addr[] = {
95 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xfb
96 };
97 
98 static struct in6_addr ll_addr = {{{
99 0xfe, 0x80, 0x43, 0xb8, 0, 0, 0, 0, 0x9f, 0x74, 0x88, 0x9c, 0x1b, 0x44, 0x72, 0x39
100 }}};
101 
102 static struct in6_addr sender_ll_addr = {{{
103 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x9f, 0x74, 0x88, 0x9c, 0x1b, 0x44, 0x72, 0x39
104 }}};
105 
106 static bool test_started;
107 static struct k_sem wait_data;
108 static struct net_pkt *response_pkts[MAX_RESP_PKTS];
109 static size_t responses_count;
110 static struct service_info services[EXT_RECORDS_NUM];
111 static struct dns_sd_rec records[EXT_RECORDS_NUM];
112 
net_iface_get_mac(const struct device * dev)113 static uint8_t *net_iface_get_mac(const struct device *dev)
114 {
115 	struct net_if_test *data = dev->data;
116 
117 	if (data->mac_addr[2] == 0x00) {
118 		/* 00-00-5E-00-53-xx Documentation RFC 7042 */
119 		data->mac_addr[0] = 0x00;
120 		data->mac_addr[1] = 0x00;
121 		data->mac_addr[2] = 0x5E;
122 		data->mac_addr[3] = 0x00;
123 		data->mac_addr[4] = 0x53;
124 		data->mac_addr[5] = 0x01;
125 	}
126 
127 	data->ll_addr.addr = data->mac_addr;
128 	data->ll_addr.len = 6U;
129 
130 	return data->mac_addr;
131 }
132 
net_iface_init(struct net_if * iface)133 static void net_iface_init(struct net_if *iface)
134 {
135 	uint8_t *mac = net_iface_get_mac(net_if_get_device(iface));
136 
137 	net_if_set_link_addr(iface, mac, sizeof(struct net_eth_addr),
138 			     NET_LINK_ETHERNET);
139 }
140 
sender_iface(const struct device * dev,struct net_pkt * pkt)141 static int sender_iface(const struct device *dev, struct net_pkt *pkt)
142 {
143 	struct net_ipv6_hdr *hdr;
144 
145 	if (!pkt->buffer) {
146 		return -ENODATA;
147 	}
148 
149 	if (test_started) {
150 		hdr = NET_IPV6_HDR(pkt);
151 
152 		if (net_ipv6_addr_cmp_raw(hdr->dst, mdns_server_ipv6_addr)) {
153 			if (responses_count < MAX_RESP_PKTS) {
154 				net_pkt_ref(pkt);
155 				response_pkts[responses_count++] = pkt;
156 				k_sem_give(&wait_data);
157 			}
158 		}
159 	}
160 
161 	return 0;
162 }
163 
164 static struct dummy_api net_iface_api = {
165 	.iface_api.init = net_iface_init,
166 	.send = sender_iface,
167 };
168 
169 #define _ETH_L2_LAYER DUMMY_L2
170 #define _ETH_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(DUMMY_L2)
171 
172 NET_DEVICE_INIT_INSTANCE(net_iface1_test,
173 			 "iface1",
174 			 iface1,
175 			 NULL,
176 			 NULL,
177 			 &net_iface1_data,
178 			 NULL,
179 			 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
180 			 &net_iface_api,
181 			 _ETH_L2_LAYER,
182 			 _ETH_L2_CTX_TYPE,
183 			 127);
184 
test_setup(void)185 static void *test_setup(void)
186 {
187 	struct net_if_addr *ifaddr;
188 	int idx;
189 
190 	memset(response_pkts, 0, sizeof(response_pkts));
191 	memset(services, 0, sizeof(services));
192 	memset(records, 0, sizeof(records));
193 
194 	responses_count = 0;
195 
196 	/* Cross assign records and buffers to entries for allocation */
197 	for (int i = 0; i < EXT_RECORDS_NUM; ++i) {
198 		services[i].record = &records[i];
199 
200 		records[i].instance = services[i].instance;
201 		records[i].service = services[i].service;
202 		records[i].proto = services[i].proto;
203 		records[i].domain = services[i].domain;
204 		records[i].text = services[i].text;
205 		records[i].port = &services[i].port;
206 	}
207 
208 	mdns_responder_set_ext_records(records, EXT_RECORDS_NUM);
209 
210 	/* The semaphore is there to wait the data to be received. */
211 	k_sem_init(&wait_data, 0, UINT_MAX);
212 
213 	iface1 = net_if_get_by_index(1);
214 
215 	zassert_not_null(iface1, "Iface1 is NULL");
216 
217 	((struct net_if_test *) net_if_get_device(iface1)->data)->idx =
218 		net_if_get_by_iface(iface1);
219 
220 	idx = net_if_get_by_iface(iface1);
221 	zassert_equal(idx, 1, "Invalid index iface1");
222 
223 	zassert_not_null(iface1, "Interface 1");
224 
225 	ifaddr = net_if_ipv6_addr_add(iface1, &ll_addr,
226 				      NET_ADDR_MANUAL, 0);
227 
228 	net_ipv6_nbr_add(iface1, &sender_ll_addr, net_if_get_link_addr(iface1), false,
229 			 NET_IPV6_NBR_STATE_STATIC);
230 
231 	zassert_not_null(ifaddr, "Failed to add LL-addr");
232 
233 	/* we need to set the addresses preferred */
234 	ifaddr->addr_state = NET_ADDR_PREFERRED;
235 
236 	net_if_up(iface1);
237 
238 	return NULL;
239 }
240 
free_service(struct service_info * service)241 static void free_service(struct service_info *service)
242 {
243 	service->used = false;
244 	service->instance[0] = '\0';
245 	service->service[0] = '\0';
246 	service->proto[0] = '\0';
247 	service->domain[0] = '\0';
248 	service->port = 0;
249 
250 }
251 
free_ext_record(struct dns_sd_rec * rec)252 static void free_ext_record(struct dns_sd_rec *rec)
253 {
254 	for (int i = 0; i < EXT_RECORDS_NUM; ++i) {
255 		if (services[i].record == rec) {
256 			free_service(&services[i]);
257 			return;
258 		}
259 	}
260 }
261 
before(void * d)262 static void before(void *d)
263 {
264 	ARG_UNUSED(d);
265 
266 	test_started = true;
267 }
268 
cleanup(void * d)269 static void cleanup(void *d)
270 {
271 	ARG_UNUSED(d);
272 
273 	test_started = false;
274 
275 	for (size_t i = 0; i < responses_count; ++i) {
276 		if (response_pkts[i]) {
277 			net_pkt_unref(response_pkts[i]);
278 			response_pkts[i] = NULL;
279 		}
280 	}
281 
282 	/* Clear semaphore counter */
283 	while (k_sem_take(&wait_data, K_NO_WAIT) == 0) {
284 		/* NOP */
285 	}
286 
287 	for (int i = 0; i < EXT_RECORDS_NUM; ++i) {
288 		if (services[i].used) {
289 			free_service(&services[i]);
290 		}
291 	}
292 }
293 
send_msg(const uint8_t * data,size_t len)294 static void send_msg(const uint8_t *data, size_t len)
295 {
296 	struct net_pkt *pkt;
297 	int res;
298 
299 	pkt = net_pkt_alloc_with_buffer(iface1, NET_IPV6UDPH_LEN + len, AF_UNSPEC,
300 					0, K_FOREVER);
301 	zassert_not_null(pkt, "PKT is null");
302 
303 	res = net_pkt_write(pkt, ipv6_hdr_start, sizeof(ipv6_hdr_start));
304 	zassert_equal(res, 0, "pkt write for header start failed");
305 
306 	res = net_pkt_write_be16(pkt, len + NET_UDPH_LEN);
307 	zassert_equal(res, 0, "pkt write for header length failed");
308 
309 	res = net_pkt_write(pkt, ipv6_hdr_rest, sizeof(ipv6_hdr_rest));
310 	zassert_equal(res, 0, "pkt write for rest of the header failed");
311 
312 	res = net_pkt_write_be16(pkt, 5353);
313 	zassert_equal(res, 0, "pkt write for UDP src port failed");
314 
315 	res = net_pkt_write_be16(pkt, 5353);
316 	zassert_equal(res, 0, "pkt write for UDP dst port failed");
317 
318 	res = net_pkt_write_be16(pkt, len + NET_UDPH_LEN);
319 	zassert_equal(res, 0, "pkt write for UDP length failed");
320 
321 	/* to simplify testing checking of UDP checksum is disabled in prj.conf */
322 	res = net_pkt_write_be16(pkt, 0);
323 	zassert_equal(res, 0, "net_pkt_write_be16() for UDP checksum failed");
324 
325 	res = net_pkt_write(pkt, data, len);
326 	zassert_equal(res, 0, "net_pkt_write() for data failed");
327 
328 	res = net_recv_data(iface1, pkt);
329 	zassert_equal(res, 0, "net_recv_data() failed");
330 }
331 
alloc_ext_record(const char * instance,const char * service,const char * proto,const char * domain,uint8_t * txt,size_t txt_len,uint16_t port)332 static struct dns_sd_rec *alloc_ext_record(const char *instance, const char *service,
333 					   const char *proto, const char *domain, uint8_t *txt,
334 					   size_t txt_len, uint16_t port)
335 {
336 	for (int i = 0; i < EXT_RECORDS_NUM; ++i) {
337 		if (!services[i].used) {
338 			services[i].used = true;
339 
340 			strcpy(services[i].instance, instance);
341 			strcpy(services[i].service, service);
342 			strcpy(services[i].proto, proto);
343 			strcpy(services[i].domain, domain);
344 
345 			if (txt && txt_len) {
346 				memcpy(services[i].text, txt, txt_len);
347 			}
348 
349 			services[i].port = htons(port);
350 			services[i].record->text_size = txt_len;
351 
352 			return services[i].record;
353 		}
354 	}
355 
356 	return NULL;
357 }
358 
check_service_type_enum_resp(struct net_pkt * pkt,const uint8_t * payload,size_t len)359 static void check_service_type_enum_resp(struct net_pkt *pkt, const uint8_t *payload, size_t len)
360 {
361 	int res;
362 
363 	net_pkt_cursor_init(pkt);
364 
365 	net_pkt_set_overwrite(pkt, true);
366 	net_pkt_skip(pkt, NET_IPV6UDPH_LEN);
367 
368 	res = net_buf_data_match(pkt->cursor.buf, pkt->cursor.pos - pkt->cursor.buf->data,
369 				 service_enum_start, sizeof(service_enum_start));
370 
371 	zassert_equal(res, sizeof(service_enum_start),
372 		      "mDNS content beginning does not match");
373 
374 	net_pkt_skip(pkt, sizeof(service_enum_start));
375 
376 	res = net_pkt_get_len(pkt) - net_pkt_get_current_offset(pkt);
377 	zassert_equal(res, len, "Remaining packet's length does match payload's length");
378 
379 	res = net_buf_data_match(pkt->cursor.buf, pkt->cursor.pos - pkt->cursor.buf->data, payload,
380 				 len);
381 	zassert_equal(res, len, "Payload does not match");
382 }
383 
ZTEST(test_mdns_responder,test_external_records)384 ZTEST(test_mdns_responder, test_external_records)
385 {
386 	int res;
387 	struct dns_sd_rec *records[EXT_RECORDS_NUM];
388 
389 	/* mDNS responder can advertise only ports that are bound - reuse its own port */
390 	DNS_SD_REGISTER_UDP_SERVICE(foo, "zephyr", "_foo", "local", DNS_SD_EMPTY_TXT, 5353);
391 
392 	records[0] = alloc_ext_record("test_rec", "_custom", "_tcp", "local", NULL, 0, 5353);
393 	zassert_not_null(records[0], "Failed to alloc the record");
394 
395 	records[1] = alloc_ext_record("foo", "_bar", "_udp", "local", NULL, 0, 5353);
396 	zassert_not_null(records[1], "Failed to alloc the record");
397 
398 	records[2] = alloc_ext_record("bar", "_foo", "_tcp", "local", NULL, 0, 5353);
399 	zassert_not_null(records[2], "Failed to alloc the record");
400 
401 	/* Request service type enumeration */
402 	send_msg(dns_sd_service_enumeration_query, sizeof(dns_sd_service_enumeration_query));
403 
404 	/* Expect 4 packets */
405 	for (int i = 0; i < 4; ++i) {
406 		res = k_sem_take(&wait_data, RESPONSE_TIMEOUT);
407 		zassert_equal(res, 0, "Did not receive a response number %d", i + 1);
408 	}
409 
410 	/* Responder always starts with statically allocated services */
411 	check_service_type_enum_resp(response_pkts[0], payload_foo_udp_local,
412 				     sizeof(payload_foo_udp_local));
413 
414 	/* Responder iterates through external records backwards so check responses in LIFO seq. */
415 	check_service_type_enum_resp(response_pkts[1], payload_foo_tcp_local,
416 				     sizeof(payload_foo_tcp_local));
417 	check_service_type_enum_resp(response_pkts[2], payload_bar_udp_local,
418 				     sizeof(payload_bar_udp_local));
419 	check_service_type_enum_resp(response_pkts[3], payload_custom_tcp_local,
420 				     sizeof(payload_custom_tcp_local));
421 
422 	/* Remove record from the middle */
423 	free_ext_record(records[1]);
424 
425 	/* Repeat service type enumeration */
426 	send_msg(dns_sd_service_enumeration_query, sizeof(dns_sd_service_enumeration_query));
427 
428 	/* Expect 3 packets */
429 	for (int i = 0; i < 3; ++i) {
430 		res = k_sem_take(&wait_data, RESPONSE_TIMEOUT);
431 		zassert_equal(res, 0, "Did not receive a response number %d", i + 1);
432 	}
433 
434 	/* Repeat checks without the removed record */
435 	check_service_type_enum_resp(response_pkts[4], payload_foo_udp_local,
436 				     sizeof(payload_foo_udp_local));
437 	check_service_type_enum_resp(response_pkts[5], payload_foo_tcp_local,
438 				     sizeof(payload_foo_tcp_local));
439 	check_service_type_enum_resp(response_pkts[6], payload_custom_tcp_local,
440 				     sizeof(payload_custom_tcp_local));
441 }
442 
443 ZTEST_SUITE(test_mdns_responder, NULL, test_setup, before, cleanup, NULL);
444