]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/mdns_objects/mdns_interface_monitor.c
mDNSResponder-1310.40.42.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / mdns_objects / mdns_interface_monitor.c
1 /*
2 * Copyright (c) 2019-2020 Apple Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "mdns_internal.h"
18 #include "mdns_interface_monitor.h"
19 #include "mdns_helpers.h"
20 #include "mdns_objects.h"
21
22 #include <CoreUtils/CoreUtils.h>
23 #include <network_information.h>
24 #include <notify.h>
25 #include <os/log.h>
26 #include <os/object_private.h>
27
28 //======================================================================================================================
29 // MARK: - Interface Monitor Kind Definition
30
31 struct mdns_interface_monitor_s {
32 struct mdns_object_s base; // Object base.
33 mdns_interface_monitor_t next; // Next monitor in list.
34 dispatch_queue_t user_queue; // User's queue for invoking handlers.
35 nw_path_evaluator_t path_evaluator; // Path evaluator for interface properties.
36 dispatch_source_t update_source; // Data source for triggering user's update handler.
37 mdns_interface_monitor_update_handler_t update_handler; // User's update handler.
38 mdns_event_handler_t event_handler; // User's event handler.
39 char * ifname; // Name of monitored interface.
40 uint32_t ifindex; // Index of monitored interface.
41 mdns_interface_flags_t pending_flags; // The latest interface flags from path updates.
42 mdns_interface_flags_t flags; // The current interface flags made known to user.
43 bool user_activated; // True if user called activate method.
44 bool activated; // True if the monitor has been activated.
45 bool invalidated; // True if the monitor has been invalidated.
46 bool path_evaluator_started; // True if the path evaluator has been started.
47 };
48
49 MDNS_OBJECT_SUBKIND_DEFINE(interface_monitor);
50
51 //======================================================================================================================
52 // MARK: - Local Prototypes
53
54 static dispatch_queue_t
55 _mdns_internal_queue(void);
56
57 static dispatch_queue_t
58 _mdns_nwi_state_mutex_queue(void);
59
60 static void
61 _mdns_interface_monitor_activate_async(mdns_interface_monitor_t monitor);
62
63 static void
64 _mdns_interface_monitor_terminate(mdns_interface_monitor_t me, const OSStatus error);
65
66 static mdns_interface_flags_t
67 _mdns_get_interface_flags_from_nw_path(nw_path_t path, mdns_interface_flags_t current_flags);
68
69 static mdns_interface_flags_t
70 _mdns_get_interface_flags_from_nwi_state(const char *ifname, mdns_interface_flags_t current_flags);
71
72 static void
73 _mdns_start_nwi_state_monitoring(void);
74
75 //======================================================================================================================
76 // MARK: - Globals
77
78 static mdns_interface_monitor_t g_monitor_list = NULL;
79 static nwi_state_t g_nwi_state = NULL;
80
81 //======================================================================================================================
82 // MARK: - Internals
83
84 static dispatch_queue_t
85 _mdns_internal_queue(void)
86 {
87 static dispatch_once_t s_once = 0;
88 static dispatch_queue_t s_queue = NULL;
89 dispatch_once(&s_once,
90 ^{
91 s_queue = dispatch_queue_create("com.apple.mdns.internal_queue", DISPATCH_QUEUE_SERIAL);
92 });
93 return s_queue;
94 }
95
96 //======================================================================================================================
97
98 static dispatch_queue_t
99 _mdns_nwi_state_mutex_queue(void)
100 {
101 static dispatch_once_t s_once = 0;
102 static dispatch_queue_t s_queue = NULL;
103 dispatch_once(&s_once,
104 ^{
105 s_queue = dispatch_queue_create("com.apple.mdns.nwi_state_mutex", DISPATCH_QUEUE_SERIAL);
106 });
107 return s_queue;
108 }
109
110 //======================================================================================================================
111
112 MDNS_LOG_CATEGORY_DEFINE(ifmon, "interface_monitor");
113 MDNS_LOG_CATEGORY_DEFINE(nwi, "NWI");
114
115 //======================================================================================================================
116 // MARK: - Interface Monitor Public Methods
117
118 mdns_interface_monitor_t
119 mdns_interface_monitor_create(uint32_t interface_index)
120 {
121 mdns_interface_monitor_t monitor = NULL;
122 nw_interface_t interface = NULL;
123 nw_parameters_t params = NULL;
124
125 mdns_interface_monitor_t obj = _mdns_interface_monitor_alloc();
126 require_quiet(obj, exit);
127
128 obj->ifindex = interface_index;
129 char ifname[IF_NAMESIZE + 1];
130 if (if_indextoname(obj->ifindex, ifname) == NULL) {
131 os_log_error(_mdns_ifmon_log(), "if_indextoname returned NULL for index %u", obj->ifindex);
132 goto exit;
133 }
134 obj->ifname = strdup(ifname);
135 require_quiet(obj->ifname, exit);
136
137 interface = nw_interface_create_with_index(obj->ifindex);
138 if (!interface) {
139 os_log_error(_mdns_ifmon_log(), "nw_interface_create_with_index returned NULL for index %u", obj->ifindex);
140 goto exit;
141 }
142
143 params = nw_parameters_create();
144 require_quiet(params, exit);
145
146 nw_parameters_require_interface(params, interface);
147 obj->path_evaluator = nw_path_create_evaluator_for_endpoint(NULL, params);
148 if (!obj->path_evaluator) {
149 os_log_error(_mdns_ifmon_log(), "nw_path_create_evaluator_for_endpoint returned NULL for params: %@", params);
150 goto exit;
151 }
152
153 nw_path_t path = nw_path_evaluator_copy_path(obj->path_evaluator);
154 require_quiet(path, exit);
155
156 obj->pending_flags = _mdns_get_interface_flags_from_nw_path(path, mdns_interface_flag_null);
157 obj->pending_flags = _mdns_get_interface_flags_from_nwi_state(obj->ifname, obj->pending_flags);
158 obj->flags = obj->pending_flags;
159 nw_forget(&path);
160
161 monitor = obj;
162 obj = NULL;
163
164 exit:
165 if (obj) {
166 mdns_release(obj);
167 }
168 nw_release_null_safe(interface);
169 nw_release_null_safe(params);
170 return monitor;
171 }
172
173 //======================================================================================================================
174
175 void
176 mdns_interface_monitor_activate(mdns_interface_monitor_t me)
177 {
178 if (!me->user_activated) {
179 if (me->user_queue) {
180 _mdns_interface_monitor_activate_async(me);
181 }
182 me->user_activated = true;
183 }
184 }
185
186 //======================================================================================================================
187
188 void
189 mdns_interface_monitor_invalidate(mdns_interface_monitor_t me)
190 {
191 mdns_retain(me);
192 dispatch_async(_mdns_internal_queue(),
193 ^{
194 if (!me->invalidated) {
195 _mdns_interface_monitor_terminate(me, kNoErr);
196 me->invalidated = true;
197 }
198 mdns_release(me);
199 });
200 }
201
202 //======================================================================================================================
203
204 void
205 mdns_interface_monitor_set_queue(mdns_interface_monitor_t me, dispatch_queue_t queue)
206 {
207 if (!me->user_activated) {
208 dispatch_retain(queue);
209 dispatch_release_null_safe(me->user_queue);
210 me->user_queue = queue;
211 } else if (!me->user_queue) {
212 me->user_queue = queue;
213 dispatch_retain(me->user_queue);
214 _mdns_interface_monitor_activate_async(me);
215 }
216 }
217
218 //======================================================================================================================
219
220 void
221 mdns_interface_monitor_set_event_handler(mdns_interface_monitor_t me, mdns_event_handler_t handler)
222 {
223 mdns_event_handler_t const new_handler = handler ? Block_copy(handler) : NULL;
224 if (me->event_handler) {
225 Block_release(me->event_handler);
226 }
227 me->event_handler = new_handler;
228 }
229
230 //======================================================================================================================
231
232 void
233 mdns_interface_monitor_set_update_handler(mdns_interface_monitor_t me, mdns_interface_monitor_update_handler_t handler)
234 {
235 mdns_interface_monitor_update_handler_t const new_handler = handler ? Block_copy(handler) : NULL;
236 if (me->update_handler) {
237 Block_release(me->update_handler);
238 }
239 me->update_handler = new_handler;
240 }
241
242 //======================================================================================================================
243
244 uint32_t
245 mdns_interface_monitor_get_interface_index(mdns_interface_monitor_t me)
246 {
247 return me->ifindex;
248 }
249
250 //======================================================================================================================
251
252 bool
253 mdns_interface_monitor_has_ipv4_connectivity(mdns_interface_monitor_t me)
254 {
255 return ((me->flags & mdns_interface_flag_ipv4_connectivity) ? true : false);
256 }
257
258 //======================================================================================================================
259
260 bool
261 mdns_interface_monitor_has_ipv6_connectivity(mdns_interface_monitor_t me)
262 {
263 return ((me->flags & mdns_interface_flag_ipv6_connectivity) ? true : false);
264 }
265
266 //======================================================================================================================
267
268 bool
269 mdns_interface_monitor_is_expensive(mdns_interface_monitor_t me)
270 {
271 return ((me->flags & mdns_interface_flag_expensive) ? true : false);
272 }
273
274 //======================================================================================================================
275
276 bool
277 mdns_interface_monitor_is_constrained(mdns_interface_monitor_t me)
278 {
279 return ((me->flags & mdns_interface_flag_constrained) ? true : false);
280 }
281
282 //======================================================================================================================
283
284 bool
285 mdns_interface_monitor_is_clat46(mdns_interface_monitor_t me)
286 {
287 return ((me->flags & mdns_interface_flag_clat46) ? true : false);
288 }
289
290 //======================================================================================================================
291
292 bool
293 mdns_interface_monitor_is_vpn(const mdns_interface_monitor_t me)
294 {
295 return ((me->flags & mdns_interface_flag_vpn) ? true : false);
296 }
297
298 //======================================================================================================================
299 // MARK: - Interface Monitor Private Methods
300
301 typedef struct {
302 mdns_interface_flags_t flag;
303 const char * desc;
304 } mdns_interface_flag_description_t;
305
306 static char *
307 _mdns_interface_monitor_copy_description(mdns_interface_monitor_t me, const bool debug, __unused const bool privacy)
308 {
309 char * description = NULL;
310 char buffer[128];
311 char * dst = buffer;
312 const char * const lim = &buffer[countof(buffer)];
313 int n;
314
315 *dst = '\0';
316 if (debug) {
317 n = mdns_snprintf_add(&dst, lim, "<%s: %p>: ", me->base.kind->name, me);
318 require_quiet(n >= 0, exit);
319 }
320 n = mdns_snprintf_add(&dst, lim, "interface %s (%u): ", me->ifname, me->ifindex);
321 require_quiet(n >= 0, exit);
322
323 const mdns_interface_flag_description_t mdns_interface_flag_descriptions[] = {
324 {mdns_interface_flag_ipv4_connectivity, "ipv4"},
325 {mdns_interface_flag_ipv6_connectivity, "ipv6"},
326 {mdns_interface_flag_expensive, "expensive"},
327 {mdns_interface_flag_constrained, "constrained"},
328 {mdns_interface_flag_clat46, "clat46"},
329 {mdns_interface_flag_vpn, "vpn"}
330 };
331 const char *separator = "";
332 for (size_t i = 0; i < countof(mdns_interface_flag_descriptions); ++i) {
333 const mdns_interface_flag_description_t * const flag_desc = &mdns_interface_flag_descriptions[i];
334 if (me->flags & flag_desc->flag) {
335 n = mdns_snprintf_add(&dst, lim, "%s%s", separator, flag_desc->desc);
336 require_quiet(n >= 0, exit);
337 separator = ", ";
338 }
339 }
340 description = strdup(buffer);
341
342 exit:
343 return description;
344 }
345
346 //======================================================================================================================
347
348 static void
349 _mdns_interface_monitor_finalize(mdns_interface_monitor_t me)
350 {
351 dispatch_forget(&me->user_queue);
352 nw_forget(&me->path_evaluator);
353 BlockForget(&me->update_handler);
354 BlockForget(&me->event_handler);
355 ForgetMem(&me->ifname);
356 }
357
358 //======================================================================================================================
359
360 static void
361 _mdns_interface_monitor_activate_internal(mdns_interface_monitor_t monitor);
362
363 static void
364 _mdns_interface_monitor_activate_async(mdns_interface_monitor_t me)
365 {
366 mdns_retain(me);
367 dispatch_async(_mdns_internal_queue(),
368 ^{
369 _mdns_interface_monitor_activate_internal(me);
370 mdns_release(me);
371 });
372 }
373
374 static void
375 _mdns_interface_monitor_activate_internal(mdns_interface_monitor_t me)
376 {
377 OSStatus err;
378 require_action_quiet(!me->activated && !me->invalidated, exit, err = kNoErr);
379 me->activated = true;
380
381 me->update_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_REPLACE, 0, 0, me->user_queue);
382 require_action_quiet(me->update_source, exit, err = kNoResourcesErr);
383
384 mdns_retain(me);
385 const dispatch_source_t update_source = me->update_source;
386 dispatch_source_set_event_handler(me->update_source,
387 ^{
388 const unsigned long data = dispatch_source_get_data(update_source);
389 const mdns_interface_flags_t new_flags = ((mdns_interface_flags_t)data) & ~mdns_interface_flag_reserved;
390 const mdns_interface_flags_t changed_flags = me->flags ^ new_flags;
391 if (changed_flags != 0) {
392 me->flags = new_flags;
393 if (me->update_handler) {
394 me->update_handler(changed_flags);
395 }
396 }
397 });
398 dispatch_source_set_cancel_handler(me->update_source,
399 ^{
400 mdns_release(me);
401 });
402 dispatch_activate(me->update_source);
403
404 mdns_retain(me);
405 nw_path_evaluator_set_update_handler(me->path_evaluator, _mdns_internal_queue(),
406 ^(nw_path_t path)
407 {
408 const mdns_interface_flags_t new_flags = _mdns_get_interface_flags_from_nw_path(path, me->pending_flags);
409 if (new_flags != me->pending_flags) {
410 me->pending_flags = new_flags;
411 if (me->update_source) {
412 // Note: mdns_interface_flag_reserved is used to ensure that the data is non-zero. According to the
413 // dispatch_source_create(3) man page, if the data value is zero, the source handler won't be invoked.
414 dispatch_source_merge_data(me->update_source, me->pending_flags | mdns_interface_flag_reserved);
415 }
416 }
417 });
418 nw_path_evaluator_set_cancel_handler(me->path_evaluator,
419 ^{
420 mdns_release(me);
421 });
422 nw_path_evaluator_start(me->path_evaluator);
423 me->path_evaluator_started = true;
424
425 mdns_interface_monitor_t *p = &g_monitor_list;
426 while (*p != NULL) {
427 p = &(*p)->next;
428 }
429 mdns_retain(me);
430 *p = me;
431
432 // This is called after adding the monitor to the global list to ensure that the initial NWI state check is aware
433 // that the interface monitor exists.
434 _mdns_start_nwi_state_monitoring();
435 err = kNoErr;
436
437 exit:
438 if (err) {
439 _mdns_interface_monitor_terminate(me, err);
440 }
441 }
442
443 //======================================================================================================================
444
445 static void
446 _mdns_interface_monitor_terminate(mdns_interface_monitor_t me, const OSStatus error)
447 {
448 dispatch_source_forget(&me->update_source);
449 if (me->path_evaluator) {
450 if (me->path_evaluator_started) {
451 nw_path_evaluator_cancel(me->path_evaluator);
452 }
453 nw_forget(&me->path_evaluator);
454 }
455 for (mdns_interface_monitor_t *p = &g_monitor_list; *p; p = &(*p)->next) {
456 if (*p == me) {
457 *p = me->next;
458 me->next = NULL;
459 mdns_release(me);
460 break;
461 }
462 }
463 mdns_retain(me);
464 dispatch_async(me->user_queue,
465 ^{
466 if (me->event_handler) {
467 me->event_handler(error ? mdns_event_error : mdns_event_invalidated, error);
468 }
469 mdns_release(me);
470 });
471 }
472
473 //======================================================================================================================
474 // MARK: - NW Path Helpers
475
476 #define MDNS_INTERFACE_FLAGS_FROM_NWPATH \
477 (mdns_interface_flag_ipv4_connectivity | \
478 mdns_interface_flag_ipv6_connectivity | \
479 mdns_interface_flag_expensive | \
480 mdns_interface_flag_constrained)
481
482 static mdns_interface_flags_t
483 _mdns_get_interface_flags_from_nw_path(nw_path_t path, mdns_interface_flags_t current_flags)
484 {
485 mdns_interface_flags_t flags = current_flags & ~MDNS_INTERFACE_FLAGS_FROM_NWPATH;
486 if (nw_path_has_ipv4(path)) {
487 flags |= mdns_interface_flag_ipv4_connectivity;
488 }
489 if (nw_path_has_ipv6(path)) {
490 flags |= mdns_interface_flag_ipv6_connectivity;
491 }
492 if (nw_path_is_expensive(path)) {
493 flags |= mdns_interface_flag_expensive;
494 }
495 if (nw_path_is_constrained(path)) {
496 flags |= mdns_interface_flag_constrained;
497 }
498 return flags;
499 }
500
501 //======================================================================================================================
502 // MARK: - NWI Helpers
503
504 #define MDNS_INTERFACE_FLAGS_FROM_NWI_STATE ( \
505 mdns_interface_flag_clat46 | \
506 mdns_interface_flag_vpn \
507 )
508
509 static mdns_interface_flags_t
510 _mdns_get_interface_flags_from_nwi_state(const char * const ifname, const mdns_interface_flags_t current_flags)
511 {
512 __block mdns_interface_flags_t flags = current_flags;
513 dispatch_sync(_mdns_nwi_state_mutex_queue(),
514 ^{
515 require_return(g_nwi_state);
516 const nwi_ifstate_t ifstate = nwi_state_get_ifstate(g_nwi_state, ifname);
517 flags &= ~MDNS_INTERFACE_FLAGS_FROM_NWI_STATE;
518 require_return(ifstate);
519 const nwi_ifstate_flags ifstate_flags = nwi_ifstate_get_flags(ifstate);
520 if (ifstate_flags & NWI_IFSTATE_FLAGS_HAS_CLAT46) {
521 flags |= mdns_interface_flag_clat46;
522 }
523 if (nwi_ifstate_get_vpn_server(ifstate)) {
524 flags |= mdns_interface_flag_vpn;
525 }
526 });
527 return flags;
528 }
529
530 //======================================================================================================================
531
532 static void
533 _mdns_nwi_state_update(void);
534
535 static void
536 _mdns_start_nwi_state_monitoring(void)
537 {
538 static int s_nwi_notify_token = NOTIFY_TOKEN_INVALID;
539 if (s_nwi_notify_token == NOTIFY_TOKEN_INVALID) {
540 const uint32_t status = notify_register_dispatch(nwi_state_get_notify_key(), &s_nwi_notify_token,
541 _mdns_internal_queue(),
542 ^(__unused int token)
543 {
544 _mdns_nwi_state_update();
545 });
546 if (s_nwi_notify_token == NOTIFY_TOKEN_INVALID) {
547 os_log_error(_mdns_nwi_log(), "Failed to register for NWI state notifications (status %u)", status);
548 } else {
549 _mdns_nwi_state_update();
550 }
551 }
552 }
553
554 static void
555 _mdns_nwi_state_update(void)
556 {
557 nwi_state_t new_state = nwi_state_copy();
558 if (!new_state) {
559 os_log_error(_mdns_nwi_log(), "Failed to copy NWI state");
560 }
561 __block nwi_state_t old_state;
562 dispatch_sync(_mdns_nwi_state_mutex_queue(),
563 ^{
564 old_state = g_nwi_state;
565 g_nwi_state = new_state;
566 });
567 nwi_state_release_null_safe(old_state);
568 for (mdns_interface_monitor_t m = g_monitor_list; m; m = m->next) {
569 const mdns_interface_flags_t new_flags = _mdns_get_interface_flags_from_nwi_state(m->ifname, m->pending_flags);
570 if (new_flags != m->pending_flags) {
571 m->pending_flags = new_flags;
572 if (m->update_source) {
573 // Note: mdns_interface_flag_reserved is used to ensure that the data is non-zero. According to the
574 // dispatch_source_create(3) man page, if the data value is zero, the source handler won't be invoked.
575 dispatch_source_merge_data(m->update_source, m->pending_flags | mdns_interface_flag_reserved);
576 }
577 }
578 }
579 }