/* route.h * * Copyright (c) 2019-2024 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This code adds border router support to 3rd party HomeKit Routers as part of Appleā€™s commitment to the CHIP project. * * Definitions for route.c */ #ifndef __SERVICE_REGISTRATION_ROUTE_H #define __SERVICE_REGISTRATION_ROUTE_H #if defined(USE_IPCONFIGURATION_SERVICE) #include #include "IPConfigurationService.h" #endif typedef struct icmp_listener icmp_listener_t; typedef struct route_state route_state_t; typedef struct srp_server_state srp_server_t; typedef struct nat64 nat64_t; typedef struct omr_watcher omr_watcher_t; typedef struct omr_watcher_callback omr_watcher_callback_t; typedef struct omr_publisher omr_publisher_t; typedef struct route_tracker route_tracker_t; // RFC 4861 specifies a minimum of 4 seconds between RAs. We add a bit of fuzz. #define MIN_DELAY_BETWEEN_RAS 4000 #define RA_FUZZ_TIME 1000 // RFC 4861 specifies a maximum of three transmissions when sending an RA. #define MAX_RA_RETRANSMISSION 3 // There's no limit for unicast neighbor solicits, but we limit it to three. #define MAX_NS_RETRANSMISSIONS 3 // 60 seconds between router probes, three retries, four seconds per retry. We should have an answer from the router at // most 76 seconds after the previous answer, assuming that it takes four seconds for the response to arrive, which is // of course ridiculously long. #define MAX_ROUTER_RECEIVED_TIME_GAP_BEFORE_UNREACHABLE 76 * MSEC_PER_SEC // The end of the valid lifetime of the prefix is the time we received it plus the valid lifetime that was expressed in // the PIO option of the RA that advertised the prefix. If we have a prefix that is within ten minutes of expiring, we // consider it stale and start advertising a prefix. This should never happen in a network where a router is advertising // a prefix--if it does, either we're having trouble receiving multicast RAs (meaning that we don't get every beacon) or // the router has gone away. #define MAX_ROUTER_RECEIVED_TIME_GAP_BEFORE_STALE 600 * MSEC_PER_SEC // The Thread BR prefix needs to stick around long enough that it's not likely to accidentally disappear because of // dropped multicasts, but short enough that it goes away quickly when a router that's advertising IPv6 connectivity // comes online. #define BR_PREFIX_LIFETIME 30 * 60 #define BR_PREFIX_SLASH_64_BYTES 8 #ifndef RTR_SOLICITATION_INTERVAL #define RTR_SOLICITATION_INTERVAL 4 /* 4sec */ #endif #ifndef ND6_INFINITE_LIFETIME #define ND6_INFINITE_LIFETIME 0xffffffff #endif #define MAX_ANYCAST_NUM 5 typedef struct interface interface_t; typedef struct icmp_message icmp_message_t; struct interface { int ref_count; interface_t *NULLABLE next; char *NONNULL name; // Wakeup event for next beacon. wakeup_t *NULLABLE beacon_wakeup; // Wakeup event called after we're done sending solicits. At this point we delete all routes more than 10 minutes // old; if none are left, then we assume there's no IPv6 service on the interface. wakeup_t *NULLABLE post_solicit_wakeup; // Wakeup event to trigger the next router solicit or neighbor solicit to be sent. wakeup_t *NULLABLE router_solicit_wakeup; // Wakeup event to trigger the next router solicit to be sent. wakeup_t *NULLABLE neighbor_solicit_wakeup; // Wakeup event to deconfigure the on-link prefix after it is no longer valid. wakeup_t *NULLABLE deconfigure_wakeup; #if SRP_FEATURE_VICARIOUS_ROUTER_DISCOVERY // Wakeup event to detect that vicarious router discovery is complete wakeup_t *NULLABLE vicarious_discovery_complete; #endif // SRP_FEATURE_VICARIOUS_ROUTER_DISCOVERY // Wakeup event to periodically notice whether routers we have heard previously on this interface have gone stale. wakeup_t *NULLABLE stale_evaluation_wakeup; // Wakeup event to periodically probe routers for reachability wakeup_t *NULLABLE router_probe_wakeup; // The route state object to which this interface belongs. route_state_t *NULLABLE route_state; // List of Router Advertisement messages from different routers. icmp_message_t *NULLABLE routers; // List of Router Solicit messages from different hosts for which we are still transmitting unicast // RAs (we sent three unicast RAs per solicit to ensure delivery). icmp_message_t *NULLABLE solicits; int prefix_number; #if defined(USE_IPCONFIGURATION_SERVICE) // The service used to configure this interface with an address in the on-link prefix IPConfigurationServiceRef NULLABLE ip_configuration_service; // SCDynamicStoreRef SCDynamicStoreRef NULLABLE ip_configuration_store; #else subproc_t *NULLABLE link_route_adder_process; #endif struct in6_addr link_local; // Link-local address struct in6_addr ipv6_prefix; // This is the prefix we advertise, if advertise_ipv6_prefix is true. // Absolute time of last beacon, and of next beacon. uint64_t last_beacon, next_beacon; // Absolute deadline for deprecating the on-link prefix we've been announcing uint64_t deprecate_deadline; // Last time we did a router probe uint64_t last_router_probe; // Preferred lifetime for the on-link prefix uint32_t preferred_lifetime; // Valid lifetime for the on-link prefix uint32_t valid_lifetime; // When the interface becomes active, we send up to three solicits. // Later on, we send three neighbor solicit probes every sixty seconds to verify router reachability int num_solicits_sent; // The interface index according to the operating systme. int index; // Number of IPv4 addresses configured on link. int num_ipv4_addresses; // Number of IPv4 addresses configured on link. int num_ipv6_addresses, old_num_ipv6_addresses; // Number of beacons sent. After the first three, the inter-beacon interval goes up. int num_beacons_sent; // The interface link layer address, if known. uint8_t link_layer[6]; // True if the interface is not usable. bool inactive, previously_inactive; // True if this interface can never be used for routing to the thread network (e.g., loopback, tunnels, etc.) bool ineligible, previously_ineligible; // True if we've determined that it's the thread interface. bool is_thread; // True if we have (or intended to) advertised our own prefix on this link. It should be true until the prefix // we advertised should have expired on all hosts that might have received it. This will be set before we actually // advertise a prefix, so before the first time we advertise a prefix it may be set even though the prefix can't // appear in any host's routing table yet. bool our_prefix_advertised; // True if we should suppress on-link prefix. This would be the case when deprecating if we aren't sending // periodic updates of the deprecated prefix. bool suppress_ipv6_prefix; // True if we've gotten a link-layer address. bool have_link_layer_address; // True if the on-link prefix is configured on the interface. bool on_link_prefix_configured; // True if we've sent our first beacon since the interface came up. bool sent_first_beacon; // Indicates whether or not router discovery was ever started for this interface. bool router_discovery_started; // Indicates whether or not router discovery has completed for this interface. bool router_discovery_complete; // Indicates whether we're currently doing router discovery, so that we don't // restart it when we're already doing it. bool router_discovery_in_progress; // Indicates that we've received a router discovery message from some other host, // and are waiting 20 seconds to snoop for replies to that RD message that are // multicast. If we hear no replies during that time, we trigger router discovery. bool vicarious_router_discovery_in_progress; // True if we are probing usable routers with neighbor solicits to see if they are still alive. bool probing; // Indicates that we have received an interface removal event, it is useful when srp-mdns-proxy is changed to a new // network where the network signature are the same and they both have no IPv6 service (so no IPv6 prefix will be // removed), in such case there will be no change from srp-mdns-proxy's point of view. However, configd may still // flush the IPv6 routing since changing network would cause interface up/down. When the flushing happens, // srp-mdns-proxy should be able to reconfigure the IPv6 routing by reconfiguring IPv6 prefix. By setting // need_reconfigure_prefix only when interface address removal happens and check it during the routing evaluation // srp-mdns-proxy can reconfigure it after the routing evaluation finishes, like router discovery. bool need_reconfigure_prefix; // This variable is used to notice when the path evaluator doesn't return an interface on the interface list. // In this situation, the interface is inactive and if we are using it we should stop. bool listed; }; typedef enum icmp_option_type { icmp_option_source_link_layer_address = 1, icmp_option_target_link_layer_address = 2, icmp_option_prefix_information = 3, icmp_option_redirected_header = 4, icmp_option_mtu = 5, icmp_option_route_information = 24, icmp_option_ra_flags_extension = 26, } icmp_option_type_t; typedef enum icmp_type { icmp_type_echo_request = 128, icmp_type_echo_reply = 129, icmp_type_router_solicitation = 133, icmp_type_router_advertisement = 134, icmp_type_neighbor_solicitation = 135, icmp_type_neighbor_advertisement = 136, icmp_type_redirect = 137, } icmp_type_t; #ifndef ND_OPT_RA_FLAGS_EXTENSION #define ND_OPT_RA_FLAGS_EXTENSION icmp_option_ra_flags_extension #endif #define RA_FLAGS1_STUB_ROUTER 0x80 typedef struct link_layer_address { uint16_t length; uint8_t address[32]; } link_layer_address_t; typedef uint8_t ra_flags_extension_t[6]; typedef struct prefix_information { struct in6_addr prefix; uint8_t length; uint8_t flags; uint32_t valid_lifetime; uint32_t preferred_lifetime; bool found; // For comparing RAs } prefix_information_t; typedef struct route_information { struct in6_addr prefix; uint8_t length; uint8_t flags; uint32_t route_lifetime; } route_information_t; typedef struct icmp_option { icmp_option_type_t type; union { link_layer_address_t link_layer_address; prefix_information_t prefix_information; route_information_t route_information; ra_flags_extension_t ra_flags_extension; } option; } icmp_option_t; struct icmp_message { icmp_message_t *NULLABLE next; interface_t *NULLABLE interface; icmp_option_t *NULLABLE options; wakeup_t *NULLABLE wakeup; route_state_t *NULLABLE route_state; bool usable; // True if this router was usable at the last policy evaluation bool reachable; // True if this router was reachable when last probed bool reached; // Set to true when we get a neighbor advertise from the router bool new_router; // If this router information is a newly received one. bool received_time_already_adjusted; // if the received time of the message is already adjusted by vicarious mode bool stub_router; // True if this RA came from a stub router. int retransmissions_received; // # times we've received a solicit from this host during retransmit window int messages_sent; // # of unicast RAs we've sent in response to a solicit, or # of unicast NSs // we've sent to confirm router aliveness struct in6_addr source; struct in6_addr destination; uint64_t received_time; uint64_t latest_na; // Most recent time at which we successfully got a neighbor advertise uint32_t reachable_time; uint32_t retransmission_timer; uint8_t cur_hop_limit; // Current hop limit for Router Advertisement messages. uint8_t flags; uint8_t type; uint8_t code; uint16_t checksum; // We hope the kernel figures this out for us. uint16_t router_lifetime; int num_options; int hop_limit; // Hop limit provided by the kernel, must be 255. }; struct route_state { route_state_t *NULLABLE next; const char *NULLABLE name; srp_server_t *NULLABLE srp_server; interface_address_state_t *NULLABLE interface_addresses; omr_watcher_t *NULLABLE omr_watcher; omr_publisher_t *NULLABLE omr_publisher; route_tracker_t *NULLABLE route_tracker; omr_watcher_callback_t *NULLABLE omr_watcher_callback; // If true, a prefix with L=1, A=0 in an RA with M=1 is treated as usable. The reason it's not treated as // usable by default is that this will break Thread for Android phones on networks where IPv6 is present // but only DHCPv6 is supported. bool config_enable_dhcpv6_prefixes; interface_t *NULLABLE interfaces; bool have_thread_prefix; struct in6_addr my_thread_ula_prefix; bool have_mesh_local_prefix; bool have_mesh_local_address; bool advertising_srp_anycast_service; bool advertising_srp_unicast_service; bool have_proposed_srp_listener_address; bool seen_listener_address; struct in6_addr thread_mesh_local_prefix; struct in6_addr thread_mesh_local_address; struct in6_addr proposed_srp_listener_address; struct in6_addr srp_listener_ip_address; uint16_t srp_service_listen_port; comm_t *NULLABLE srp_listener; struct in6_addr xpanid_prefix; bool have_xpanid_prefix; int num_thread_interfaces; // Should be zero or one. int ula_serial; int num_thread_prefixes; int times_advertised_unicast, times_advertised_anycast; int times_unadvertised_unicast, times_unadvertised_anycast; subproc_t *NULLABLE thread_interface_enumerator_process; subproc_t *NULLABLE thread_prefix_adder_process; subproc_t *NULLABLE thread_rti_setter_process; subproc_t *NULLABLE thread_forwarding_setter_process; subproc_t *NULLABLE tcpdump_logger_process; char *NULLABLE thread_interface_name; char *NULLABLE home_interface_name; bool have_non_thread_interface; bool seen_legacy_service; #if SRP_FEATURE_NAT64 nat64_t *NULLABLE nat64; #endif bool have_rloc16; uint8_t thread_sequence_number; #ifndef RA_TESTER wakeup_t *NULLABLE thread_network_shutdown_wakeup; cti_network_state_t current_thread_state; cti_connection_t NULLABLE thread_role_context; cti_connection_t NULLABLE thread_state_context; cti_connection_t NULLABLE thread_xpanid_context; cti_connection_t NULLABLE thread_route_context; cti_connection_t NULLABLE thread_rloc16_context; cti_connection_t NULLABLE thread_ml_prefix_connection; bool thread_network_running; bool thread_network_shutting_down; #endif #if !defined(RA_TESTER) wakeup_t *NULLABLE wpan_reconnect_wakeup; #endif // !defined(RA_TESTER) #if !defined(RA_TESTER) uint64_t partition_last_prefix_set_change; uint64_t partition_last_pref_id_set_change; uint64_t partition_last_role_change; uint64_t partition_last_state_change; uint64_t partition_settle_start; uint64_t partition_service_last_add_time; bool partition_have_prefix_list; bool partition_have_pref_id_list; bool partition_tunnel_name_is_known; bool partition_can_advertise_service; bool partition_can_advertise_anycast_service; bool partition_can_provide_routing; bool partition_has_xpanid; bool partition_may_offer_service; bool partition_settle_satisfied; wakeup_t *NULLABLE partition_settle_wakeup; wakeup_t *NULLABLE partition_post_partition_wakeup; wakeup_t *NULLABLE partition_pref_id_wait_wakeup; wakeup_t *NULLABLE partition_service_add_pending_wakeup; wakeup_t *NULLABLE partition_anycast_service_add_pending_wakeup; wakeup_t *NULLABLE service_set_changed_wakeup; #endif // RA_TESTER }; extern route_state_t *NONNULL route_states; // same route_state_t *NULLABLE route_state_create(srp_server_t *NONNULL server_state, const char *NONNULL name); void route_ula_setup(route_state_t *NULLABLE route_state); void route_ula_generate(route_state_t *NULLABLE route_state); bool start_route_listener(route_state_t *NULLABLE route_state); bool start_icmp_listener(void); void icmp_leave_join(int sock, int ifindex, bool join); void infrastructure_network_startup(route_state_t *NULLABLE route_state); void infrastructure_network_shutdown(route_state_t *NULLABLE route_state); void partition_maybe_advertise_anycast_service(route_state_t *NULLABLE route_state); void partition_stop_advertising_anycast_service(route_state_t *NULLABLE route_state, uint8_t sequence_number); void partition_stop_advertising_pref_id(route_state_t *NULLABLE route_state); void partition_start_srp_listener(route_state_t *NULLABLE route_state); void partition_discontinue_srp_service(route_state_t *NULLABLE route_state); void partition_discontinue_all_srp_service(route_state_t *NULLABLE route_state); void partition_block_anycast_service(route_state_t *NULLABLE route_state, bool block); void adv_ctl_add_prefix(route_state_t *NONNULL route_state, const uint8_t *NONNULL data); void adv_ctl_remove_prefix(route_state_t *NONNULL route_state, const uint8_t *NONNULL data); #define interface_retain(interface) interface_retain_(interface, __FILE__, __LINE__) void interface_retain_(interface_t *NONNULL interface, const char *NONNULL file, int line); #define interface_release(interface) interface_release_(interface, __FILE__, __LINE__) void interface_release_(interface_t *NONNULL interface, const char *NONNULL file, int line); void route_refresh_interface_list(route_state_t *NONNULL route_state); void router_solicit(icmp_message_t *NONNULL message); void router_advertisement(icmp_message_t *NONNULL message); void neighbor_advertisement(icmp_message_t *NONNULL message); int route_get_current_infra_interface_index(void); #endif // __SERVICE_REGISTRATION_ROUTE_H // Local Variables: // mode: C // tab-width: 4 // c-file-style: "bsd" // c-basic-offset: 4 // fill-column: 120 // indent-tabs-mode: nil // End: