ntp: support per-source IP family restriction

Add a new parameter to the NSR_AddSourceByName() function to allow
individual sources to be limited to IPv4 or IPv6 addresses. This doesn't
change the options passed to the resolver. It's just an additional
filter in the processing of resolved addresses following the -4/-6
command-line option of chronyd.
This commit is contained in:
Miroslav Lichvar 2024-02-05 14:06:52 +01:00
parent e11b518a1f
commit d7c2b1d2f3
6 changed files with 38 additions and 17 deletions

View file

@ -789,7 +789,7 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
NTP_EF_FLAG_EXP_NET_CORRECTION : 0); NTP_EF_FLAG_EXP_NET_CORRECTION : 0);
params.sel_options = convert_addsrc_select_options(ntohl(rx_message->data.ntp_source.flags)); params.sel_options = convert_addsrc_select_options(ntohl(rx_message->data.ntp_source.flags));
status = NSR_AddSourceByName(name, port, pool, type, &params, NULL); status = NSR_AddSourceByName(name, IPADDR_UNSPEC, port, pool, type, &params, NULL);
switch (status) { switch (status) {
case NSR_Success: case NSR_Success:
break; break;

9
conf.c
View file

@ -1728,8 +1728,9 @@ reload_source_dirs(void)
/* Add new sources */ /* Add new sources */
if (pass == 1 && d > 0) { if (pass == 1 && d > 0) {
source = &new_sources[j]; source = &new_sources[j];
s = NSR_AddSourceByName(source->params.name, source->params.port, source->pool, s = NSR_AddSourceByName(source->params.name, IPADDR_UNSPEC, source->params.port,
source->type, &source->params.params, &new_ids[j]); source->pool, source->type, &source->params.params,
&new_ids[j]);
if (s == NSR_UnresolvedName) { if (s == NSR_UnresolvedName) {
unresolved++; unresolved++;
@ -1842,8 +1843,8 @@ CNF_AddSources(void)
for (i = 0; i < ARR_GetSize(ntp_sources); i++) { for (i = 0; i < ARR_GetSize(ntp_sources); i++) {
source = (NTP_Source *)ARR_GetElement(ntp_sources, i); source = (NTP_Source *)ARR_GetElement(ntp_sources, i);
s = NSR_AddSourceByName(source->params.name, source->params.port, source->pool, s = NSR_AddSourceByName(source->params.name, IPADDR_UNSPEC, source->params.port,
source->type, &source->params.params, NULL); source->pool, source->type, &source->params.params, NULL);
if (s != NSR_Success && s != NSR_UnresolvedName) if (s != NSR_Success && s != NSR_UnresolvedName)
LOG(LOGS_ERR, "Could not add source %s", source->params.name); LOG(LOGS_ERR, "Could not add source %s", source->params.name);

View file

@ -61,6 +61,8 @@ typedef struct {
(may be an IP address) */ (may be an IP address) */
IPAddr resolved_addr; /* Address resolved from the name, which can be IPAddr resolved_addr; /* Address resolved from the name, which can be
different from remote_addr (e.g. NTS-KE) */ different from remote_addr (e.g. NTS-KE) */
int family; /* IP family of acceptable resolved addresses
(IPADDR_UNSPEC if any) */
int pool_id; /* ID of the pool from which was this source int pool_id; /* ID of the pool from which was this source
added or INVALID_POOL */ added or INVALID_POOL */
int tentative; /* Flag indicating there was no valid response int tentative; /* Flag indicating there was no valid response
@ -98,6 +100,8 @@ struct UnresolvedSource {
int pool_id; int pool_id;
/* Name to be resolved */ /* Name to be resolved */
char *name; char *name;
/* Address family to filter resolved addresses */
int family;
/* Flag indicating addresses should be used in a random order */ /* Flag indicating addresses should be used in a random order */
int random_order; int random_order;
/* Flag indicating current address should be replaced only if it is /* Flag indicating current address should be replaced only if it is
@ -353,7 +357,7 @@ log_source(SourceRecord *record, int addition, int once_per_pool)
/* Procedure to add a new source */ /* Procedure to add a new source */
static NSR_Status static NSR_Status
add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type, add_source(NTP_Remote_Address *remote_addr, char *name, int family, NTP_Source_Type type,
SourceParameters *params, int pool_id, uint32_t conf_id) SourceParameters *params, int pool_id, uint32_t conf_id)
{ {
SourceRecord *record; SourceRecord *record;
@ -391,6 +395,7 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
record->data = NCR_CreateInstance(remote_addr, type, params, record->name); record->data = NCR_CreateInstance(remote_addr, type, params, record->name);
record->remote_addr = NCR_GetRemoteAddress(record->data); record->remote_addr = NCR_GetRemoteAddress(record->data);
record->resolved_addr = remote_addr->ip_addr; record->resolved_addr = remote_addr->ip_addr;
record->family = family;
record->pool_id = pool_id; record->pool_id = pool_id;
record->tentative = 1; record->tentative = 1;
record->conf_id = conf_id; record->conf_id = conf_id;
@ -552,6 +557,10 @@ process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs
DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&new_addr.ip_addr)); DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&new_addr.ip_addr));
/* Skip addresses not from the requested family */
if (us->family != IPADDR_UNSPEC && us->family != new_addr.ip_addr.family)
continue;
if (us->pool_id != INVALID_POOL) { if (us->pool_id != INVALID_POOL) {
/* In the pool resolving mode, try to replace a source from /* In the pool resolving mode, try to replace a source from
the pool which does not have a real address yet */ the pool which does not have a real address yet */
@ -768,14 +777,14 @@ NSR_Status
NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id) SourceParameters *params, uint32_t *conf_id)
{ {
return add_source(remote_addr, NULL, type, params, INVALID_POOL, return add_source(remote_addr, NULL, IPADDR_UNSPEC, type, params, INVALID_POOL,
get_next_conf_id(conf_id)); get_next_conf_id(conf_id));
} }
/* ================================================== */ /* ================================================== */
NSR_Status NSR_Status
NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, NSR_AddSourceByName(char *name, int family, int port, int pool, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id) SourceParameters *params, uint32_t *conf_id)
{ {
struct UnresolvedSource *us; struct UnresolvedSource *us;
@ -787,7 +796,9 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
/* If the name is an IP address, add the source with the address directly */ /* If the name is an IP address, add the source with the address directly */
if (UTI_StringToIP(name, &remote_addr.ip_addr)) { if (UTI_StringToIP(name, &remote_addr.ip_addr)) {
remote_addr.port = port; remote_addr.port = port;
return add_source(&remote_addr, name, type, params, INVALID_POOL, if (family != IPADDR_UNSPEC && family != remote_addr.ip_addr.family)
return NSR_InvalidAF;
return add_source(&remote_addr, name, IPADDR_UNSPEC, type, params, INVALID_POOL,
get_next_conf_id(conf_id)); get_next_conf_id(conf_id));
} }
@ -799,6 +810,7 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
us = MallocNew(struct UnresolvedSource); us = MallocNew(struct UnresolvedSource);
us->name = Strdup(name); us->name = Strdup(name);
us->family = family;
us->random_order = 0; us->random_order = 0;
us->refreshment = 0; us->refreshment = 0;
@ -835,7 +847,7 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
for (i = 0; i < new_sources; i++) { for (i = 0; i < new_sources; i++) {
if (i > 0) if (i > 0)
remote_addr.ip_addr.addr.id = ++last_address_id; remote_addr.ip_addr.addr.id = ++last_address_id;
if (add_source(&remote_addr, name, type, params, us->pool_id, cid) != NSR_Success) if (add_source(&remote_addr, name, family, type, params, us->pool_id, cid) != NSR_Success)
return NSR_TooManySources; return NSR_TooManySources;
} }
@ -1026,6 +1038,7 @@ resolve_source_replacement(SourceRecord *record, int refreshment)
us = MallocNew(struct UnresolvedSource); us = MallocNew(struct UnresolvedSource);
us->name = Strdup(record->name); us->name = Strdup(record->name);
us->family = record->family;
/* Ignore the order of addresses from the resolver to not get /* Ignore the order of addresses from the resolver to not get
stuck with a pair of unreachable or otherwise unusable servers stuck with a pair of unreachable or otherwise unusable servers
(e.g. falsetickers) in case the order doesn't change, or a group (e.g. falsetickers) in case the order doesn't change, or a group

View file

@ -55,9 +55,12 @@ extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type
/* Procedure to add a new server, peer source, or pool of servers specified by /* Procedure to add a new server, peer source, or pool of servers specified by
name instead of address. The name is resolved in exponentially increasing name instead of address. The name is resolved in exponentially increasing
intervals until it succeeds or fails with a non-temporary error. If the intervals until it succeeds or fails with a non-temporary error. The
name is an address, it is equivalent to NSR_AddSource(). */ specified family filters resolved addresses. If the name is an address
extern NSR_Status NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, and its family does not conflict with the specified family, it is equivalent
to NSR_AddSource(). */
extern NSR_Status NSR_AddSourceByName(char *name, int family, int port, int pool,
NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id); SourceParameters *params, uint32_t *conf_id);
extern const char *NSR_StatusToString(NSR_Status status); extern const char *NSR_StatusToString(NSR_Status status);

View file

@ -201,7 +201,7 @@ NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
} }
NSR_Status NSR_Status
NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, NSR_AddSourceByName(char *name, int family, int port, int pool, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id) SourceParameters *params, uint32_t *conf_id)
{ {
return NSR_TooManySources; return NSR_TooManySources;

View file

@ -125,7 +125,7 @@ void
test_unit(void) test_unit(void)
{ {
char source_line[] = "127.0.0.1 offline", conf[] = "port 0", name[64]; char source_line[] = "127.0.0.1 offline", conf[] = "port 0", name[64];
int i, j, k, slot, found, pool, prev_n; int i, j, k, family, slot, found, pool, prev_n;
uint32_t hash = 0, conf_id; uint32_t hash = 0, conf_id;
NTP_Remote_Address addrs[256], addr; NTP_Remote_Address addrs[256], addr;
NTP_Local_Address local_addr; NTP_Local_Address local_addr;
@ -216,7 +216,7 @@ test_unit(void)
TEST_CHECK(n_sources == 0); TEST_CHECK(n_sources == 0);
status = NSR_AddSourceByName("a b", 0, 0, 0, &source.params, &conf_id); status = NSR_AddSourceByName("a b", IPADDR_UNSPEC, 0, 0, 0, &source.params, &conf_id);
TEST_CHECK(status == NSR_InvalidName); TEST_CHECK(status == NSR_InvalidName);
local_addr.ip_addr.family = IPADDR_INET4; local_addr.ip_addr.family = IPADDR_INET4;
@ -228,11 +228,13 @@ test_unit(void)
for (i = 0; i < 500; i++) { for (i = 0; i < 500; i++) {
for (j = 0; j < 20; j++) { for (j = 0; j < 20; j++) {
snprintf(name, sizeof (name), "ntp%d.example.net", (int)(random() % 10)); snprintf(name, sizeof (name), "ntp%d.example.net", (int)(random() % 10));
family = random() % 2 ? IPADDR_UNSPEC : random() % 2 ? IPADDR_INET4 : IPADDR_INET6;
pool = random() % 2; pool = random() % 2;
prev_n = n_sources; prev_n = n_sources;
DEBUG_LOG("%d/%d adding source %s pool=%d", i, j, name, pool); DEBUG_LOG("%d/%d adding source %s pool=%d", i, j, name, pool);
status = NSR_AddSourceByName(name, 0, pool, random() % 2 ? NTP_SERVER : NTP_PEER, status = NSR_AddSourceByName(name, family, 0, pool,
random() % 2 ? NTP_SERVER : NTP_PEER,
&source.params, &conf_id); &source.params, &conf_id);
TEST_CHECK(status == NSR_UnresolvedName); TEST_CHECK(status == NSR_UnresolvedName);
@ -242,11 +244,13 @@ test_unit(void)
for (us = unresolved_sources; us->next; us = us->next) for (us = unresolved_sources; us->next; us = us->next)
; ;
TEST_CHECK(strcmp(us->name, name) == 0); TEST_CHECK(strcmp(us->name, name) == 0);
TEST_CHECK(us->family == family);
if (pool) { if (pool) {
TEST_CHECK(us->address.ip_addr.family == IPADDR_UNSPEC && us->pool_id >= 0); TEST_CHECK(us->address.ip_addr.family == IPADDR_UNSPEC && us->pool_id >= 0);
} else { } else {
TEST_CHECK(strcmp(NSR_GetName(&us->address.ip_addr), name) == 0); TEST_CHECK(strcmp(NSR_GetName(&us->address.ip_addr), name) == 0);
TEST_CHECK(find_slot2(&us->address, &slot) == 2); TEST_CHECK(find_slot2(&us->address, &slot) == 2);
TEST_CHECK(get_record(slot)->family == family);
} }
if (random() % 2) { if (random() % 2) {