YAMAGUCHI Takanori
2016-Jun-16 08:29 UTC
[nsd-users] Patch: {max,min}-{refresh,retry}-time
Hi, I wrote a patch to limit too long or too short SOA refresh and retry parameters. This allows the slave server to override the definition of SOA RR. Changes are welcome. Regards. -------------- next part -------------- diff --git a/configlexer.lex b/configlexer.lex index 113fa22..03903de 100644 --- a/configlexer.lex +++ b/configlexer.lex @@ -268,6 +268,10 @@ zonefiles-check{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILES_CHECK; zonefiles-write{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILES_WRITE;} log-time-ascii{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_LOG_TIME_ASCII;} round-robin{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ROUND_ROBIN;} +max-refresh-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MAX_REFRESH_TIME;} +min-refresh-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MIN_REFRESH_TIME;} +max-retry-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MAX_RETRY_TIME;} +min-retry-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MIN_RETRY_TIME;} {NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;} /* Quoted strings. Strip leading and ending quotes */ diff --git a/configparser.y b/configparser.y index 7ebf782..e0f9c15 100644 --- a/configparser.y +++ b/configparser.y @@ -69,6 +69,8 @@ extern config_parser_state_t* cfg_parser; %token VAR_RRL_WHITELIST_RATELIMIT VAR_RRL_WHITELIST %token VAR_ZONEFILES_CHECK VAR_ZONEFILES_WRITE VAR_LOG_TIME_ASCII %token VAR_ROUND_ROBIN VAR_ZONESTATS VAR_REUSEPORT VAR_VERSION +%token VAR_MAX_REFRESH_TIME VAR_MIN_REFRESH_TIME +%token VAR_MAX_RETRY_TIME VAR_MIN_RETRY_TIME %% toplevelvars: /* empty */ | toplevelvars toplevelvar ; @@ -598,7 +600,8 @@ content_pattern: pattern_name | zone_config_item; zone_config_item: zone_zonefile | zone_allow_notify | zone_request_xfr | zone_notify | zone_notify_retry | zone_provide_xfr | zone_outgoing_interface | zone_allow_axfr_fallback | include_pattern | - zone_rrl_whitelist | zone_zonestats; + zone_rrl_whitelist | zone_zonestats | zone_max_refresh_time | + zone_min_refresh_time | zone_max_retry_time | zone_min_retry_time; pattern_name: VAR_NAME STRING { OUTYY(("P(pattern_name:%s)\n", $2)); @@ -819,6 +822,46 @@ zone_rrl_whitelist: VAR_RRL_WHITELIST STRING #endif } ; +zone_max_refresh_time: VAR_MAX_REFRESH_TIME STRING +{ + OUTYY(("P(zone_max_refresh_time:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("number expected"); + else { + cfg_parser->current_pattern->max_refresh_time = atoi($2); + cfg_parser->current_pattern->max_refresh_time_is_default = 0; + } +}; +zone_min_refresh_time: VAR_MIN_REFRESH_TIME STRING +{ + OUTYY(("P(zone_min_refresh_time:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("number expected"); + else { + cfg_parser->current_pattern->min_refresh_time = atoi($2); + cfg_parser->current_pattern->min_refresh_time_is_default = 0; + } +}; +zone_max_retry_time: VAR_MAX_RETRY_TIME STRING +{ + OUTYY(("P(zone_max_retry_time:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("number expected"); + else { + cfg_parser->current_pattern->max_retry_time = atoi($2); + cfg_parser->current_pattern->max_retry_time_is_default = 0; + } +}; +zone_min_retry_time: VAR_MIN_RETRY_TIME STRING +{ + OUTYY(("P(zone_min_retry_time:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("number expected"); + else { + cfg_parser->current_pattern->min_retry_time = atoi($2); + cfg_parser->current_pattern->min_retry_time_is_default = 0; + } +}; /* key: declaration */ keystart: VAR_KEY diff --git a/nsd-checkconf.c b/nsd-checkconf.c index e5f669f..9525c56 100644 --- a/nsd-checkconf.c +++ b/nsd-checkconf.c @@ -59,6 +59,12 @@ extern int optind; return; \ } +#define ZONE_GET_INT(NAME, VAR, PATTERN) \ + if (strcasecmp(#NAME, (VAR)) == 0) { \ + printf("%d\n", (int) PATTERN->NAME); \ + return; \ + } + #define SERV_GET_BIN(NAME, VAR) \ if (strcasecmp(#NAME, (VAR)) == 0) { \ printf("%s\n", opt->NAME?"yes":"no"); \ @@ -306,6 +312,10 @@ config_print_zone(nsd_options_t* opt, const char* k, int s, const char *o, ZONE_GET_STR(zonestats, o, zone->pattern); ZONE_GET_OUTGOING(outgoing_interface, o, zone->pattern); ZONE_GET_BIN(allow_axfr_fallback, o, zone->pattern); + ZONE_GET_INT(max_refresh_time, o, zone->pattern); + ZONE_GET_INT(min_refresh_time, o, zone->pattern); + ZONE_GET_INT(max_retry_time, o, zone->pattern); + ZONE_GET_INT(min_retry_time, o, zone->pattern); #ifdef RATELIMIT ZONE_GET_RRL(rrl_whitelist, o, zone->pattern); #endif @@ -331,6 +341,10 @@ config_print_zone(nsd_options_t* opt, const char* k, int s, const char *o, ZONE_GET_STR(zonestats, o, p); ZONE_GET_OUTGOING(outgoing_interface, o, p); ZONE_GET_BIN(allow_axfr_fallback, o, p); + ZONE_GET_INT(max_refresh_time, o, p); + ZONE_GET_INT(min_refresh_time, o, p); + ZONE_GET_INT(max_retry_time, o, p); + ZONE_GET_INT(min_retry_time, o, p); #ifdef RATELIMIT ZONE_GET_RRL(rrl_whitelist, o, p); #endif @@ -431,6 +445,14 @@ static void print_zone_content_elems(pattern_options_t* pat) if(!pat->allow_axfr_fallback_is_default) printf("\tallow-axfr-fallback: %s\n", pat->allow_axfr_fallback?"yes":"no"); + if(!pat->max_refresh_time_is_default) + printf("\tmax-refresh-time: %d\n", pat->max_refresh_time); + if(!pat->min_refresh_time_is_default) + printf("\tmin-refresh-time: %d\n", pat->min_refresh_time); + if(!pat->max_retry_time_is_default) + printf("\tmax-retry-time: %d\n", pat->max_retry_time); + if(!pat->min_retry_time_is_default) + printf("\tmin-retry-time: %d\n", pat->min_retry_time); } void diff --git a/nsd.conf.sample.in b/nsd.conf.sample.in index 48eef14..a55816e 100644 --- a/nsd.conf.sample.in +++ b/nsd.conf.sample.in @@ -258,6 +258,11 @@ remote-control: # set local interface for sending zone transfer requests. # default is let the OS choose. #outgoing-interface: 10.0.0.10 + # limit the refresh and retry interval in seconds. + #max-refresh-time: 2419200 + #min-refresh-time: 300 + #max-retry-time: 1209600 + #min-retry-time: 500 # if compiled with --enable-zone-stats, give name of stat block for # this zone (or group of zones). Output from nsd-control stats. diff --git a/options.c b/options.c index eabc8bf..e211af9 100644 --- a/options.c +++ b/options.c @@ -822,6 +822,14 @@ pattern_options_create(region_type* region) p->allow_axfr_fallback_is_default = 1; p->implicit = 0; p->xfrd_flags = 0; + p->max_refresh_time = 2419200; /* 4 weeks */ + p->max_refresh_time_is_default = 1; + p->min_refresh_time = 300; + p->min_refresh_time_is_default = 1; + p->max_retry_time = 1209600; /* 2 weeks */ + p->max_retry_time_is_default = 1; + p->min_retry_time = 500; + p->min_retry_time_is_default = 1; #ifdef RATELIMIT p->rrl_whitelist = 0; #endif @@ -944,6 +952,14 @@ copy_pat_fixed(region_type* region, pattern_options_t* orig, if(p->zonestats) orig->zonestats = region_strdup(region, p->zonestats); else orig->zonestats = NULL; + orig->max_refresh_time = p->max_refresh_time; + orig->max_refresh_time_is_default = p->max_refresh_time_is_default; + orig->min_refresh_time = p->min_refresh_time; + orig->min_refresh_time_is_default = p->min_refresh_time_is_default; + orig->max_retry_time = p->max_retry_time; + orig->max_retry_time_is_default = p->max_retry_time_is_default; + orig->min_retry_time = p->min_retry_time; + orig->min_retry_time_is_default = p->min_retry_time_is_default; #ifdef RATELIMIT orig->rrl_whitelist = p->rrl_whitelist; #endif @@ -1017,6 +1033,18 @@ pattern_options_equal(pattern_options_t* p, pattern_options_t* q) if(!acl_list_equal(p->provide_xfr, q->provide_xfr)) return 0; if(!acl_list_equal(p->outgoing_interface, q->outgoing_interface)) return 0; + if(p->max_refresh_time != q->max_refresh_time) return 0; + if(!booleq(p->max_refresh_time_is_default, + q->max_refresh_time_is_default)) return 0; + if(p->min_refresh_time != q->min_refresh_time) return 0; + if(!booleq(p->min_refresh_time_is_default, + q->min_refresh_time_is_default)) return 0; + if(p->max_retry_time != q->max_retry_time) return 0; + if(!booleq(p->max_retry_time_is_default, + q->max_retry_time_is_default)) return 0; + if(p->min_retry_time != q->min_retry_time) return 0; + if(!booleq(p->min_retry_time_is_default, + q->min_retry_time_is_default)) return 0; #ifdef RATELIMIT if(p->rrl_whitelist != q->rrl_whitelist) return 0; #endif @@ -1054,6 +1082,19 @@ unmarshal_u16(struct buffer* b) #endif static void +marshal_u32(struct buffer* b, uint32_t v) +{ + buffer_reserve(b, 4); + buffer_write_u32(b, v); +} + +static uint32_t +unmarshal_u32(struct buffer* b) +{ + return buffer_read_u32(b); +} + +static void marshal_str(struct buffer* b, const char* s) { if(!s) marshal_u8(b, 0); @@ -1143,6 +1184,14 @@ pattern_options_marshal(struct buffer* b, pattern_options_t* p) marshal_acl_list(b, p->notify); marshal_acl_list(b, p->provide_xfr); marshal_acl_list(b, p->outgoing_interface); + marshal_u32(b, p->max_refresh_time); + marshal_u8(b, p->max_refresh_time_is_default); + marshal_u32(b, p->min_refresh_time); + marshal_u8(b, p->min_refresh_time_is_default); + marshal_u32(b, p->max_retry_time); + marshal_u8(b, p->max_retry_time_is_default); + marshal_u32(b, p->min_retry_time); + marshal_u8(b, p->min_retry_time_is_default); } pattern_options_t* @@ -1165,6 +1214,14 @@ pattern_options_unmarshal(region_type* r, struct buffer* b) p->notify = unmarshal_acl_list(r, b); p->provide_xfr = unmarshal_acl_list(r, b); p->outgoing_interface = unmarshal_acl_list(r, b); + p->max_refresh_time = unmarshal_u32(b); + p->max_refresh_time_is_default = unmarshal_u8(b); + p->min_refresh_time = unmarshal_u32(b); + p->min_refresh_time_is_default = unmarshal_u8(b); + p->max_retry_time = unmarshal_u32(b); + p->max_retry_time_is_default = unmarshal_u8(b); + p->min_retry_time = unmarshal_u32(b); + p->min_retry_time_is_default = unmarshal_u8(b); return p; } @@ -1875,6 +1932,22 @@ config_apply_pattern(const char* name) a->notify_retry = pat->notify_retry; a->notify_retry_is_default = 0; } + if(!pat->max_refresh_time_is_default) { + a->max_refresh_time = pat->max_refresh_time; + a->max_refresh_time_is_default = 0; + } + if(!pat->min_refresh_time_is_default) { + a->min_refresh_time = pat->min_refresh_time; + a->min_refresh_time_is_default = 0; + } + if(!pat->max_retry_time_is_default) { + a->max_retry_time = pat->max_retry_time; + a->max_retry_time_is_default = 0; + } + if(!pat->min_refresh_time_is_default) { + a->min_retry_time = pat->min_retry_time; + a->min_retry_time_is_default = 0; + } #ifdef RATELIMIT a->rrl_whitelist |= pat->rrl_whitelist; #endif diff --git a/options.h b/options.h index ceba624..3f500ed 100644 --- a/options.h +++ b/options.h @@ -154,6 +154,14 @@ struct pattern_options { uint8_t notify_retry_is_default; uint8_t implicit; /* pattern is implicit, part_of_config zone used */ uint8_t xfrd_flags; + uint32_t max_refresh_time; + uint8_t max_refresh_time_is_default; + uint32_t min_refresh_time; + uint8_t min_refresh_time_is_default; + uint32_t max_retry_time; + uint8_t max_retry_time_is_default; + uint32_t min_retry_time; + uint8_t min_retry_time_is_default; }; #define PATTERN_IMPLICIT_MARKER "_implicit_" diff --git a/xfrd-disk.c b/xfrd-disk.c index 3fa8630..b23a619 100644 --- a/xfrd-disk.c +++ b/xfrd-disk.c @@ -147,6 +147,7 @@ xfrd_read_state(struct xfrd_state* xfrd) uint32_t filetime = 0; uint32_t numzones, i; region_type *tempregion; + time_t soa_refresh; tempregion = region_create(xalloc, free); if(!tempregion) @@ -265,10 +266,15 @@ xfrd_read_state(struct xfrd_state* xfrd) * or there is a notification, * or there is a soa && current time is past refresh point */ + soa_refresh = ntohl(soa_disk_read.refresh); + if (soa_refresh > zone->zone_options->pattern->max_refresh_time) + soa_refresh = zone->zone_options->pattern->max_refresh_time; + else if (soa_refresh < zone->zone_options->pattern->min_refresh_time) + soa_refresh = zone->zone_options->pattern->min_refresh_time; if(timeout == 0 || soa_notified_acquired_read != 0 || (soa_disk_acquired_read != 0 && (uint32_t)xfrd_time() - soa_disk_acquired_read - > ntohl(soa_disk_read.refresh))) + > soa_refresh)) { zone->state = xfrd_zone_refreshing; xfrd_set_refresh_now(zone); diff --git a/xfrd.c b/xfrd.c index c2c75ed..2a55421 100644 --- a/xfrd.c +++ b/xfrd.c @@ -702,7 +702,12 @@ xfrd_set_timer_refresh(xfrd_zone_t* zone) return; } /* refresh or expire timeout, whichever is earlier */ - set_refresh = zone->soa_disk_acquired + ntohl(zone->soa_disk.refresh); + set_refresh = ntohl(zone->soa_disk.refresh); + if (set_refresh > zone->zone_options->pattern->max_refresh_time) + set_refresh = zone->zone_options->pattern->max_refresh_time; + else if (set_refresh < zone->zone_options->pattern->min_refresh_time) + set_refresh = zone->zone_options->pattern->min_refresh_time; + set_refresh += zone->soa_disk_acquired; set_expire = zone->soa_disk_acquired + ntohl(zone->soa_disk.expire); if(set_refresh < set_expire) set = set_refresh; @@ -719,6 +724,7 @@ xfrd_set_timer_refresh(xfrd_zone_t* zone) static void xfrd_set_timer_retry(xfrd_zone_t* zone) { + time_t set_retry; /* set timer for next retry or expire timeout if earlier. */ if(zone->soa_disk_acquired == 0) { /* if no information, use reasonable timeout */ @@ -743,10 +749,14 @@ xfrd_set_timer_retry(xfrd_zone_t* zone) xfrd_time() + (time_t)ntohl(zone->soa_disk.retry) < zone->soa_disk_acquired + (time_t)ntohl(zone->soa_disk.expire)) { - if(ntohl(zone->soa_disk.retry) < XFRD_LOWERBOUND_RETRY) - xfrd_set_timer(zone, XFRD_LOWERBOUND_RETRY); - else - xfrd_set_timer(zone, ntohl(zone->soa_disk.retry)); + set_retry = ntohl(zone->soa_disk.retry); + if(set_retry > zone->zone_options->pattern->max_retry_time) + set_retry = zone->zone_options->pattern->max_retry_time; + else if(set_retry < zone->zone_options->pattern->min_retry_time) + set_retry = zone->zone_options->pattern->min_retry_time; + if(set_retry < XFRD_LOWERBOUND_RETRY) + set_retry = XFRD_LOWERBOUND_RETRY; + xfrd_set_timer(zone, set_retry); } else { if(ntohl(zone->soa_disk.expire) < XFRD_LOWERBOUND_RETRY) xfrd_set_timer(zone, XFRD_LOWERBOUND_RETRY);
Hi, On 16/06/16 10:29, YAMAGUCHI Takanori wrote:> Hi, > > I wrote a patch to limit too long or too short SOA refresh and retry parameters. > This allows the slave server to override the definition of SOA RR. > Changes are welcome.Thank you for the patch. I have applied it to the code repository. I hope the options are useful. Fixed options.c if(!pat->min_[refresh/retry]_time_is_default) for typing error. Best regards, Wouter -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 819 bytes Desc: OpenPGP digital signature URL: <http://lists.nlnetlabs.nl/pipermail/nsd-users/attachments/20160616/b0574a87/attachment.bin>