Add decreasing TTLs with jitter when a TTL becomes low

Fixes #33
pull/34/head
Frank Denis 4 years ago
parent 4a7f6abe07
commit dd1b550ef9

@ -83,6 +83,13 @@ cache_ttl_max = 86400
cache_ttl_error = 600
## DNS cache: to avoid bursts of traffic when an RRSET expires,
## introduce jitter to the decreasing TTL sent to clients when the
## actual TTL goes below that value.
client_ttl_jitter = 60
## Run as a background process
daemonize = false

@ -30,6 +30,10 @@ impl CachedResponse {
pub fn has_expired(&self) -> bool {
Instant::recent() > self.expiry
}
pub fn ttl(&self) -> u32 {
(self.expiry - Instant::recent()).as_secs() as _
}
}
#[derive(Clone, Derivative)]
@ -37,9 +41,9 @@ impl CachedResponse {
pub struct Cache {
#[derivative(Debug = "ignore")]
cache: Arc<Mutex<ClockProCache<u128, CachedResponse>>>,
ttl_min: u32,
ttl_max: u32,
ttl_error: u32,
pub ttl_min: u32,
pub ttl_max: u32,
pub ttl_error: u32,
}
impl Cache {

@ -82,6 +82,7 @@ pub struct Config {
pub pid_file: Option<PathBuf>,
pub log_file: Option<PathBuf>,
pub my_ip: Option<String>,
pub client_ttl_jitter: Option<u32>,
#[cfg(feature = "metrics")]
pub metrics: Option<MetricsConfig>,
pub anonymized_dns: Option<AnonymizedDNSConfig>,

@ -381,6 +381,28 @@ pub fn min_ttl(packet: &[u8], min_ttl: u32, max_ttl: u32, failure_ttl: u32) -> R
Ok(found_min_ttl)
}
pub fn set_ttl(packet: &mut [u8], ttl: u32) -> Result<(), Error> {
let packet_len = packet.len();
ensure!(packet_len > DNS_OFFSET_QUESTION, "Short packet");
ensure!(packet_len <= DNS_MAX_PACKET_SIZE, "Large packet");
ensure!(qdcount(packet) == 1, "No question");
let mut offset = skip_name(packet, DNS_OFFSET_QUESTION)?;
assert!(offset > DNS_OFFSET_QUESTION);
ensure!(packet_len - offset > 4, "Short packet");
offset += 4;
let (ancount, nscount, arcount) = (ancount(packet), nscount(packet), arcount(packet));
let rrcount = ancount as usize + nscount as usize + arcount as usize;
offset = traverse_rrs_mut(packet, offset, rrcount, |packet, offset| {
let qtype = BigEndian::read_u16(&packet[offset..]);
if qtype != DNS_TYPE_OPT {
BigEndian::write_u32(&mut packet[offset + 4..], ttl)
}
Ok(())
})?;
ensure!(packet_len == offset, "Garbage after packet");
Ok(())
}
fn add_edns_section(packet: &mut Vec<u8>, max_payload_size: u16) -> Result<(), Error> {
let opt_rr: [u8; 11] = [
0,

@ -49,6 +49,7 @@ pub struct Globals {
pub anonymized_dns_allow_non_reserved_ports: bool,
pub anonymized_dns_blacklisted_ips: Vec<IpAddr>,
pub access_control_tokens: Option<Vec<String>>,
pub client_ttl_jitter: u32,
pub my_ip: Option<Vec<u8>>,
#[cfg(feature = "metrics")]
#[derivative(Debug = "ignore")]

@ -705,6 +705,7 @@ fn main() -> Result<(), Error> {
anonymized_dns_blacklisted_ips,
access_control_tokens,
my_ip: config.my_ip.map(|ip| ip.as_bytes().to_ascii_lowercase()),
client_ttl_jitter: config.client_ttl_jitter.unwrap_or(60),
#[cfg(feature = "metrics")]
varz: Varz::default(),
});

@ -226,7 +226,13 @@ pub async fn get_cached_response_or_resolve(
#[cfg(feature = "metrics")]
globals.varz.client_queries_cached.inc();
cached_response.set_tid(original_tid);
let mut ttl = cached_response.ttl();
if ttl < globals.client_ttl_jitter {
let jitter = rand::thread_rng().gen::<u32>() % globals.client_ttl_jitter;
ttl = globals.client_ttl_jitter.saturating_add(jitter);
}
let mut response = cached_response.into_response();
dns::set_ttl(&mut response, ttl)?;
dns::recase_qname(&mut response, &packet_qname)?;
return Ok(response);
}

Loading…
Cancel
Save