Some time ago (it has been years actually), Otto instrumented malloc(3) to see where unwind is using a lot of memory when it's just sitting there. One of the remaining areas is struct config_file with its member outgoing_avail_ports:
if(!(cfg->outgoing_avail_ports = (int*)calloc(65536, sizeof(int)))) goto error_exit; That's a 256k just wasted on OpenBSD. Some time ago I added and upstreamed --disable-explicit-port-randomisation to unbound(8) so that it doesn't need to try to find a random outgoing port on its own. On OpenBSD we can just trust the kernel to do this for us. Apparently I left that struct behind which is part of the userland machinery in unbound. Especially in unwind this burns a lot of memory because this is allocated per libunbound context. Without a config file memory usage goes down from 10M to 6.7M. This is a diff to get rid of outgoing_avail_ports in unwind and unbound. I'll try to upstream it. unbound tests would be very much appreciated. Comments, OKs? diff --git sbin/unwind/libunbound/libunbound/libworker.c sbin/unwind/libunbound/libunbound/libworker.c index 11bf5f9db55..941a3d660c8 100644 --- sbin/unwind/libunbound/libunbound/libworker.c +++ sbin/unwind/libunbound/libunbound/libworker.c @@ -225,6 +225,7 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb) if(!w->is_bg || w->is_bg_thread) { lock_basic_lock(&ctx->cfglock); } +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION numports = cfg_condense_ports(cfg, &ports); if(numports == 0) { if(!w->is_bg || w->is_bg_thread) { @@ -233,6 +234,10 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb) libworker_delete(w); return NULL; } +#else + numports = 1; + ports = NULL; +#endif w->back = outside_network_create(w->base, cfg->msg_buffer_size, (size_t)cfg->outgoing_num_ports, cfg->out_ifs, cfg->num_out_ifs, cfg->do_ip4, cfg->do_ip6, diff --git sbin/unwind/libunbound/util/config_file.c sbin/unwind/libunbound/util/config_file.c index d7bd37a8890..f3713792a25 100644 --- sbin/unwind/libunbound/util/config_file.c +++ sbin/unwind/libunbound/util/config_file.c @@ -84,8 +84,10 @@ size_t http2_response_buffer_max = 4 * 1024 * 1024; /** global config during parsing */ struct config_parser_state* cfg_parser = 0; +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION /** init ports possible for use */ static void init_outgoing_availports(int* array, int num); +#endif struct config_file* config_create(void) @@ -176,9 +178,11 @@ config_create(void) cfg->infra_keep_probing = 0; cfg->delay_close = 0; cfg->udp_connect = 1; +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION if(!(cfg->outgoing_avail_ports = (int*)calloc(65536, sizeof(int)))) goto error_exit; init_outgoing_availports(cfg->outgoing_avail_ports, 65536); +#endif if(!(cfg->username = strdup(UB_USERNAME))) goto error_exit; #ifdef HAVE_CHROOT if(!(cfg->chrootdir = strdup(CHROOT_DIR))) goto error_exit; @@ -482,12 +486,14 @@ int config_set_option(struct config_file* cfg, const char* opt, } else if(strcmp(opt, "num-threads:") == 0) { /* not supported, library must have 1 thread in bgworker */ return 0; +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION } else if(strcmp(opt, "outgoing-port-permit:") == 0) { return cfg_mark_ports(val, 1, cfg->outgoing_avail_ports, 65536); } else if(strcmp(opt, "outgoing-port-avoid:") == 0) { return cfg_mark_ports(val, 0, cfg->outgoing_avail_ports, 65536); +#endif } else if(strcmp(opt, "local-zone:") == 0) { return cfg_parse_local_zone(cfg, val); } else if(strcmp(opt, "val-override-date:") == 0) { @@ -1577,7 +1583,9 @@ config_delete(struct config_file* cfg) free(cfg->nsid_cfg_str); free(cfg->nsid); free(cfg->module_conf); +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION free(cfg->outgoing_avail_ports); +#endif config_delstrlist(cfg->caps_whitelist); config_delstrlist(cfg->private_address); config_delstrlist(cfg->private_domain); @@ -1640,6 +1648,7 @@ config_delete(struct config_file* cfg) free(cfg); } +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION static void init_outgoing_availports(int* a, int num) { @@ -1668,11 +1677,7 @@ int cfg_mark_ports(const char* str, int allow, int* avail, int num) { char* mid = strchr(str, '-'); -#ifdef DISABLE_EXPLICIT_PORT_RANDOMISATION - log_warn("Explicit port randomisation disabled, ignoring " - "outgoing-port-permit and outgoing-port-avoid configuration " - "options"); -#endif + if(!mid) { int port = atoi(str); if(port == 0 && strcmp(str, "0") != 0) { @@ -1738,6 +1743,7 @@ int cfg_condense_ports(struct config_file* cfg, int** avail) log_assert(at == num); return num; } +#endif void cfg_apply_local_port_policy(struct config_file* cfg, int num) { (void)cfg; diff --git sbin/unwind/libunbound/util/config_file.h sbin/unwind/libunbound/util/config_file.h index 0b457e3476b..136a0fd73fd 100644 --- sbin/unwind/libunbound/util/config_file.h +++ sbin/unwind/libunbound/util/config_file.h @@ -159,8 +159,10 @@ struct config_file { size_t outgoing_num_tcp; /** number of incoming tcp buffers per (per thread) */ size_t incoming_num_tcp; +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION /** allowed udp port numbers, array with 0 if not allowed */ int* outgoing_avail_ports; +#endif /** EDNS buffer size to use */ size_t edns_buffer_size; diff --git sbin/unwind/libunbound/util/configparser.y sbin/unwind/libunbound/util/configparser.y index c003f335839..da0bbd3d94c 100644 --- sbin/unwind/libunbound/util/configparser.y +++ sbin/unwind/libunbound/util/configparser.y @@ -760,18 +760,28 @@ server_outgoing_range: VAR_OUTGOING_RANGE STRING_ARG server_outgoing_port_permit: VAR_OUTGOING_PORT_PERMIT STRING_ARG { OUTYY(("P(server_outgoing_port_permit:%s)\n", $2)); +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION if(!cfg_mark_ports($2, 1, cfg_parser->cfg->outgoing_avail_ports, 65536)) yyerror("port number or range (\"low-high\") expected"); +#else + log_warn("option outgoing-port-permit ignored: Explicit port " + "randomisation disabled"); +#endif free($2); } ; server_outgoing_port_avoid: VAR_OUTGOING_PORT_AVOID STRING_ARG { OUTYY(("P(server_outgoing_port_avoid:%s)\n", $2)); +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION if(!cfg_mark_ports($2, 0, cfg_parser->cfg->outgoing_avail_ports, 65536)) yyerror("port number or range (\"low-high\") expected"); +#else + log_warn("option outgoing-port-avoid ignored: Explicit port " + "randomisation disabled"); +#endif free($2); } ; diff --git usr.sbin/unbound/daemon/daemon.c usr.sbin/unbound/daemon/daemon.c index 0e3923b4e9f..0051961647c 100644 --- usr.sbin/unbound/daemon/daemon.c +++ usr.sbin/unbound/daemon/daemon.c @@ -402,6 +402,7 @@ static void daemon_setup_modules(struct daemon* daemon) log_edns_known_options(VERB_ALGO, daemon->env); } +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION /** * Obtain allowed port numbers, concatenate the list, and shuffle them * (ready to be handed out to threads). @@ -432,6 +433,7 @@ static int daemon_get_shufport(struct daemon* daemon, int* shufport) } return avail; } +#endif /** * Allocate empty worker structures. With backptr and thread-number, @@ -452,12 +454,16 @@ daemon_create_workers(struct daemon* daemon) fatal_exit("could not init random generator"); hash_set_raninit((uint32_t)ub_random(daemon->rand)); } +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION shufport = (int*)calloc(65536, sizeof(int)); if(!shufport) fatal_exit("out of memory during daemon init"); numport = daemon_get_shufport(daemon, shufport); verbose(VERB_ALGO, "total of %d outgoing ports available", numport); - +#else + numport = 1; + shufport = NULL; +#endif daemon->num = (daemon->cfg->num_threads?daemon->cfg->num_threads:1); if(daemon->reuseport && (int)daemon->num < (int)daemon->num_ports) { log_warn("cannot reduce num-threads to %d because so-reuseport " diff --git usr.sbin/unbound/daemon/worker.c usr.sbin/unbound/daemon/worker.c index bf8c5d6b676..b8c645898b0 100644 --- usr.sbin/unbound/daemon/worker.c +++ usr.sbin/unbound/daemon/worker.c @@ -1872,11 +1872,15 @@ worker_create(struct daemon* daemon, int id, int* ports, int n) if(!worker) return NULL; worker->numports = n; +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION worker->ports = (int*)memdup(ports, sizeof(int)*n); if(!worker->ports) { free(worker); return NULL; } +#else + worker->ports = NULL; +#endif worker->daemon = daemon; worker->thread_num = id; if(!(worker->cmd = tube_create())) { diff --git usr.sbin/unbound/libunbound/libworker.c usr.sbin/unbound/libunbound/libworker.c index 11bf5f9db55..941a3d660c8 100644 --- usr.sbin/unbound/libunbound/libworker.c +++ usr.sbin/unbound/libunbound/libworker.c @@ -225,6 +225,7 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb) if(!w->is_bg || w->is_bg_thread) { lock_basic_lock(&ctx->cfglock); } +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION numports = cfg_condense_ports(cfg, &ports); if(numports == 0) { if(!w->is_bg || w->is_bg_thread) { @@ -233,6 +234,10 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb) libworker_delete(w); return NULL; } +#else + numports = 1; + ports = NULL; +#endif w->back = outside_network_create(w->base, cfg->msg_buffer_size, (size_t)cfg->outgoing_num_ports, cfg->out_ifs, cfg->num_out_ifs, cfg->do_ip4, cfg->do_ip6, diff --git usr.sbin/unbound/util/config_file.c usr.sbin/unbound/util/config_file.c index d7bd37a8890..f3713792a25 100644 --- usr.sbin/unbound/util/config_file.c +++ usr.sbin/unbound/util/config_file.c @@ -84,8 +84,10 @@ size_t http2_response_buffer_max = 4 * 1024 * 1024; /** global config during parsing */ struct config_parser_state* cfg_parser = 0; +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION /** init ports possible for use */ static void init_outgoing_availports(int* array, int num); +#endif struct config_file* config_create(void) @@ -176,9 +178,11 @@ config_create(void) cfg->infra_keep_probing = 0; cfg->delay_close = 0; cfg->udp_connect = 1; +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION if(!(cfg->outgoing_avail_ports = (int*)calloc(65536, sizeof(int)))) goto error_exit; init_outgoing_availports(cfg->outgoing_avail_ports, 65536); +#endif if(!(cfg->username = strdup(UB_USERNAME))) goto error_exit; #ifdef HAVE_CHROOT if(!(cfg->chrootdir = strdup(CHROOT_DIR))) goto error_exit; @@ -482,12 +486,14 @@ int config_set_option(struct config_file* cfg, const char* opt, } else if(strcmp(opt, "num-threads:") == 0) { /* not supported, library must have 1 thread in bgworker */ return 0; +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION } else if(strcmp(opt, "outgoing-port-permit:") == 0) { return cfg_mark_ports(val, 1, cfg->outgoing_avail_ports, 65536); } else if(strcmp(opt, "outgoing-port-avoid:") == 0) { return cfg_mark_ports(val, 0, cfg->outgoing_avail_ports, 65536); +#endif } else if(strcmp(opt, "local-zone:") == 0) { return cfg_parse_local_zone(cfg, val); } else if(strcmp(opt, "val-override-date:") == 0) { @@ -1577,7 +1583,9 @@ config_delete(struct config_file* cfg) free(cfg->nsid_cfg_str); free(cfg->nsid); free(cfg->module_conf); +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION free(cfg->outgoing_avail_ports); +#endif config_delstrlist(cfg->caps_whitelist); config_delstrlist(cfg->private_address); config_delstrlist(cfg->private_domain); @@ -1640,6 +1648,7 @@ config_delete(struct config_file* cfg) free(cfg); } +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION static void init_outgoing_availports(int* a, int num) { @@ -1668,11 +1677,7 @@ int cfg_mark_ports(const char* str, int allow, int* avail, int num) { char* mid = strchr(str, '-'); -#ifdef DISABLE_EXPLICIT_PORT_RANDOMISATION - log_warn("Explicit port randomisation disabled, ignoring " - "outgoing-port-permit and outgoing-port-avoid configuration " - "options"); -#endif + if(!mid) { int port = atoi(str); if(port == 0 && strcmp(str, "0") != 0) { @@ -1738,6 +1743,7 @@ int cfg_condense_ports(struct config_file* cfg, int** avail) log_assert(at == num); return num; } +#endif void cfg_apply_local_port_policy(struct config_file* cfg, int num) { (void)cfg; diff --git usr.sbin/unbound/util/config_file.h usr.sbin/unbound/util/config_file.h index 0b457e3476b..136a0fd73fd 100644 --- usr.sbin/unbound/util/config_file.h +++ usr.sbin/unbound/util/config_file.h @@ -159,8 +159,10 @@ struct config_file { size_t outgoing_num_tcp; /** number of incoming tcp buffers per (per thread) */ size_t incoming_num_tcp; +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION /** allowed udp port numbers, array with 0 if not allowed */ int* outgoing_avail_ports; +#endif /** EDNS buffer size to use */ size_t edns_buffer_size; diff --git usr.sbin/unbound/util/configparser.y usr.sbin/unbound/util/configparser.y index c003f335839..da0bbd3d94c 100644 --- usr.sbin/unbound/util/configparser.y +++ usr.sbin/unbound/util/configparser.y @@ -760,18 +760,28 @@ server_outgoing_range: VAR_OUTGOING_RANGE STRING_ARG server_outgoing_port_permit: VAR_OUTGOING_PORT_PERMIT STRING_ARG { OUTYY(("P(server_outgoing_port_permit:%s)\n", $2)); +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION if(!cfg_mark_ports($2, 1, cfg_parser->cfg->outgoing_avail_ports, 65536)) yyerror("port number or range (\"low-high\") expected"); +#else + log_warn("option outgoing-port-permit ignored: Explicit port " + "randomisation disabled"); +#endif free($2); } ; server_outgoing_port_avoid: VAR_OUTGOING_PORT_AVOID STRING_ARG { OUTYY(("P(server_outgoing_port_avoid:%s)\n", $2)); +#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION if(!cfg_mark_ports($2, 0, cfg_parser->cfg->outgoing_avail_ports, 65536)) yyerror("port number or range (\"low-high\") expected"); +#else + log_warn("option outgoing-port-avoid ignored: Explicit port " + "randomisation disabled"); +#endif free($2); } ; -- I'm not entirely sure you are real.