2 * Copyright (c) 2019-2020 Apple Inc. All rights reserved.
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
8 * https://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "mdns_internal.h"
18 #include "mdns_interface_monitor.h"
19 #include "mdns_helpers.h"
20 #include "mdns_objects.h"
22 #include <CoreUtils/CoreUtils.h>
23 #include <network_information.h>
26 #include <os/object_private.h>
28 //======================================================================================================================
29 // MARK: - Interface Monitor Kind Definition
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.
49 MDNS_OBJECT_SUBKIND_DEFINE(interface_monitor
);
51 //======================================================================================================================
52 // MARK: - Local Prototypes
54 static dispatch_queue_t
55 _mdns_internal_queue(void);
57 static dispatch_queue_t
58 _mdns_nwi_state_mutex_queue(void);
61 _mdns_interface_monitor_activate_async(mdns_interface_monitor_t monitor
);
64 _mdns_interface_monitor_terminate(mdns_interface_monitor_t me
, const OSStatus error
);
66 static mdns_interface_flags_t
67 _mdns_get_interface_flags_from_nw_path(nw_path_t path
, mdns_interface_flags_t current_flags
);
69 static mdns_interface_flags_t
70 _mdns_get_interface_flags_from_nwi_state(const char *ifname
, mdns_interface_flags_t current_flags
);
73 _mdns_start_nwi_state_monitoring(void);
75 //======================================================================================================================
78 static mdns_interface_monitor_t g_monitor_list
= NULL
;
79 static nwi_state_t g_nwi_state
= NULL
;
81 //======================================================================================================================
84 static dispatch_queue_t
85 _mdns_internal_queue(void)
87 static dispatch_once_t s_once
= 0;
88 static dispatch_queue_t s_queue
= NULL
;
89 dispatch_once(&s_once
,
91 s_queue
= dispatch_queue_create("com.apple.mdns.internal_queue", DISPATCH_QUEUE_SERIAL
);
96 //======================================================================================================================
98 static dispatch_queue_t
99 _mdns_nwi_state_mutex_queue(void)
101 static dispatch_once_t s_once
= 0;
102 static dispatch_queue_t s_queue
= NULL
;
103 dispatch_once(&s_once
,
105 s_queue
= dispatch_queue_create("com.apple.mdns.nwi_state_mutex", DISPATCH_QUEUE_SERIAL
);
110 //======================================================================================================================
112 MDNS_LOG_CATEGORY_DEFINE(ifmon
, "interface_monitor");
113 MDNS_LOG_CATEGORY_DEFINE(nwi
, "NWI");
115 //======================================================================================================================
116 // MARK: - Interface Monitor Public Methods
118 mdns_interface_monitor_t
119 mdns_interface_monitor_create(uint32_t interface_index
)
121 mdns_interface_monitor_t monitor
= NULL
;
122 nw_interface_t interface
= NULL
;
123 nw_parameters_t params
= NULL
;
125 mdns_interface_monitor_t obj
= _mdns_interface_monitor_alloc();
126 require_quiet(obj
, exit
);
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
);
134 obj
->ifname
= strdup(ifname
);
135 require_quiet(obj
->ifname
, exit
);
137 interface
= nw_interface_create_with_index(obj
->ifindex
);
139 os_log_error(_mdns_ifmon_log(), "nw_interface_create_with_index returned NULL for index %u", obj
->ifindex
);
143 params
= nw_parameters_create();
144 require_quiet(params
, exit
);
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
);
153 nw_path_t path
= nw_path_evaluator_copy_path(obj
->path_evaluator
);
154 require_quiet(path
, exit
);
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
;
168 nw_release_null_safe(interface
);
169 nw_release_null_safe(params
);
173 //======================================================================================================================
176 mdns_interface_monitor_activate(mdns_interface_monitor_t me
)
178 if (!me
->user_activated
) {
179 if (me
->user_queue
) {
180 _mdns_interface_monitor_activate_async(me
);
182 me
->user_activated
= true;
186 //======================================================================================================================
189 mdns_interface_monitor_invalidate(mdns_interface_monitor_t me
)
192 dispatch_async(_mdns_internal_queue(),
194 if (!me
->invalidated
) {
195 _mdns_interface_monitor_terminate(me
, kNoErr
);
196 me
->invalidated
= true;
202 //======================================================================================================================
205 mdns_interface_monitor_set_queue(mdns_interface_monitor_t me
, dispatch_queue_t queue
)
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
);
218 //======================================================================================================================
221 mdns_interface_monitor_set_event_handler(mdns_interface_monitor_t me
, mdns_event_handler_t handler
)
223 mdns_event_handler_t
const new_handler
= handler
? Block_copy(handler
) : NULL
;
224 if (me
->event_handler
) {
225 Block_release(me
->event_handler
);
227 me
->event_handler
= new_handler
;
230 //======================================================================================================================
233 mdns_interface_monitor_set_update_handler(mdns_interface_monitor_t me
, mdns_interface_monitor_update_handler_t handler
)
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
);
239 me
->update_handler
= new_handler
;
242 //======================================================================================================================
245 mdns_interface_monitor_get_interface_index(mdns_interface_monitor_t me
)
250 //======================================================================================================================
253 mdns_interface_monitor_has_ipv4_connectivity(mdns_interface_monitor_t me
)
255 return ((me
->flags
& mdns_interface_flag_ipv4_connectivity
) ? true : false);
258 //======================================================================================================================
261 mdns_interface_monitor_has_ipv6_connectivity(mdns_interface_monitor_t me
)
263 return ((me
->flags
& mdns_interface_flag_ipv6_connectivity
) ? true : false);
266 //======================================================================================================================
269 mdns_interface_monitor_is_expensive(mdns_interface_monitor_t me
)
271 return ((me
->flags
& mdns_interface_flag_expensive
) ? true : false);
274 //======================================================================================================================
277 mdns_interface_monitor_is_constrained(mdns_interface_monitor_t me
)
279 return ((me
->flags
& mdns_interface_flag_constrained
) ? true : false);
282 //======================================================================================================================
285 mdns_interface_monitor_is_clat46(mdns_interface_monitor_t me
)
287 return ((me
->flags
& mdns_interface_flag_clat46
) ? true : false);
290 //======================================================================================================================
293 mdns_interface_monitor_is_vpn(const mdns_interface_monitor_t me
)
295 return ((me
->flags
& mdns_interface_flag_vpn
) ? true : false);
298 //======================================================================================================================
299 // MARK: - Interface Monitor Private Methods
302 mdns_interface_flags_t flag
;
304 } mdns_interface_flag_description_t
;
307 _mdns_interface_monitor_copy_description(mdns_interface_monitor_t me
, const bool debug
, __unused
const bool privacy
)
309 char * description
= NULL
;
312 const char * const lim
= &buffer
[countof(buffer
)];
317 n
= mdns_snprintf_add(&dst
, lim
, "<%s: %p>: ", me
->base
.kind
->name
, me
);
318 require_quiet(n
>= 0, exit
);
320 n
= mdns_snprintf_add(&dst
, lim
, "interface %s (%u): ", me
->ifname
, me
->ifindex
);
321 require_quiet(n
>= 0, exit
);
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"}
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
);
340 description
= strdup(buffer
);
346 //======================================================================================================================
349 _mdns_interface_monitor_finalize(mdns_interface_monitor_t me
)
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
);
358 //======================================================================================================================
361 _mdns_interface_monitor_activate_internal(mdns_interface_monitor_t monitor
);
364 _mdns_interface_monitor_activate_async(mdns_interface_monitor_t me
)
367 dispatch_async(_mdns_internal_queue(),
369 _mdns_interface_monitor_activate_internal(me
);
375 _mdns_interface_monitor_activate_internal(mdns_interface_monitor_t me
)
378 require_action_quiet(!me
->activated
&& !me
->invalidated
, exit
, err
= kNoErr
);
379 me
->activated
= true;
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
);
385 const dispatch_source_t update_source
= me
->update_source
;
386 dispatch_source_set_event_handler(me
->update_source
,
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
);
398 dispatch_source_set_cancel_handler(me
->update_source
,
402 dispatch_activate(me
->update_source
);
405 nw_path_evaluator_set_update_handler(me
->path_evaluator
, _mdns_internal_queue(),
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
);
418 nw_path_evaluator_set_cancel_handler(me
->path_evaluator
,
422 nw_path_evaluator_start(me
->path_evaluator
);
423 me
->path_evaluator_started
= true;
425 mdns_interface_monitor_t
*p
= &g_monitor_list
;
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();
439 _mdns_interface_monitor_terminate(me
, err
);
443 //======================================================================================================================
446 _mdns_interface_monitor_terminate(mdns_interface_monitor_t me
, const OSStatus error
)
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
);
453 nw_forget(&me
->path_evaluator
);
455 for (mdns_interface_monitor_t
*p
= &g_monitor_list
; *p
; p
= &(*p
)->next
) {
464 dispatch_async(me
->user_queue
,
466 if (me
->event_handler
) {
467 me
->event_handler(error
? mdns_event_error
: mdns_event_invalidated
, error
);
473 //======================================================================================================================
474 // MARK: - NW Path Helpers
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)
482 static mdns_interface_flags_t
483 _mdns_get_interface_flags_from_nw_path(nw_path_t path
, mdns_interface_flags_t current_flags
)
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
;
489 if (nw_path_has_ipv6(path
)) {
490 flags
|= mdns_interface_flag_ipv6_connectivity
;
492 if (nw_path_is_expensive(path
)) {
493 flags
|= mdns_interface_flag_expensive
;
495 if (nw_path_is_constrained(path
)) {
496 flags
|= mdns_interface_flag_constrained
;
501 //======================================================================================================================
502 // MARK: - NWI Helpers
504 #define MDNS_INTERFACE_FLAGS_FROM_NWI_STATE ( \
505 mdns_interface_flag_clat46 | \
506 mdns_interface_flag_vpn \
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
)
512 __block mdns_interface_flags_t flags
= current_flags
;
513 dispatch_sync(_mdns_nwi_state_mutex_queue(),
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
;
523 if (nwi_ifstate_get_vpn_server(ifstate
)) {
524 flags
|= mdns_interface_flag_vpn
;
530 //======================================================================================================================
533 _mdns_nwi_state_update(void);
536 _mdns_start_nwi_state_monitoring(void)
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
)
544 _mdns_nwi_state_update();
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
);
549 _mdns_nwi_state_update();
555 _mdns_nwi_state_update(void)
557 nwi_state_t new_state
= nwi_state_copy();
559 os_log_error(_mdns_nwi_log(), "Failed to copy NWI state");
561 __block nwi_state_t old_state
;
562 dispatch_sync(_mdns_nwi_state_mutex_queue(),
564 old_state
= g_nwi_state
;
565 g_nwi_state
= new_state
;
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
);