When the per-thread client trie has a cached entry from a broad
subnet (e.g. /8), it shadows more specific client definitions
(e.g. /24) for subsequent connections. This causes the wrong
shared secret to be used, breaking packet authentication (RADIUS)
or decryption (TACACS+).
After the trie lookup returns a cached client, verify it against
the global client list. If a more specific match exists, discard
the cached result so the existing code path does a fresh lookup
and caches the correct client.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
&address.socket.inet.src_ipaddr.addr, address.socket.inet.src_ipaddr.prefix);
fr_assert(!client || !client->connection);
+ /*
+ * Verify the cached client is the most specific match.
+ * A broader subnet may have been cached first, shadowing
+ * a more specific client definition.
+ */
+ if (client && (client->state == PR_CLIENT_STATIC)) {
+ fr_client_t *radclient;
+
+ radclient = inst->app_io->client_find(thread->child,
+ &address.socket.inet.src_ipaddr, inst->ipproto);
+ if (radclient && (radclient->ipaddr.prefix > client->src_ipaddr.prefix)) {
+ client = NULL;
+ }
+ }
+
} else {
client = connection->client;