blob: 499d877a7af556044f3d060fbaef0dd70239cfcb [file] [log] [blame] [edit]
/* 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 <SystemConfiguration/SystemConfiguration.h>
#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: