Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package passt for openSUSE:Factory checked in at 2024-08-17 12:39:53 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/passt (Old) and /work/SRC/openSUSE:Factory/.passt.new.2698 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "passt" Sat Aug 17 12:39:53 2024 rev:13 rq:1194188 version:20240814.61c0b0d Changes: -------- --- /work/SRC/openSUSE:Factory/passt/passt.changes 2024-08-13 13:22:08.707499255 +0200 +++ /work/SRC/openSUSE:Factory/.passt.new.2698/passt.changes 2024-08-17 12:40:03.896570499 +0200 @@ -1,0 +2,20 @@ +Thu Aug 15 06:18:55 UTC 2024 - dcermak@suse.com + +- Update to version 20240814.61c0b0d: + * flow: Don't crash if guest attempts to connect to port 0 + * conf: Don't ignore -t and -u options after -D + * ndp.c: Turn NDP responder into more declarative implementation + * conf: Delay handling -D option until after addresses are configured + * Correct inaccurate comments on ip[46]_ctx::addr + * log: Don't prefix message with timestamp on --debug if it's a continuation + * conf: Stop parsing options at first non-option argument + * passt, util: Close any open file that the parent might have leaked + * nstool: Propagate SIGTERM to processes executed in the namespace + * nstool: Fix some trivial typos + * log: Avoid duplicate calls to logtime() + * log: Handle errors from clock_gettime() + * log: Correct formatting of timestamps + * util: Some corrections for timespec_diff_us + * conf, pasta: Make -g and -a skip route/addresses copy for matching IP version only + +------------------------------------------------------------------- Old: ---- passt-20240806.ee36266.tar.zst New: ---- passt-20240814.61c0b0d.tar.zst ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ passt.spec ++++++ --- /var/tmp/diff_new_pack.lxUoE3/_old 2024-08-17 12:40:04.360589787 +0200 +++ /var/tmp/diff_new_pack.lxUoE3/_new 2024-08-17 12:40:04.360589787 +0200 @@ -44,7 +44,7 @@ %endif Name: passt -Version: 20240806.ee36266 +Version: 20240814.61c0b0d Release: 0 Summary: User-mode networking daemons for virtual machines and namespaces License: GPL-2.0-or-later AND BSD-3-Clause ++++++ _service ++++++ --- /var/tmp/diff_new_pack.lxUoE3/_old 2024-08-17 12:40:04.392591116 +0200 +++ /var/tmp/diff_new_pack.lxUoE3/_new 2024-08-17 12:40:04.396591283 +0200 @@ -4,7 +4,7 @@ <param name="scm">git</param> <param name="changesgenerate">enable</param> <param name="versionformat">%cs.%h</param> - <param name="revision">2024_08_06.ee36266</param> + <param name="revision">2024_08_14.61c0b0d</param> </service> <service mode="manual" name="recompress"> <param name="file">*.tar</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.lxUoE3/_old 2024-08-17 12:40:04.420592280 +0200 +++ /var/tmp/diff_new_pack.lxUoE3/_new 2024-08-17 12:40:04.424592447 +0200 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://passt.top/passt</param> - <param name="changesrevision">ee36266a55478672ad2c5f4efbd6ca0bef3d37cd</param></service></servicedata> + <param name="changesrevision">61c0b0d0f199589c72bd53e267d797a8e4bf1478</param></service></servicedata> (No newline at EOF) ++++++ passt-20240806.ee36266.tar.zst -> passt-20240814.61c0b0d.tar.zst ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/passt-20240806.ee36266/conf.c new/passt-20240814.61c0b0d/conf.c --- old/passt-20240806.ee36266/conf.c 2024-08-06 15:03:48.000000000 +0200 +++ new/passt-20240814.61c0b0d/conf.c 2024-08-14 12:20:31.000000000 +0200 @@ -1235,16 +1235,17 @@ const char *logname = (c->mode == MODE_PASTA) ? "pasta" : "passt"; char userns[PATH_MAX] = { 0 }, netns[PATH_MAX] = { 0 }; bool copy_addrs_opt = false, copy_routes_opt = false; - struct in6_addr *dns6 = c->ip6.dns, dns6_tmp; - struct in_addr *dns4 = c->ip4.dns, dns4_tmp; enum fwd_ports_mode fwd_default = FWD_NONE; bool v4_only = false, v6_only = false; + struct in6_addr *dns6 = c->ip6.dns; + struct in_addr *dns4 = c->ip4.dns; struct fqdn *dnss = c->dns_search; unsigned int ifi4 = 0, ifi6 = 0; const char *logfile = NULL; const char *optstring; size_t logsize = 0; char *runas = NULL; + long fd_tap_opt; int name, ret; uid_t uid; gid_t gid; @@ -1252,14 +1253,15 @@ if (c->mode == MODE_PASTA) { c->no_dhcp_dns = c->no_dhcp_dns_search = 1; fwd_default = FWD_AUTO; - optstring = "dqfel:hF:I:p:P:m:a:n:M:g:i:o:D:S:46t:u:T:U:"; + optstring = "+dqfel:hF:I:p:P:m:a:n:M:g:i:o:D:S:46t:u:T:U:"; } else { - optstring = "dqfel:hs:F:p:P:m:a:n:M:g:i:o:D:S:461t:u:"; + optstring = "+dqfel:hs:F:p:P:m:a:n:M:g:i:o:D:S:461t:u:"; } c->tcp.fwd_in.mode = c->tcp.fwd_out.mode = FWD_UNSET; c->udp.fwd_in.mode = c->udp.fwd_out.mode = FWD_UNSET; + optind = 1; do { name = getopt_long(argc, argv, optstring, options, NULL); @@ -1379,14 +1381,16 @@ die("--no-copy-routes is for pasta mode only"); warn("--no-copy-routes will be dropped soon"); - c->no_copy_routes = copy_routes_opt = true; + c->ip4.no_copy_routes = c->ip6.no_copy_routes = true; + copy_routes_opt = true; break; case 19: if (c->mode != MODE_PASTA) die("--no-copy-addrs is for pasta mode only"); warn("--no-copy-addrs will be dropped soon"); - c->no_copy_addrs = copy_addrs_opt = true; + c->ip4.no_copy_addrs = c->ip6.no_copy_addrs = true; + copy_addrs_opt = true; break; case 20: if (c->mode != MODE_PASTA) @@ -1422,11 +1426,13 @@ break; case 'F': errno = 0; - c->fd_tap = strtol(optarg, NULL, 0); + fd_tap_opt = strtol(optarg, NULL, 0); - if (c->fd_tap < 0 || errno) + if (errno || + fd_tap_opt <= STDERR_FILENO || fd_tap_opt > INT_MAX) die("Invalid --fd: %s", optarg); + c->fd_tap = fd_tap_opt; c->one_off = true; *c->sock_path = 0; break; @@ -1465,23 +1471,26 @@ break; case 'a': - if (c->mode == MODE_PASTA) - c->no_copy_addrs = 1; - if (inet_pton(AF_INET6, optarg, &c->ip6.addr) && !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr) && !IN6_IS_ADDR_LOOPBACK(&c->ip6.addr) && !IN6_IS_ADDR_V4MAPPED(&c->ip6.addr) && !IN6_IS_ADDR_V4COMPAT(&c->ip6.addr) && - !IN6_IS_ADDR_MULTICAST(&c->ip6.addr)) + !IN6_IS_ADDR_MULTICAST(&c->ip6.addr)) { + if (c->mode == MODE_PASTA) + c->ip6.no_copy_addrs = true; break; + } if (inet_pton(AF_INET, optarg, &c->ip4.addr) && !IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr) && !IN4_IS_ADDR_BROADCAST(&c->ip4.addr) && !IN4_IS_ADDR_LOOPBACK(&c->ip4.addr) && - !IN4_IS_ADDR_MULTICAST(&c->ip4.addr)) + !IN4_IS_ADDR_MULTICAST(&c->ip4.addr)) { + if (c->mode == MODE_PASTA) + c->ip4.no_copy_addrs = true; break; + } die("Invalid address: %s", optarg); break; @@ -1495,19 +1504,22 @@ parse_mac(c->mac, optarg); break; case 'g': - if (c->mode == MODE_PASTA) - c->no_copy_routes = 1; - if (inet_pton(AF_INET6, optarg, &c->ip6.gw) && !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.gw) && - !IN6_IS_ADDR_LOOPBACK(&c->ip6.gw)) + !IN6_IS_ADDR_LOOPBACK(&c->ip6.gw)) { + if (c->mode == MODE_PASTA) + c->ip6.no_copy_routes = true; break; + } if (inet_pton(AF_INET, optarg, &c->ip4.gw) && !IN4_IS_ADDR_UNSPECIFIED(&c->ip4.gw) && !IN4_IS_ADDR_BROADCAST(&c->ip4.gw) && - !IN4_IS_ADDR_LOOPBACK(&c->ip4.gw)) + !IN4_IS_ADDR_LOOPBACK(&c->ip4.gw)) { + if (c->mode == MODE_PASTA) + c->ip4.no_copy_routes = true; break; + } die("Invalid gateway address: %s", optarg); break; @@ -1533,40 +1545,6 @@ die("Invalid or redundant outbound address: %s", optarg); break; - case 'D': - if (!strcmp(optarg, "none")) { - c->no_dns = 1; - - dns4 = &c->ip4.dns[0]; - memset(c->ip4.dns, 0, sizeof(c->ip4.dns)); - c->ip4.dns[0] = (struct in_addr){ 0 }; - c->ip4.dns_match = (struct in_addr){ 0 }; - c->ip4.dns_host = (struct in_addr){ 0 }; - - dns6 = &c->ip6.dns[0]; - memset(c->ip6.dns, 0, sizeof(c->ip6.dns)); - c->ip6.dns_match = (struct in6_addr){ 0 }; - c->ip6.dns_host = (struct in6_addr){ 0 }; - - break; - } - - c->no_dns = 0; - - if (dns4 - &c->ip4.dns[0] < ARRAY_SIZE(c->ip4.dns) && - inet_pton(AF_INET, optarg, &dns4_tmp)) { - add_dns4(c, &dns4_tmp, &dns4); - break; - } - - if (dns6 - &c->ip6.dns[0] < ARRAY_SIZE(c->ip6.dns) && - inet_pton(AF_INET6, optarg, &dns6_tmp)) { - add_dns6(c, &dns6_tmp, &dns6); - break; - } - - die("Cannot use DNS address %s", optarg); - break; case 'S': if (!strcmp(optarg, "none")) { c->no_dns_search = 1; @@ -1608,6 +1586,7 @@ case 'u': case 'T': case 'U': + case 'D': /* Handle these later, once addresses are configured */ break; case 'h': @@ -1665,16 +1644,55 @@ if (c->ifi6 && IN6_IS_ADDR_UNSPECIFIED(&c->ip6.gw)) c->no_map_gw = 1; - /* Inbound port options can be parsed now (after IPv4/IPv6 settings) */ + /* Inbound port options & DNS can be parsed now (after IPv4/IPv6 + * settings) + */ udp_portmap_clear(); optind = 1; do { name = getopt_long(argc, argv, optstring, options, NULL); - if (name == 't') + if (name == 't') { conf_ports(c, name, optarg, &c->tcp.fwd_in); - else if (name == 'u') + } else if (name == 'u') { conf_ports(c, name, optarg, &c->udp.fwd_in); + } else if (name == 'D') { + struct in6_addr dns6_tmp; + struct in_addr dns4_tmp; + + if (!strcmp(optarg, "none")) { + c->no_dns = 1; + + dns4 = &c->ip4.dns[0]; + memset(c->ip4.dns, 0, sizeof(c->ip4.dns)); + c->ip4.dns[0] = (struct in_addr){ 0 }; + c->ip4.dns_match = (struct in_addr){ 0 }; + c->ip4.dns_host = (struct in_addr){ 0 }; + + dns6 = &c->ip6.dns[0]; + memset(c->ip6.dns, 0, sizeof(c->ip6.dns)); + c->ip6.dns_match = (struct in6_addr){ 0 }; + c->ip6.dns_host = (struct in6_addr){ 0 }; + + continue; + } + + c->no_dns = 0; + + if (dns4 - &c->ip4.dns[0] < ARRAY_SIZE(c->ip4.dns) && + inet_pton(AF_INET, optarg, &dns4_tmp)) { + add_dns4(c, &dns4_tmp, &dns4); + continue; + } + + if (dns6 - &c->ip6.dns[0] < ARRAY_SIZE(c->ip6.dns) && + inet_pton(AF_INET6, optarg, &dns6_tmp)) { + add_dns6(c, &dns6_tmp, &dns6); + continue; + } + + die("Cannot use DNS address %s", optarg); + } } while (name != -1); if (c->mode == MODE_PASTA) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/passt-20240806.ee36266/flow.c new/passt-20240814.61c0b0d/flow.c --- old/passt-20240806.ee36266/flow.c 2024-08-06 15:03:48.000000000 +0200 +++ new/passt-20240814.61c0b0d/flow.c 2024-08-14 12:20:31.000000000 +0200 @@ -279,7 +279,8 @@ else type_or_state = FLOW_TYPE(f); - logmsg(true, pri, "Flow %u (%s): %s", flow_idx(f), type_or_state, msg); + logmsg(true, false, pri, + "Flow %u (%s): %s", flow_idx(f), type_or_state, msg); } /** @@ -560,12 +561,6 @@ { struct siphash_state state = SIPHASH_INIT(c->hash_secret); - /* For the hash table to work, we need complete endpoint information, - * and at least a forwarding port. - */ - ASSERT(pif != PIF_NONE && !inany_is_unspecified(&side->eaddr) && - side->eport != 0 && side->fport != 0); - inany_siphash_feed(&state, &side->faddr); inany_siphash_feed(&state, &side->eaddr); @@ -585,8 +580,16 @@ static uint64_t flow_sidx_hash(const struct ctx *c, flow_sidx_t sidx) { const struct flow_common *f = &flow_at_sidx(sidx)->f; - return flow_hash(c, FLOW_PROTO(f), - f->pif[sidx.sidei], &f->side[sidx.sidei]); + const struct flowside *side = &f->side[sidx.sidei]; + uint8_t pif = f->pif[sidx.sidei]; + + /* For the hash table to work, entries must have complete endpoint + * information, and at least a forwarding port. + */ + ASSERT(pif != PIF_NONE && !inany_is_unspecified(&side->eaddr) && + side->eport != 0 && side->fport != 0); + + return flow_hash(c, FLOW_PROTO(f), pif, side); } /** diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/passt-20240806.ee36266/isolation.c new/passt-20240814.61c0b0d/isolation.c --- old/passt-20240806.ee36266/isolation.c 2024-08-06 15:03:48.000000000 +0200 +++ new/passt-20240814.61c0b0d/isolation.c 2024-08-14 12:20:31.000000000 +0200 @@ -29,7 +29,8 @@ * * Executed immediately after startup, drops capabilities we don't * need at any point during execution (or which we gain back when we - * need by joining other namespaces). + * need by joining other namespaces), and closes any leaked file we + * might have inherited from the parent process. * * 2. isolate_user() * ================= @@ -166,14 +167,17 @@ } /** - * isolate_initial() - Early, config independent self isolation + * isolate_initial() - Early, mostly config independent self isolation + * @argc: Argument count + * @argv: Command line options: only --fd (if present) is relevant here * * Should: * - drop unneeded capabilities + * - close all open files except for standard streams and the one from --fd * Musn't: * - remove filesytem access (we need to access files during setup) */ -void isolate_initial(void) +void isolate_initial(int argc, char **argv) { uint64_t keep; @@ -207,6 +211,8 @@ keep |= BIT(CAP_SETFCAP) | BIT(CAP_SYS_PTRACE); drop_caps_ep_except(keep); + + close_open_files(argc, argv); } /** diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/passt-20240806.ee36266/isolation.h new/passt-20240814.61c0b0d/isolation.h --- old/passt-20240806.ee36266/isolation.h 2024-08-06 15:03:48.000000000 +0200 +++ new/passt-20240814.61c0b0d/isolation.h 2024-08-14 12:20:31.000000000 +0200 @@ -7,7 +7,7 @@ #ifndef ISOLATION_H #define ISOLATION_H -void isolate_initial(void); +void isolate_initial(int argc, char **argv); void isolate_user(uid_t uid, gid_t gid, bool use_userns, const char *userns, enum passt_modes mode); int isolate_prefork(const struct ctx *c); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/passt-20240806.ee36266/log.c new/passt-20240814.61c0b0d/log.c --- old/passt-20240806.ee36266/log.c 2024-08-06 15:03:48.000000000 +0200 +++ new/passt-20240814.61c0b0d/log.c 2024-08-14 12:20:31.000000000 +0200 @@ -46,14 +46,41 @@ bool log_conf_parsed; /* Logging options already parsed */ bool log_stderr = true; /* Not daemonised, no shell spawned */ +#define LL_STRLEN (sizeof("-9223372036854775808")) +#define LOGTIME_STRLEN (LL_STRLEN + 5) + /** - * logtime_fmt_and_arg() - Build format and arguments to print relative log time - * @x: Current timestamp + * logtime() - Get the current time for logging purposes + * @ts: Buffer into which to store the timestamp + * + * Return: pointer to @now, or NULL if there was an error retrieving the time */ -#define logtime_fmt_and_arg(x) \ - "%lli.%04lli", \ - (timespec_diff_us((x), &log_start) / 1000000LL), \ - (timespec_diff_us((x), &log_start) / 100LL) +const struct timespec *logtime(struct timespec *ts) +{ + if (clock_gettime(CLOCK_MONOTONIC, ts)) + return NULL; + return ts; +} + +/** + * logtime_fmt() - Format timestamp into a string for the log + * @buf: Buffer into which to format the time + * @size: Size of @buf + * @ts: Time to format (or NULL on error) + * + * Return: number of characters written to @buf (excluding \0) + */ +static int logtime_fmt(char *buf, size_t size, const struct timespec *ts) +{ + if (ts) { + int64_t delta = timespec_diff_us(ts, &log_start); + + return snprintf(buf, size, "%lli.%04lli", delta / 1000000LL, + (delta / 100LL) % 10000); + } + + return snprintf(buf, size, "<error>"); +} /* Prefixes for log file messages, indexed by priority */ const char *logfile_prefix[] = { @@ -85,7 +112,7 @@ return; n = snprintf(buf, BUFSIZ, "%s - log truncated at ", log_header); - n += snprintf(buf + n, BUFSIZ - n, logtime_fmt_and_arg(now)); + n += logtime_fmt(buf + n, BUFSIZ - n, now); /* Avoid partial lines by padding the header with spaces */ nl = memchr(buf + n + 1, '\n', BUFSIZ - n - 1); @@ -117,8 +144,7 @@ header_len = snprintf(buf, BUFSIZ, "%s - log truncated at ", log_header); - header_len += snprintf(buf + header_len, BUFSIZ - header_len, - logtime_fmt_and_arg(now)); + header_len += logtime_fmt(buf + header_len, BUFSIZ - header_len, now); if (lseek(fd, 0, SEEK_SET) == -1) return; @@ -199,19 +225,17 @@ * logfile_write() - Write entry to log file, trigger rotation if full * @newline: Append newline at the end of the message, if missing * @pri: Facility and level map, same as priority for vsyslog() + * @now: Timestamp * @format: Same as vsyslog() format * @ap: Same as vsyslog() ap */ -static void logfile_write(bool newline, int pri, const char *format, va_list ap) +static void logfile_write(bool newline, int pri, const struct timespec *now, + const char *format, va_list ap) { - struct timespec now; char buf[BUFSIZ]; int n; - if (clock_gettime(CLOCK_MONOTONIC, &now)) - return; - - n = snprintf(buf, BUFSIZ, logtime_fmt_and_arg(&now)); + n = logtime_fmt(buf, BUFSIZ, now); n += snprintf(buf + n, BUFSIZ - n, ": %s", logfile_prefix[pri]); n += vsnprintf(buf + n, BUFSIZ - n, format, ap); @@ -219,7 +243,7 @@ if (newline && format[strlen(format)] != '\n') n += snprintf(buf + n, BUFSIZ - n, "\n"); - if ((log_written + n >= log_size) && logfile_rotate(log_file, &now)) + if ((log_written + n >= log_size) && logfile_rotate(log_file, now)) return; if ((n = write(log_file, buf, n)) >= 0) @@ -229,19 +253,24 @@ /** * vlogmsg() - Print or send messages to log or output files as configured * @newline: Append newline at the end of the message, if missing + * @cont: Continuation of a previous message, on the same line * @pri: Facility and level map, same as priority for vsyslog() * @format: Message * @ap: Variable argument list */ -void vlogmsg(bool newline, int pri, const char *format, va_list ap) +void vlogmsg(bool newline, bool cont, int pri, const char *format, va_list ap) { bool debug_print = (log_mask & LOG_MASK(LOG_DEBUG)) && log_file == -1; - struct timespec tp; + const struct timespec *now; + struct timespec ts; + + now = logtime(&ts); + + if (debug_print && !cont) { + char timestr[LOGTIME_STRLEN]; - if (debug_print) { - clock_gettime(CLOCK_MONOTONIC, &tp); - fprintf(stderr, logtime_fmt_and_arg(&tp)); - fprintf(stderr, ": "); + logtime_fmt(timestr, sizeof(timestr), now); + fprintf(stderr, "%s: ", timestr); } if ((log_mask & LOG_MASK(LOG_PRI(pri))) || !log_conf_parsed) { @@ -249,7 +278,7 @@ va_copy(ap2, ap); /* Don't clobber ap, we need it again */ if (log_file != -1) - logfile_write(newline, pri, format, ap2); + logfile_write(newline, pri, now, format, ap2); else if (!(log_mask & LOG_MASK(LOG_DEBUG))) passt_vsyslog(newline, pri, format, ap2); @@ -267,15 +296,16 @@ /** * logmsg() - vlogmsg() wrapper for variable argument lists * @newline: Append newline at the end of the message, if missing + * @cont: Continuation of a previous message, on the same line * @pri: Facility and level map, same as priority for vsyslog() * @format: Message */ -void logmsg(bool newline, int pri, const char *format, ...) +void logmsg(bool newline, bool cont, int pri, const char *format, ...) { va_list ap; va_start(ap, format); - vlogmsg(newline, pri, format, ap); + vlogmsg(newline, cont, pri, format, ap); va_end(ap); } @@ -290,10 +320,10 @@ va_list ap; va_start(ap, format); - vlogmsg(false, pri, format, ap); + vlogmsg(false, false, pri, format, ap); va_end(ap); - logmsg(true, pri, ": %s", strerror(errno_copy)); + logmsg(true, true, pri, ": %s", strerror(errno_copy)); } /** diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/passt-20240806.ee36266/log.h new/passt-20240814.61c0b0d/log.h --- old/passt-20240806.ee36266/log.h 2024-08-06 15:03:48.000000000 +0200 +++ new/passt-20240814.61c0b0d/log.h 2024-08-14 12:20:31.000000000 +0200 @@ -13,21 +13,21 @@ #define LOGFILE_CUT_RATIO 30 /* When full, cut ~30% size */ #define LOGFILE_SIZE_MIN (5UL * MAX(BUFSIZ, PAGE_SIZE)) -void vlogmsg(bool newline, int pri, const char *format, va_list ap); -void logmsg(bool newline, int pri, const char *format, ...) - __attribute__((format(printf, 3, 4))); +void vlogmsg(bool newline, bool cont, int pri, const char *format, va_list ap); +void logmsg(bool newline, bool cont, int pri, const char *format, ...) + __attribute__((format(printf, 4, 5))); void logmsg_perror(int pri, const char *format, ...) __attribute__((format(printf, 2, 3))); -#define err(...) logmsg(true, LOG_ERR, __VA_ARGS__) -#define warn(...) logmsg(true, LOG_WARNING, __VA_ARGS__) -#define info(...) logmsg(true, LOG_INFO, __VA_ARGS__) -#define debug(...) logmsg(true, LOG_DEBUG, __VA_ARGS__) +#define err(...) logmsg(true, false, LOG_ERR, __VA_ARGS__) +#define warn(...) logmsg(true, false, LOG_WARNING, __VA_ARGS__) +#define info(...) logmsg(true, false, LOG_INFO, __VA_ARGS__) +#define debug(...) logmsg(true, false, LOG_DEBUG, __VA_ARGS__) -#define err_perror(...) logmsg_perror( LOG_ERR, __VA_ARGS__) -#define warn_perror(...) logmsg_perror( LOG_WARNING, __VA_ARGS__) -#define info_perror(...) logmsg_perror( LOG_INFO, __VA_ARGS__) -#define debug_perror(...) logmsg_perror( LOG_DEBUG, __VA_ARGS__) +#define err_perror(...) logmsg_perror( LOG_ERR, __VA_ARGS__) +#define warn_perror(...) logmsg_perror( LOG_WARNING, __VA_ARGS__) +#define info_perror(...) logmsg_perror( LOG_INFO, __VA_ARGS__) +#define debug_perror(...) logmsg_perror( LOG_DEBUG, __VA_ARGS__) #define die(...) \ do { \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/passt-20240806.ee36266/ndp.c new/passt-20240814.61c0b0d/ndp.c --- old/passt-20240806.ee36266/ndp.c 2024-08-06 15:03:48.000000000 +0200 +++ new/passt-20240814.61c0b0d/ndp.c 2024-08-14 12:20:31.000000000 +0200 @@ -38,22 +38,193 @@ #define NS 135 #define NA 136 +enum ndp_option_types { + OPT_SRC_L2_ADDR = 1, + OPT_TARGET_L2_ADDR = 2, + OPT_PREFIX_INFO = 3, + OPT_MTU = 5, + OPT_RDNSS_TYPE = 25, + OPT_DNSSL_TYPE = 31, +}; + +/** + * struct opt_header - Option header + * @type: Option type + * @len: Option length, in units of 8 bytes +*/ +struct opt_header { + uint8_t type; + uint8_t len; +} __attribute__((packed)); + +/** + * struct opt_l2_addr - Link-layer address + * @header: Option header + * @mac: MAC address + */ +struct opt_l2_addr { + struct opt_header header; + unsigned char mac[ETH_ALEN]; +} __attribute__((packed)); + +/** + * struct ndp_na - NDP Neighbor Advertisement (NA) message + * @ih: ICMPv6 header + * @target_addr: Target IPv6 address + * @target_l2_addr: Target link-layer address + */ +struct ndp_na { + struct icmp6hdr ih; + struct in6_addr target_addr; + struct opt_l2_addr target_l2_addr; +} __attribute__((packed)); + +/** + * struct opt_prefix_info - Prefix Information option + * @header: Option header + * @prefix_len: The number of leading bits in the Prefix that are valid + * @prefix_flags: Flags associated with the prefix + * @valid_lifetime: Valid lifetime (ms) + * @pref_lifetime: Preferred lifetime (ms) + * @reserved: Unused + */ +struct opt_prefix_info { + struct opt_header header; + uint8_t prefix_len; + uint8_t prefix_flags; + uint32_t valid_lifetime; + uint32_t pref_lifetime; + uint32_t reserved; +} __attribute__((packed)); + +/** + * struct opt_mtu - Maximum transmission unit (MTU) option + * @header: Option header + * @reserved: Unused + * @value: MTU value, network order + */ +struct opt_mtu { + struct opt_header header; + uint16_t reserved; + uint32_t value; +} __attribute__((packed)); + +/** + * struct rdnss - Recursive DNS Server (RDNSS) option + * @header: Option header + * @reserved: Unused + * @lifetime: Validity time (s) + * @dns: List of DNS server addresses + */ +struct opt_rdnss { + struct opt_header header; + uint16_t reserved; + uint32_t lifetime; + struct in6_addr dns[MAXNS + 1]; +} __attribute__((packed)); + +/** + * struct dnssl - DNS Search List (DNSSL) option + * @header: Option header + * @reserved: Unused + * @lifetime: Validity time (s) + * @domains: List of NULL-seperated search domains + */ +struct opt_dnssl { + struct opt_header header; + uint16_t reserved; + uint32_t lifetime; + unsigned char domains[MAXDNSRCH * NS_MAXDNAME]; +} __attribute__((packed)); + +/** + * struct ndp_ra - NDP Router Advertisement (RA) message + * @ih: ICMPv6 header + * @reachable: Reachability time, after confirmation (ms) + * @retrans: Time between retransmitted NS messages (ms) + * @prefix_info: Prefix Information option + * @prefix: IPv6 prefix + * @mtu: MTU option + * @source_ll: Target link-layer address + * @var: Variable fields + */ +struct ndp_ra { + struct icmp6hdr ih; + uint32_t reachable; + uint32_t retrans; + struct opt_prefix_info prefix_info; + struct in6_addr prefix; + struct opt_l2_addr source_ll; + + unsigned char var[sizeof(struct opt_mtu) + sizeof(struct opt_rdnss) + + sizeof(struct opt_dnssl)]; +} __attribute__((packed)); + +/** + * struct ndp_ns - NDP Neighbor Solicitation (NS) message + * @ih: ICMPv6 header + * @target_addr: Target IPv6 address + */ +struct ndp_ns { + struct icmp6hdr ih; + struct in6_addr target_addr; +} __attribute__((packed)); + /** * ndp() - Check for NDP solicitations, reply as needed * @c: Execution context * @ih: ICMPv6 header - * @saddr Source IPv6 address + * @saddr: Source IPv6 address + * @p: Packet pool * * Return: 0 if not handled here, 1 if handled, -1 on failure */ -int ndp(struct ctx *c, const struct icmp6hdr *ih, const struct in6_addr *saddr) +int ndp(struct ctx *c, const struct icmp6hdr *ih, const struct in6_addr *saddr, + const struct pool *p) { + struct ndp_na na = { + .ih = { + .icmp6_type = NA, + .icmp6_code = 0, + .icmp6_router = 1, + .icmp6_solicited = 1, + .icmp6_override = 1, + }, + .target_l2_addr = { + .header = { + .type = OPT_TARGET_L2_ADDR, + .len = 1, + }, + } + }; + struct ndp_ra ra = { + .ih = { + .icmp6_type = RA, + .icmp6_code = 0, + .icmp6_hop_limit = 255, + /* RFC 8319 */ + .icmp6_rt_lifetime = htons_constant(65535), + .icmp6_addrconf_managed = 1, + }, + .prefix_info = { + .header = { + .type = OPT_PREFIX_INFO, + .len = 4, + }, + .prefix_len = 64, + .prefix_flags = 0xc0, /* prefix flags: L, A */ + .valid_lifetime = ~0U, + .pref_lifetime = ~0U, + }, + .source_ll = { + .header = { + .type = OPT_SRC_L2_ADDR, + .len = 1, + }, + }, + }; const struct in6_addr *rsaddr; /* src addr for reply */ - char buf[BUFSIZ] = { 0 }; - struct ipv6hdr *ip6hr; - struct icmp6hdr *ihr; - struct ethhdr *ehr; - unsigned char *p; + unsigned char *ptr = NULL; size_t dlen; if (ih->icmp6_type < RS || ih->icmp6_type > NA) @@ -62,28 +233,22 @@ if (c->no_ndp) return 1; - ehr = (struct ethhdr *)buf; - ip6hr = (struct ipv6hdr *)(ehr + 1); - ihr = (struct icmp6hdr *)(ip6hr + 1); - if (ih->icmp6_type == NS) { + struct ndp_ns *ns = packet_get(p, 0, 0, sizeof(struct ndp_ns), + NULL); + + if (!ns) + return -1; + if (IN6_IS_ADDR_UNSPECIFIED(saddr)) return 1; info("NDP: received NS, sending NA"); - ihr->icmp6_type = NA; - ihr->icmp6_code = 0; - ihr->icmp6_router = 1; - ihr->icmp6_solicited = 1; - ihr->icmp6_override = 1; - - p = (unsigned char *)(ihr + 1); - memcpy(p, ih + 1, sizeof(struct in6_addr)); /* target address */ - p += 16; - *p++ = 2; /* target ll */ - *p++ = 1; /* length */ - memcpy(p, c->mac, ETH_ALEN); - p += 6; + + memcpy(&na.target_addr, &ns->target_addr, + sizeof(na.target_addr)); + memcpy(na.target_l2_addr.mac, c->mac, ETH_ALEN); + } else if (ih->icmp6_type == RS) { size_t dns_s_len = 0; int i, n; @@ -92,31 +257,20 @@ return 1; info("NDP: received RS, sending RA"); - ihr->icmp6_type = RA; - ihr->icmp6_code = 0; - ihr->icmp6_hop_limit = 255; - ihr->icmp6_rt_lifetime = htons(65535); /* RFC 8319 */ - ihr->icmp6_addrconf_managed = 1; - - p = (unsigned char *)(ihr + 1); - p += 8; /* reachable, retrans time */ - *p++ = 3; /* prefix */ - *p++ = 4; /* length */ - *p++ = 64; /* prefix length */ - *p++ = 0xc0; /* prefix flags: L, A */ - *(uint32_t *)p = (uint32_t)~0U; /* lifetime */ - p += 4; - *(uint32_t *)p = (uint32_t)~0U; /* preferred lifetime */ - p += 8; - memcpy(p, &c->ip6.addr, 8); /* prefix */ - p += 16; + memcpy(&ra.prefix, &c->ip6.addr, sizeof(ra.prefix)); + + ptr = &ra.var[0]; if (c->mtu != -1) { - *p++ = 5; /* type */ - *p++ = 1; /* length */ - p += 2; /* reserved */ - *(uint32_t *)p = htonl(c->mtu); /* MTU */ - p += 4; + struct opt_mtu *mtu = (struct opt_mtu *)ptr; + *mtu = (struct opt_mtu) { + .header = { + .type = OPT_MTU, + .len = 1, + }, + .value = htonl(c->mtu), + }; + ptr += sizeof(struct opt_mtu); } if (c->no_dhcp_dns) @@ -124,59 +278,64 @@ for (n = 0; !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns[n]); n++); if (n) { - *p++ = 25; /* RDNSS */ - *p++ = 1 + 2 * n; /* length */ - p += 2; /* reserved */ - *(uint32_t *)p = (uint32_t)~0U; /* lifetime */ - p += 4; - + struct opt_rdnss *rdnss = (struct opt_rdnss *)ptr; + *rdnss = (struct opt_rdnss) { + .header = { + .type = OPT_RDNSS_TYPE, + .len = 1 + 2 * n, + }, + .lifetime = ~0U, + }; for (i = 0; i < n; i++) { - memcpy(p, &c->ip6.dns[i], 16); /* address */ - p += 16; + memcpy(&rdnss->dns[i], &c->ip6.dns[i], + sizeof(rdnss->dns[i])); } + ptr += offsetof(struct opt_rdnss, dns) + + i * sizeof(rdnss->dns[0]); for (n = 0; *c->dns_search[n].n; n++) dns_s_len += strlen(c->dns_search[n].n) + 2; } if (!c->no_dhcp_dns_search && dns_s_len) { - *p++ = 31; /* DNSSL */ - *p++ = (dns_s_len + 8 - 1) / 8 + 1; /* length */ - p += 2; /* reserved */ - *(uint32_t *)p = (uint32_t)~0U; /* lifetime */ - p += 4; + struct opt_dnssl *dnssl = (struct opt_dnssl *)ptr; + *dnssl = (struct opt_dnssl) { + .header = { + .type = OPT_DNSSL_TYPE, + .len = DIV_ROUND_UP(dns_s_len, 8) + 1, + }, + .lifetime = ~0U, + }; + ptr = dnssl->domains; for (i = 0; i < n; i++) { + size_t len; char *dot; - *(p++) = '.'; + *(ptr++) = '.'; - strncpy((char *)p, c->dns_search[i].n, - sizeof(buf) - - ((intptr_t)p - (intptr_t)buf)); - for (dot = (char *)p - 1; *dot; dot++) { + len = sizeof(dnssl->domains) - + (ptr - dnssl->domains); + + strncpy((char *)ptr, c->dns_search[i].n, len); + for (dot = (char *)ptr - 1; *dot; dot++) { if (*dot == '.') *dot = strcspn(dot + 1, "."); } - p += strlen(c->dns_search[i].n); - *(p++) = 0; + ptr += strlen(c->dns_search[i].n); + *(ptr++) = 0; } - memset(p, 0, 8 - dns_s_len % 8); /* padding */ - p += 8 - dns_s_len % 8; + memset(ptr, 0, 8 - dns_s_len % 8); /* padding */ + ptr += 8 - dns_s_len % 8; } dns_done: - *p++ = 1; /* source ll */ - *p++ = 1; /* length */ - memcpy(p, c->mac, ETH_ALEN); - p += 6; + memcpy(&ra.source_ll.mac, c->mac, ETH_ALEN); } else { return 1; } - dlen = (uintptr_t)p - (uintptr_t)ihr - sizeof(*ihr); - if (IN6_IS_ADDR_LINKLOCAL(saddr)) c->ip6.addr_ll_seen = *saddr; else @@ -187,7 +346,13 @@ else rsaddr = &c->ip6.addr_ll; - tap_icmp6_send(c, rsaddr, saddr, ihr, dlen + sizeof(*ihr)); + if (ih->icmp6_type == NS) { + dlen = sizeof(struct ndp_na); + tap_icmp6_send(c, rsaddr, saddr, &na, dlen); + } else if (ih->icmp6_type == RS) { + dlen = ptr - (unsigned char *)&ra; + tap_icmp6_send(c, rsaddr, saddr, &ra, dlen); + } return 1; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/passt-20240806.ee36266/ndp.h new/passt-20240814.61c0b0d/ndp.h --- old/passt-20240806.ee36266/ndp.h 2024-08-06 15:03:48.000000000 +0200 +++ new/passt-20240814.61c0b0d/ndp.h 2024-08-14 12:20:31.000000000 +0200 @@ -6,6 +6,7 @@ #ifndef NDP_H #define NDP_H -int ndp(struct ctx *c, const struct icmp6hdr *ih, const struct in6_addr *saddr); +int ndp(struct ctx *c, const struct icmp6hdr *ih, const struct in6_addr *saddr, + const struct pool *p); #endif /* NDP_H */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/passt-20240806.ee36266/passt.1 new/passt-20240814.61c0b0d/passt.1 --- old/passt-20240806.ee36266/passt.1 2024-08-06 15:03:48.000000000 +0200 +++ new/passt-20240814.61c0b0d/passt.1 2024-08-14 12:20:31.000000000 +0200 @@ -589,7 +589,7 @@ .BR \-\-no-copy-routes " " (DEPRECATED) With \-\-config-net, do not copy all the routes associated to the interface we derive addresses and routes from: set up only the default gateway. Implied by --g, \-\-gateway. +-g, \-\-gateway, for the corresponding IP version only. Default is to copy all the routing entries from the interface in the outer namespace to the target namespace, translating the output interface attribute to @@ -604,7 +604,7 @@ .BR \-\-no-copy-addrs " " (DEPRECATED) With \-\-config-net, do not copy all the addresses associated to the interface we derive addresses and routes from: set up a single one. Implied by \-a, -\-\-address. +\-\-address, for the corresponding IP version only. Default is to copy all the addresses, except for link-local ones, from the interface from the outer namespace to the target namespace. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/passt-20240806.ee36266/passt.c new/passt-20240814.61c0b0d/passt.c --- old/passt-20240806.ee36266/passt.c 2024-08-06 15:03:48.000000000 +0200 +++ new/passt-20240814.61c0b0d/passt.c 2024-08-14 12:20:31.000000000 +0200 @@ -211,7 +211,7 @@ arch_avx2_exec(argv); - isolate_initial(); + isolate_initial(argc, argv); c.pasta_netns_fd = c.fd_tap = c.pidfile_fd = -1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/passt-20240806.ee36266/passt.h new/passt-20240814.61c0b0d/passt.h --- old/passt-20240806.ee36266/passt.h 2024-08-06 15:03:48.000000000 +0200 +++ new/passt-20240814.61c0b0d/passt.h 2024-08-14 12:20:31.000000000 +0200 @@ -91,7 +91,7 @@ /** * struct ip4_ctx - IPv4 execution context - * @addr: IPv4 address for external, routable interface + * @addr: IPv4 address assigned to guest * @addr_seen: Latest IPv4 address seen as source from tap * @prefixlen: IPv4 prefix length (netmask) * @gw: Default IPv4 gateway @@ -100,6 +100,8 @@ * @dns_host: Use this DNS on the host for forwarding * @addr_out: Optional source address for outbound traffic * @ifname_out: Optional interface name to bind outbound sockets to + * @no_copy_routes: Don't copy all routes when configuring target namespace + * @no_copy_addrs: Don't copy all addresses when configuring namespace */ struct ip4_ctx { struct in_addr addr; @@ -112,11 +114,14 @@ struct in_addr addr_out; char ifname_out[IFNAMSIZ]; + + bool no_copy_routes; + bool no_copy_addrs; }; /** * struct ip6_ctx - IPv6 execution context - * @addr: IPv6 address for external, routable interface + * @addr: IPv6 address assigned to guest * @addr_ll: Link-local IPv6 address on external, routable interface * @addr_seen: Latest IPv6 global/site address seen as source from tap * @addr_ll_seen: Latest IPv6 link-local address seen as source from tap @@ -126,6 +131,8 @@ * @dns_host: Use this DNS on the host for forwarding * @addr_out: Optional source address for outbound traffic * @ifname_out: Optional interface name to bind outbound sockets to + * @no_copy_routes: Don't copy all routes when configuring target namespace + * @no_copy_addrs: Don't copy all addresses when configuring namespace */ struct ip6_ctx { struct in6_addr addr; @@ -139,6 +146,9 @@ struct in6_addr addr_out; char ifname_out[IFNAMSIZ]; + + bool no_copy_routes; + bool no_copy_addrs; }; #include <netinet/if_ether.h> @@ -173,8 +183,6 @@ * @pasta_ifn: Name of namespace interface for pasta * @pasta_ifi: Index of namespace interface for pasta * @pasta_conf_ns: Configure namespace after creating it - * @no_copy_routes: Don't copy all routes when configuring target namespace - * @no_copy_addrs: Don't copy all addresses when configuring namespace * @no_tcp: Disable TCP operation * @tcp: Context for TCP protocol handler * @no_tcp: Disable UDP operation @@ -233,8 +241,6 @@ char pasta_ifn[IF_NAMESIZE]; unsigned int pasta_ifi; int pasta_conf_ns; - int no_copy_routes; - int no_copy_addrs; int no_tcp; struct tcp_ctx tcp; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/passt-20240806.ee36266/pasta.c new/passt-20240814.61c0b0d/pasta.c --- old/passt-20240806.ee36266/pasta.c 2024-08-06 15:03:48.000000000 +0200 +++ new/passt-20240814.61c0b0d/pasta.c 2024-08-14 12:20:31.000000000 +0200 @@ -306,7 +306,7 @@ nl_link_up(nl_sock_ns, c->pasta_ifi, c->mtu); if (c->ifi4) { - if (c->no_copy_addrs) { + if (c->ip4.no_copy_addrs) { rc = nl_addr_set(nl_sock_ns, c->pasta_ifi, AF_INET, &c->ip4.addr, @@ -322,7 +322,7 @@ strerror(-rc)); } - if (c->no_copy_routes) { + if (c->ip4.no_copy_routes) { rc = nl_route_set_def(nl_sock_ns, c->pasta_ifi, AF_INET, &c->ip4.gw); } else { @@ -337,7 +337,7 @@ } if (c->ifi6) { - if (c->no_copy_addrs) { + if (c->ip6.no_copy_addrs) { rc = nl_addr_set(nl_sock_ns, c->pasta_ifi, AF_INET6, &c->ip6.addr, 64); } else { @@ -351,7 +351,7 @@ strerror(-rc)); } - if (c->no_copy_routes) { + if (c->ip6.no_copy_routes) { rc = nl_route_set_def(nl_sock_ns, c->pasta_ifi, AF_INET6, &c->ip6.gw); } else { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/passt-20240806.ee36266/tap.c new/passt-20240814.61c0b0d/tap.c --- old/passt-20240806.ee36266/tap.c 2024-08-06 15:03:48.000000000 +0200 +++ new/passt-20240814.61c0b0d/tap.c 2024-08-14 12:20:31.000000000 +0200 @@ -808,12 +808,13 @@ if (l4len < sizeof(struct icmp6hdr)) continue; - if (ndp(c, (struct icmp6hdr *)l4h, saddr)) + packet_add(pkt, l4len, l4h); + + if (ndp(c, (struct icmp6hdr *)l4h, saddr, pkt)) continue; tap_packet_debug(NULL, ip6h, NULL, proto, NULL, 1); - packet_add(pkt, l4len, l4h); icmp_tap_handler(c, PIF_TAP, AF_INET6, saddr, daddr, pkt, now); continue; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/passt-20240806.ee36266/test/nstool.c new/passt-20240814.61c0b0d/test/nstool.c --- old/passt-20240806.ee36266/test/nstool.c 2024-08-06 15:03:48.000000000 +0200 +++ new/passt-20240814.61c0b0d/test/nstool.c 2024-08-14 12:20:31.000000000 +0200 @@ -345,21 +345,43 @@ return fd; } +static pid_t sig_pid; +static void sig_handler(int signum) +{ + int err; + + err = kill(sig_pid, signum); + if (err) + die("Propagating %s: %s\n", strsignal(signum), strerror(errno)); +} + static void wait_for_child(pid_t pid) { - int status; + struct sigaction sa = { + .sa_handler = sig_handler, + .sa_flags = SA_RESETHAND, + }; + int status, err; + + sig_pid = pid; + err = sigaction(SIGTERM, &sa, NULL); + if (err) + die("sigaction(SIGTERM): %s\n", strerror(errno)); /* Match the child's exit status, if possible */ for (;;) { pid_t rc; rc = waitpid(pid, &status, WUNTRACED); - if (rc < 0) + if (rc < 0) { + if (errno == EINTR) + continue; die("waitpid() on %d: %s\n", pid, strerror(errno)); + } if (rc != pid) die("waitpid() on %d returned %d", pid, rc); if (WIFSTOPPED(status)) { - /* Stop the parent to patch */ + /* Stop the parent to match */ kill(getpid(), SIGSTOP); /* We must have resumed, resume the child */ kill(pid, SIGCONT); @@ -508,7 +530,7 @@ /* CHILD */ if (argc > optind + 1) { exe = argv[optind + 1]; - xargs = (const char * const*)(argv + optind + 1); + xargs = (const char *const *)(argv + optind + 1); } else { exe = getenv("SHELL"); if (!exe) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/passt-20240806.ee36266/util.c new/passt-20240814.61c0b0d/util.c --- old/passt-20240806.ee36266/util.c 2024-08-06 15:03:48.000000000 +0200 +++ new/passt-20240814.61c0b0d/util.c 2024-08-14 12:20:31.000000000 +0200 @@ -26,6 +26,7 @@ #include <errno.h> #include <stdbool.h> #include <linux/errqueue.h> +#include <getopt.h> #include "util.h" #include "iov.h" @@ -244,9 +245,9 @@ * @a: Minuend timestamp * @b: Subtrahend timestamp * - * Return: difference in microseconds (wraps after 2^64 / 10^6s ~= 585k years) + * Return: difference in microseconds (wraps after 2^63 / 10^6s ~= 292k years) */ -long long timespec_diff_us(const struct timespec *a, const struct timespec *b) +int64_t timespec_diff_us(const struct timespec *a, const struct timespec *b) { if (a->tv_nsec < b->tv_nsec) { return (b->tv_nsec - a->tv_nsec) / 1000 + @@ -694,3 +695,43 @@ return "<invalid>"; } + +/** + * close_open_files() - Close leaked files, but not --fd, stdin, stdout, stderr + * @argc: Argument count + * @argv: Command line options, as we need to skip any file given via --fd + */ +void close_open_files(int argc, char **argv) +{ + const struct option optfd[] = { { "fd", required_argument, NULL, 'F' }, + { 0 }, + }; + long fd = -1; + int name, rc; + + do { + name = getopt_long(argc, argv, "+:F", optfd, NULL); + + if (name == 'F') { + errno = 0; + fd = strtol(optarg, NULL, 0); + + if (errno || fd <= STDERR_FILENO || fd > INT_MAX) + die("Invalid --fd: %s", optarg); + } + } while (name != -1); + + if (fd == -1) { + rc = close_range(STDERR_FILENO + 1, ~0U, CLOSE_RANGE_UNSHARE); + } else if (fd == STDERR_FILENO + 1) { /* Still a single range */ + rc = close_range(STDERR_FILENO + 2, ~0U, CLOSE_RANGE_UNSHARE); + } else { + rc = close_range(STDERR_FILENO + 1, fd - 1, + CLOSE_RANGE_UNSHARE); + if (!rc) + rc = close_range(fd + 1, ~0U, CLOSE_RANGE_UNSHARE); + } + + if (rc) + die_perror("Failed to close files leaked by parent"); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/passt-20240806.ee36266/util.h new/passt-20240814.61c0b0d/util.h --- old/passt-20240806.ee36266/util.h 2024-08-06 15:03:48.000000000 +0200 +++ new/passt-20240814.61c0b0d/util.h 2024-08-14 12:20:31.000000000 +0200 @@ -168,7 +168,7 @@ uint32_t data); void sock_probe_mem(struct ctx *c); long timespec_diff_ms(const struct timespec *a, const struct timespec *b); -long long timespec_diff_us(const struct timespec *a, const struct timespec *b); +int64_t timespec_diff_us(const struct timespec *a, const struct timespec *b); void bitmap_set(uint8_t *map, unsigned bit); void bitmap_clear(uint8_t *map, unsigned bit); bool bitmap_isset(const uint8_t *map, unsigned bit); @@ -183,6 +183,7 @@ int fls(unsigned long x); int write_file(const char *path, const char *buf); int write_remainder(int fd, const struct iovec *iov, size_t iovcnt, size_t skip); +void close_open_files(int argc, char **argv); /** * af_name() - Return name of an address family