blob: 39730c25ce7b6bef54c1470b10b190e70130cbc2 [file] [log] [blame]
/* nat64-macos.c
*
* Copyright (c) 2021 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.
*/
#include "nat64-macos.h"
#include "interface-monitor-macos.h"
#include "srp-log.h"
#include <CoreUtils/CoreUtils.h>
#include <mdns/pf.h>
#include <mdns/system.h>
static struct sockaddr_in nat64_primary_ipv4;
static struct in6_addr nat64_prefix;
static ifmon_t nat64_ifmon = NULL;
static bool nat64_prefix_is_set = false;
static bool nat64_active = false;
static void
nat64_reset(void)
{
OSStatus err;
if (nat64_primary_ipv4.sin_family == AF_INET) {
err = mdns_pf_set_thread_nat64_rules(nat64_prefix.s6_addr, 64, nat64_primary_ipv4.sin_addr.s_addr);
if (!err) {
mdns_system_set_ipv4_forwarding(true);
mdns_system_set_ipv6_forwarding(true);
nat64_active = true;
} else {
ERROR("nat64_reset: failed to set NAT64 rules: %ld.", (long)err);
}
} else {
err = mdns_pf_delete_thread_rules();
if (err) {
ERROR("nat64_reset: failed to delete NAT64 rules: %ld.", (long)err);
}
mdns_system_set_ipv4_forwarding(false);
mdns_system_set_ipv6_forwarding(false);
}
}
void
nat64_startup(const dispatch_queue_t queue)
{
nat64_primary_ipv4.sin_family = AF_UNSPEC;
nat64_ifmon = ifmon_create(queue);
dispatch_block_t handler = ^{
const sockaddr_ip new_primary = ifmon_get_primary_ipv4_address(nat64_ifmon);
if (SockAddrCompareAddr(&nat64_primary_ipv4, &new_primary.v4) != 0) {
nat64_primary_ipv4 = new_primary.v4;
if (nat64_prefix_is_set) {
nat64_reset();
}
}
};
ifmon_set_primary_ip_changed_handler(nat64_ifmon, handler);
ifmon_activate(nat64_ifmon, handler);
}
void
nat64_set_ula_prefix(const struct in6_addr *const ula_prefix)
{
bool changed;
if (!nat64_prefix_is_set || (memcmp(nat64_prefix.s6_addr, ula_prefix->s6_addr, 5) != 0)) {
changed = true;
} else {
changed = false;
}
if (changed) {
// Set the 48-bit ULA prefix (0xfd + global identifier), then set the next 16 bits to all-ones to make the
// 64-bit IPv6 prefix.
memset(&nat64_prefix, 0, sizeof(nat64_prefix));
memcpy(&nat64_prefix.s6_addr[0], ula_prefix->s6_addr, 6);
memset(&nat64_prefix.s6_addr[6], 0xFF, 2);
nat64_prefix_is_set = true;
if (nat64_primary_ipv4.sin_family == AF_INET) {
nat64_reset();
}
}
}
const struct in6_addr *
nat64_get_ipv6_prefix(void)
{
return &nat64_prefix;
}
bool
nat64_is_active(void)
{
return nat64_active;
}