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