dev
Threads by month
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- 7 participants
- 1384 discussions
Both DHCPv4 and DHCPv6 has the capability to pass the hostname to
clients, the DHCPv4 uses option 12 (hostname) while the DHCPv6 uses option 39
(client fqdn), for some virt deployments like kubevirt is expected to
have the VirtualMachine name as the guest hostname.
This change add the following arguments:
- -H --hostname NAME to configure the hostname DHCPv4 option(12)
- --fqdn NAME to configure client fqdn option for both DHCPv4(81) and
DHCPv6(39)
Signed-off-by: Enrique Llorente <ellorent(a)redhat.com>
---
conf.c | 20 +++++++++++--
dhcp.c | 50 ++++++++++++++++++++++++++++----
dhcpv6.c | 75 +++++++++++++++++++++++++++++++++++++++---------
passt.1 | 11 +++++++
passt.h | 5 ++++
pasta.c | 18 ++++++++----
test/lib/setup | 10 +++----
test/passt.mbuto | 6 ++--
test/passt/dhcp | 15 +++++++++-
util.c | 23 +++++++++++++++
util.h | 6 ++++
11 files changed, 204 insertions(+), 35 deletions(-)
diff --git a/conf.c b/conf.c
index df2b016..5f21193 100644
--- a/conf.c
+++ b/conf.c
@@ -854,7 +854,9 @@ static void usage(const char *name, FILE *f, int status)
FPRINTF(f, " default: use addresses from /etc/resolv.conf\n");
FPRINTF(f,
" -S, --search LIST Space-separated list, search domains\n"
- " a single, empty option disables the DNS search list\n");
+ " a single, empty option disables the DNS search list\n"
+ " -H, --hostname NAME Hostname to configure client with\n"
+ " --fqdn NAME FQDN to configure client with\n");
if (strstr(name, "pasta"))
FPRINTF(f, " default: don't use any search list\n");
else
@@ -1313,6 +1315,7 @@ void conf(struct ctx *c, int argc, char **argv)
{"outbound", required_argument, NULL, 'o' },
{"dns", required_argument, NULL, 'D' },
{"search", required_argument, NULL, 'S' },
+ {"hostname", required_argument, NULL, 'H' },
{"no-tcp", no_argument, &c->no_tcp, 1 },
{"no-udp", no_argument, &c->no_udp, 1 },
{"no-icmp", no_argument, &c->no_icmp, 1 },
@@ -1357,6 +1360,7 @@ void conf(struct ctx *c, int argc, char **argv)
/* vhost-user backend program convention */
{"print-capabilities", no_argument, NULL, 26 },
{"socket-path", required_argument, NULL, 's' },
+ {"fqdn", required_argument, NULL, 27 },
{ 0 },
};
const char *logname = (c->mode == MODE_PASTA) ? "pasta" : "passt";
@@ -1379,9 +1383,9 @@ void conf(struct ctx *c, int argc, char **argv)
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:H: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:H:461t:u:";
}
c->tcp.fwd_in.mode = c->tcp.fwd_out.mode = FWD_UNSET;
@@ -1558,6 +1562,11 @@ void conf(struct ctx *c, int argc, char **argv)
case 26:
vu_print_capabilities();
break;
+ case 27:
+ if (snprintf_check(c->fqdn, PASST_MAXDNAME,
+ "%s", optarg))
+ die("Invalid FQDN: %s", optarg);
+ break;
case 'd':
c->debug = 1;
c->quiet = 0;
@@ -1727,6 +1736,11 @@ void conf(struct ctx *c, int argc, char **argv)
die("Cannot use DNS search domain %s", optarg);
break;
+ case 'H':
+ if (snprintf_check(c->hostname, PASST_MAXDNAME,
+ "%s", optarg))
+ die("Invalid hostname: %s", optarg);
+ break;
case '4':
v4_only = true;
v6_only = false;
diff --git a/dhcp.c b/dhcp.c
index d8515aa..b224bf8 100644
--- a/dhcp.c
+++ b/dhcp.c
@@ -63,6 +63,12 @@ static struct opt opts[255];
#define OPT_MIN 60 /* RFC 951 */
+/* 576 (RFC 2131), minus offset
+ * of options (268), minus end
+ * option and its length (2)
+ */
+#define OPT_MAX 306
+
/**
* dhcp_init() - Initialise DHCP options
*/
@@ -122,7 +128,7 @@ struct msg {
uint8_t sname[64];
uint8_t file[128];
uint32_t magic;
- uint8_t o[308];
+ uint8_t o[OPT_MAX + 2]; /* End option and length */
} __attribute__((__packed__));
/**
@@ -130,15 +136,31 @@ struct msg {
* @m: Message to fill
* @o: Option number
* @offset: Current offset within options field, updated on insertion
+ *
+ * Return: offset for the next option field
*/
-static void fill_one(struct msg *m, int o, int *offset)
+static int fill_one(struct msg *m, int o, int *offset)
{
+ size_t idx, slen = 0;
+
+ /* If it cannot write even enum + len + one byte, then just skip */
+ if (*offset + 2 > OPT_MAX)
+ return OPT_MAX;
+
m->o[*offset] = o;
m->o[*offset + 1] = opts[o].slen;
- memcpy(&m->o[*offset + 2], opts[o].s, opts[o].slen);
+ idx = *offset + 2;
+ slen = opts[o].slen;
+
+ /* Truncate if it goes beyond OPT_MAX */
+ if (idx + slen > OPT_MAX)
+ slen = OPT_MAX - idx;
+
+ memcpy(&m->o[*offset + 2], opts[o].s, slen);
opts[o].sent = 1;
*offset += 2 + opts[o].slen;
+ return *offset;
}
/**
@@ -172,7 +194,10 @@ static int fill(struct msg *m)
for (o = 0; o < 255; o++) {
if (opts[o].slen != -1 && !opts[o].sent)
- fill_one(m, o, &offset);
+ if (fill_one(m, o, &offset) == OPT_MAX) {
+ debug("DHCP: truncating after option %i", o);
+ break;
+ }
}
m->o[offset++] = 255;
@@ -285,7 +310,7 @@ static void opt_set_dns_search(const struct ctx *c, size_t max_len)
*/
int dhcp(const struct ctx *c, const struct pool *p)
{
- size_t mlen, dlen, offset = 0, opt_len, opt_off = 0;
+ size_t mlen, dlen, offset = 0, opt_len, opt_off = 0, hostname_len = 0, fqdn_len = 0;
char macstr[ETH_ADDRSTRLEN];
struct in_addr mask, dst;
const struct ethhdr *eh;
@@ -398,6 +423,21 @@ int dhcp(const struct ctx *c, const struct pool *p)
if (!opts[6].slen)
opts[6].slen = -1;
+ hostname_len = strlen(c->hostname);
+ if (hostname_len > 0) {
+ opts[12].slen = hostname_len;
+ memcpy(opts[12].s, &c->hostname, hostname_len);
+ }
+
+ fqdn_len = strlen(c->fqdn);
+ if (fqdn_len > 0) {
+ size_t encoded_len = 0;
+ opts[81].s[0] = 0x4; /* flags (E) */
+ encoded_len = encode_domain_name(c->fqdn, fqdn_len,
+ (char *) opts[81].s + 3);
+ opts[81].slen = encoded_len + 3;
+ }
+
if (!c->no_dhcp_dns_search)
opt_set_dns_search(c, sizeof(m->o));
diff --git a/dhcpv6.c b/dhcpv6.c
index 0523bba..ce3a1bd 100644
--- a/dhcpv6.c
+++ b/dhcpv6.c
@@ -48,6 +48,7 @@ struct opt_hdr {
# define STATUS_NOTONLINK htons_constant(4)
# define OPT_DNS_SERVERS htons_constant(23)
# define OPT_DNS_SEARCH htons_constant(24)
+# define OPT_CLIENT_FQDN htons_constant(39)
#define STR_NOTONLINK "Prefix not appropriate for link."
uint16_t l;
@@ -58,6 +59,9 @@ struct opt_hdr {
sizeof(struct opt_hdr))
#define OPT_VSIZE(x) (sizeof(struct opt_##x) - \
sizeof(struct opt_hdr))
+#define OPT_MAX_SIZE IPV6_MIN_MTU - (sizeof(struct ipv6hdr) + \
+ sizeof(struct udphdr) + \
+ sizeof(struct msg_hdr))
/**
* struct opt_client_id - DHCPv6 Client Identifier option
@@ -163,6 +167,18 @@ struct opt_dns_search {
char list[MAXDNSRCH * NS_MAXDNAME];
} __attribute__((packed));
+/**
+ * struct opt_client_fqdn - Client FQDN option (RFC 4704)
+ * @hdr: Option header
+ * @flags: Flags described by RFC 4704 (always zero for us)
+ * @domain_name: Client FQDN
+ */
+struct opt_client_fqdn{
+ struct opt_hdr hdr;
+ uint8_t flags;
+ char domain_name[PASST_MAXDNAME];
+} __attribute__((packed));
+
/**
* struct msg_hdr - DHCPv6 client/server message header
* @type: DHCP message type
@@ -193,6 +209,7 @@ struct msg_hdr {
* @client_id: Client Identifier, variable length
* @dns_servers: DNS Recursive Name Server, here just for storage size
* @dns_search: Domain Search List, here just for storage size
+ * @client_fqdn: Client FQDN, variable length
*/
static struct resp_t {
struct msg_hdr hdr;
@@ -203,6 +220,7 @@ static struct resp_t {
struct opt_client_id client_id;
struct opt_dns_servers dns_servers;
struct opt_dns_search dns_search;
+ struct opt_client_fqdn client_fqdn;
} __attribute__((__packed__)) resp = {
{ 0 },
SERVER_ID,
@@ -228,6 +246,10 @@ static struct resp_t {
{ { OPT_DNS_SEARCH, 0, },
{ 0 },
},
+
+ { { OPT_CLIENT_FQDN, 0, },
+ 0, { 0 },
+ },
};
static const struct opt_status_code sc_not_on_link = {
@@ -346,7 +368,6 @@ static size_t dhcpv6_dns_fill(const struct ctx *c, char *buf, int offset)
{
struct opt_dns_servers *srv = NULL;
struct opt_dns_search *srch = NULL;
- char *p = NULL;
int i;
if (c->no_dhcp_dns)
@@ -373,6 +394,7 @@ search:
return offset;
for (i = 0; *c->dns_search[i].n; i++) {
+ size_t encoded_name_len = 0;
size_t name_len = strlen(c->dns_search[i].n);
/* We already append separators, don't duplicate if present */
@@ -388,29 +410,53 @@ search:
offset += sizeof(struct opt_hdr);
srch->hdr.t = OPT_DNS_SEARCH;
srch->hdr.l = 0;
- p = srch->list;
}
-
- *p = '.';
- p = stpncpy(p + 1, c->dns_search[i].n, name_len);
- p++;
- srch->hdr.l += name_len + 2;
- offset += name_len + 2;
+
+ encoded_name_len = encode_domain_name(c->dns_search[i].n,
+ name_len, srch->list);
+ srch->hdr.l += encoded_name_len;
+ offset += encoded_name_len;
}
if (srch) {
- for (i = 0; i < srch->hdr.l; i++) {
- if (srch->list[i] == '.') {
- srch->list[i] = strcspn(srch->list + i + 1,
- ".");
- }
- }
srch->hdr.l = htons(srch->hdr.l);
}
return offset;
}
+/**
+ * dhcpv6_client_fqdn_fill() - Fill in client FQDN option
+ * @c: Execution context
+ * @buf: Response message buffer where options will be appended
+ * @offset: Offset in message buffer for new options
+ *
+ * Return: updated length of response message buffer.
+ */
+static size_t dhcpv6_client_fqdn_fill(const struct ctx *c, char *buf, int offset)
+{
+ size_t fqdn_len, opt_hdr_len, opt_len, encoded_fqdn_len;
+ struct opt_client_fqdn *o;
+
+ opt_hdr_len = sizeof(struct opt_hdr);
+
+ fqdn_len = MIN(strlen(c->fqdn), OPT_MAX_SIZE - (offset + opt_hdr_len + 1));
+
+ if (fqdn_len == 0)
+ return offset;
+
+ o = (struct opt_client_fqdn *)(buf + offset);
+ encoded_fqdn_len = encode_domain_name(c->fqdn, fqdn_len,
+ o->domain_name);
+ opt_len = encoded_fqdn_len + 1;
+
+ o->hdr.t = OPT_CLIENT_FQDN;
+ o->hdr.l = htons(opt_len);
+ o->flags = 0x00;
+
+ return offset + opt_hdr_len + opt_len;
+}
+
/**
* dhcpv6() - Check if this is a DHCPv6 message, reply as needed
* @c: Execution context
@@ -544,6 +590,7 @@ int dhcpv6(struct ctx *c, const struct pool *p,
n = offsetof(struct resp_t, client_id) +
sizeof(struct opt_hdr) + ntohs(client_id->l);
n = dhcpv6_dns_fill(c, (char *)&resp, n);
+ n = dhcpv6_client_fqdn_fill(c, (char *)&resp, n);
resp.hdr.xid = mh->xid;
diff --git a/passt.1 b/passt.1
index d9cd33e..8f6b194 100644
--- a/passt.1
+++ b/passt.1
@@ -401,6 +401,17 @@ Enable IPv6-only operation. IPv4 traffic will be ignored.
By default, IPv4 operation is enabled as long as at least an IPv4 route and an
interface address are configured on a given host interface.
+.TP
+.BR \-H ", " \-\-hostname " " \fIname
+Hostname to configure client with.
+Send \fIname as DHCP option 12 (hostname).
+
+.TP
+.BR \-\-fqdn " " \fIname
+FQDN to configure client with.
+Send \fIname as dhcp client fqdn option, for DHCP option 81 and for
+DHCPv6 option 39.
+
.SS \fBpasst\fR-only options
.TP
diff --git a/passt.h b/passt.h
index 0dd4efa..9909a10 100644
--- a/passt.h
+++ b/passt.h
@@ -209,6 +209,8 @@ struct ip6_ctx {
* @ifi4: Template interface for IPv4, -1: none, 0: IPv4 disabled
* @ip: IPv4 configuration
* @dns_search: DNS search list
+ * @hostname: Guest hostname
+ * @fqdn: Guest FQDN
* @ifi6: Template interface for IPv6, -1: none, 0: IPv6 disabled
* @ip6: IPv6 configuration
* @pasta_ifn: Name of namespace interface for pasta
@@ -268,6 +270,9 @@ struct ctx {
struct ip4_ctx ip4;
struct fqdn dns_search[MAXDNSRCH];
+
+ char hostname[PASST_MAXDNAME];
+ char fqdn[PASST_MAXDNAME];
int ifi6;
struct ip6_ctx ip6;
diff --git a/pasta.c b/pasta.c
index ff41c95..00678f3 100644
--- a/pasta.c
+++ b/pasta.c
@@ -173,6 +173,7 @@ void pasta_open_ns(struct ctx *c, const char *netns)
struct pasta_spawn_cmd_arg {
const char *exe;
char *const *argv;
+ struct ctx *c;
};
/**
@@ -186,6 +187,7 @@ static int pasta_spawn_cmd(void *arg)
{
char hostname[HOST_NAME_MAX + 1] = HOSTNAME_PREFIX;
const struct pasta_spawn_cmd_arg *a;
+ size_t conf_hostname_len;
sigset_t set;
/* We run in a detached PID and mount namespace: mount /proc over */
@@ -194,10 +196,16 @@ static int pasta_spawn_cmd(void *arg)
if (write_file("/proc/sys/net/ipv4/ping_group_range", "0 0"))
warn("Cannot set ping_group_range, ICMP requests might fail");
-
- if (!gethostname(hostname + sizeof(HOSTNAME_PREFIX) - 1,
- HOST_NAME_MAX + 1 - sizeof(HOSTNAME_PREFIX)) ||
- errno == ENAMETOOLONG) {
+
+ a = (const struct pasta_spawn_cmd_arg *)arg;
+
+ conf_hostname_len = strlen(a->c->hostname);
+ if (conf_hostname_len > 0) {
+ if (sethostname(a->c->hostname, conf_hostname_len))
+ warn("Unable to set configured hostname");
+ }else if (!gethostname(hostname + sizeof(HOSTNAME_PREFIX) - 1,
+ HOST_NAME_MAX + 1 - sizeof(HOSTNAME_PREFIX)) ||
+ errno == ENAMETOOLONG) {
hostname[HOST_NAME_MAX] = '\0';
if (sethostname(hostname, strlen(hostname)))
warn("Unable to set pasta-prefixed hostname");
@@ -208,7 +216,6 @@ static int pasta_spawn_cmd(void *arg)
sigaddset(&set, SIGUSR1);
sigwaitinfo(&set, NULL);
- a = (const struct pasta_spawn_cmd_arg *)arg;
execvp(a->exe, a->argv);
die_perror("Failed to start command or shell");
@@ -230,6 +237,7 @@ void pasta_start_ns(struct ctx *c, uid_t uid, gid_t gid,
struct pasta_spawn_cmd_arg arg = {
.exe = argv[0],
.argv = argv,
+ .c = c,
};
char uidmap[BUFSIZ], gidmap[BUFSIZ];
char *sh_argv[] = { NULL, NULL };
diff --git a/test/lib/setup b/test/lib/setup
index 580825f..ee67152 100755
--- a/test/lib/setup
+++ b/test/lib/setup
@@ -49,7 +49,7 @@ setup_passt() {
context_run passt "make clean"
context_run passt "make valgrind"
- context_run_bg passt "valgrind --max-stackframe=$((4 * 1024 * 1024)) --trace-children=yes --vgdb=no --error-exitcode=1 --suppressions=test/valgrind.supp ./passt ${__opts} -s ${STATESETUP}/passt.socket -f -t 10001 -u 10001 -P ${STATESETUP}/passt.pid"
+ context_run_bg passt "valgrind --max-stackframe=$((4 * 1024 * 1024)) --trace-children=yes --vgdb=no --error-exitcode=1 --suppressions=test/valgrind.supp ./passt ${__opts} -s ${STATESETUP}/passt.socket -f -t 10001 -u 10001 -H hostname1 --fqdn fqdn1.passt.test -P ${STATESETUP}/passt.pid"
# pidfile isn't created until passt is listening
wait_for [ -f "${STATESETUP}/passt.pid" ]
@@ -160,11 +160,11 @@ setup_passt_in_ns() {
if [ ${VALGRIND} -eq 1 ]; then
context_run passt "make clean"
context_run passt "make valgrind"
- context_run_bg passt "valgrind --max-stackframe=$((4 * 1024 * 1024)) --trace-children=yes --vgdb=no --error-exitcode=1 --suppressions=test/valgrind.supp ./passt -f ${__opts} -s ${STATESETUP}/passt.socket -t 10001,10011,10021,10031 -u 10001,10011,10021,10031 -P ${STATESETUP}/passt.pid --map-host-loopback ${__map_ns4} --map-host-loopback ${__map_ns6}"
+ context_run_bg passt "valgrind --max-stackframe=$((4 * 1024 * 1024)) --trace-children=yes --vgdb=no --error-exitcode=1 --suppressions=test/valgrind.supp ./passt -f ${__opts} -s ${STATESETUP}/passt.socket -H hostname1 --fqdn fqdn1.passt.test -t 10001,10011,10021,10031 -u 10001,10011,10021,10031 -P ${STATESETUP}/passt.pid --map-host-loopback ${__map_ns4} --map-host-loopback ${__map_ns6}"
else
context_run passt "make clean"
context_run passt "make"
- context_run_bg passt "./passt -f ${__opts} -s ${STATESETUP}/passt.socket -t 10001,10011,10021,10031 -u 10001,10011,10021,10031 -P ${STATESETUP}/passt.pid --map-host-loopback ${__map_ns4} --map-host-loopback ${__map_ns6}"
+ context_run_bg passt "./passt -f ${__opts} -s ${STATESETUP}/passt.socket -H hostname1 --fqdn fqdn1.passt.test -t 10001,10011,10021,10031 -u 10001,10011,10021,10031 -P ${STATESETUP}/passt.pid --map-host-loopback ${__map_ns4} --map-host-loopback ${__map_ns6}"
fi
wait_for [ -f "${STATESETUP}/passt.pid" ]
@@ -243,7 +243,7 @@ setup_two_guests() {
[ ${TRACE} -eq 1 ] && __opts="${__opts} --trace"
[ ${VHOST_USER} -eq 1 ] && __opts="${__opts} --vhost-user"
- context_run_bg passt_1 "./passt -s ${STATESETUP}/passt_1.socket -P ${STATESETUP}/passt_1.pid -f ${__opts} -t 10001 -u 10001"
+ context_run_bg passt_1 "./passt -s ${STATESETUP}/passt_1.socket -P ${STATESETUP}/passt_1.pid -f ${__opts} --fqdn fqdn1.passt.test -H hostname1 -t 10001 -u 10001"
wait_for [ -f "${STATESETUP}/passt_1.pid" ]
__opts=
@@ -252,7 +252,7 @@ setup_two_guests() {
[ ${TRACE} -eq 1 ] && __opts="${__opts} --trace"
[ ${VHOST_USER} -eq 1 ] && __opts="${__opts} --vhost-user"
- context_run_bg passt_2 "./passt -s ${STATESETUP}/passt_2.socket -P ${STATESETUP}/passt_2.pid -f ${__opts} -t 10004 -u 10004"
+ context_run_bg passt_2 "./passt -s ${STATESETUP}/passt_2.socket -P ${STATESETUP}/passt_2.pid -f ${__opts} --hostname hostname2 --fqdn fqdn2 -t 10004 -u 10004"
wait_for [ -f "${STATESETUP}/passt_2.pid" ]
__vmem="$((${MEM_KIB} / 1024 / 4))"
diff --git a/test/passt.mbuto b/test/passt.mbuto
index 138d365..1e07693 100755
--- a/test/passt.mbuto
+++ b/test/passt.mbuto
@@ -13,7 +13,7 @@
PROGS="${PROGS:-ash,dash,bash ip mount ls insmod mkdir ln cat chmod lsmod
modprobe find grep mknod mv rm umount jq iperf3 dhclient hostname
sed tr chown sipcalc cut socat dd strace ping tail killall sleep sysctl
- nproc tcp_rr tcp_crr udp_rr which tee seq bc sshd ssh-keygen cmp}"
+ nproc tcp_rr tcp_crr udp_rr which tee seq bc sshd ssh-keygen cmp env}"
# OpenSSH 9.8 introduced split binaries, with sshd being the daemon, and
# sshd-session the per-session program. We need the latter as well, and the path
@@ -41,6 +41,7 @@ FIXUP="${FIXUP}"'
#!/bin/sh
LOG=/var/log/dhclient-script.log
echo \${reason} \${interface} >> \$LOG
+env >> \$LOG
set >> \$LOG
[ -n "\${new_interface_mtu}" ] && ip link set dev \${interface} mtu \${new_interface_mtu}
@@ -54,7 +55,8 @@ set >> \$LOG
[ -n "\${new_ip6_address}" ] && ip addr add \${new_ip6_address}/\${new_ip6_prefixlen} dev \${interface}
[ -n "\${new_dhcp6_name_servers}" ] && for d in \${new_dhcp6_name_servers}; do echo "nameserver \${d}%\${interface}" >> /etc/resolv.conf; done
[ -n "\${new_dhcp6_domain_search}" ] && (printf "search"; for d in \${new_dhcp6_domain_search}; do printf " %s" "\${d}"; done; printf "\n") >> /etc/resolv.conf
-[ -n "\${new_host_name}" ] && hostname "\${new_host_name}"
+[ -n "\${new_host_name}" ] && echo "\${new_host_name}" > /tmp/new_host_name
+[ -n "\${new_fqdn_fqdn}" ] && echo "\${new_fqdn_fqdn}" > /tmp/new_fqdn_fqdn
exit 0
EOF
chmod 755 /sbin/dhclient-script
diff --git a/test/passt/dhcp b/test/passt/dhcp
index 9925ab9..145f1ba 100644
--- a/test/passt/dhcp
+++ b/test/passt/dhcp
@@ -11,7 +11,7 @@
# Copyright (c) 2021 Red Hat GmbH
# Author: Stefano Brivio <sbrivio(a)redhat.com>
-gtools ip jq dhclient sed tr
+gtools ip jq dhclient sed tr hostname
htools ip jq sed tr head
test Interface name
@@ -47,7 +47,16 @@ gout SEARCH sed 's/\. / /g' /etc/resolv.conf | sed 's/\.$//g' | sed -n 's/^searc
hout HOST_SEARCH sed 's/\. / /g' /etc/resolv.conf | sed 's/\.$//g' | sed -n 's/^search \(.*\)/\1/p' | tr ' \n' ',' | sed 's/,$//;s/$/\n/'
check [ "__SEARCH__" = "__HOST_SEARCH__" ]
+test DHCP: Hostname
+gout NEW_HOST_NAME cat /tmp/new_host_name
+check [ "__NEW_HOST_NAME__" = "hostname1" ]
+
+test DHCP: Client FQDN
+gout NEW_FQDN_FQDN cat /tmp/new_fqdn_fqdn
+check [ "__NEW_FQDN_FQDN__" = "fqdn1.passt.test" ]
+
test DHCPv6: address
+guest rm /tmp/new_fqdn_fqdn
guest /sbin/dhclient -6 __IFNAME__
# Wait for DAD to complete
guest while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
@@ -70,3 +79,7 @@ test DHCPv6: search list
gout SEARCH6 sed 's/\. / /g' /etc/resolv.conf | sed 's/\.$//g' | sed -n 's/^search \(.*\)/\1/p' | tr ' \n' ',' | sed 's/,$//;s/$/\n/'
hout HOST_SEARCH6 sed 's/\. / /g' /etc/resolv.conf | sed 's/\.$//g' | sed -n 's/^search \(.*\)/\1/p' | tr ' \n' ',' | sed 's/,$//;s/$/\n/'
check [ "__SEARCH6__" = "__HOST_SEARCH6__" ]
+
+test DHCPv6: Hostname
+gout NEW_FQDN_FQDN cat /tmp/new_fqdn_fqdn
+check [ "__NEW_FQDN_FQDN__" = "fqdn1.passt.test" ]
diff --git a/util.c b/util.c
index 11973c4..7aeb5b4 100644
--- a/util.c
+++ b/util.c
@@ -837,3 +837,26 @@ void raw_random(void *buf, size_t buflen)
if (random_read < buflen)
die("Unexpected EOF on random data source");
}
+/**
+ * encode_domain_name() - Encode domain name according to RFC 1035, section 3.1
+ * @domain_name: Input domain name to encode
+ * @len: Domain name length
+ * @buf: Buffer to fill in with encoded domain name
+ *
+ * Return: encoded domain name length
+ */
+size_t encode_domain_name(const char *domain_name, size_t len, char *buf)
+{
+ char *p;
+ size_t i;
+
+ buf[0] = strcspn(domain_name, ".");
+ p = buf + 1;
+ for (i = 0; i < len; i++) {
+ if (domain_name[i] == '.')
+ p[i] = strcspn(domain_name + i + 1, ".");
+ else
+ p[i] = domain_name[i];
+ }
+ return len + 2;
+}
diff --git a/util.h b/util.h
index 3fa1d12..c55ef29 100644
--- a/util.h
+++ b/util.h
@@ -40,6 +40,9 @@
#ifndef IP_MAX_MTU
#define IP_MAX_MTU USHRT_MAX
#endif
+#ifndef IPV6_MIN_MTU
+#define IPV6_MIN_MTU 1280
+#endif
#ifndef MIN
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
@@ -346,4 +349,7 @@ static inline int wrap_accept4(int sockfd, struct sockaddr *addr,
#define accept4(s, addr, addrlen, flags) \
wrap_accept4((s), (addr), (addrlen), (flags))
+#define PASST_MAXDNAME 253 /* RFC 1035 */
+size_t encode_domain_name(const char* domain_name, size_t len, char* buf);
+
#endif /* UTIL_H */
--
2.47.0
3
4
[PATCH] seccomp: Unconditionally allow accept(2) even if accept4(2) is present
by Stefano Brivio 03 Jan '25
by Stefano Brivio 03 Jan '25
03 Jan '25
On Alpine Linux 3.21, passt aborts right away as soon as QEMU connects
to it.
Most likely, this has always been the case with musl, because since
musl commit dc01e2cbfb29 ("add fallback emulation for accept4 on old
kernels"), accept4() without flags is implemented using accept().
However, I guess that nobody realised earlier because it's typically
pasta(1) being used on musl-based distributions, and the only place
where we call accept4() without flags is tap_listen_handler().
Add accept() to the list of allowed system calls regardless of the
presence of accept4().
Reported-by: NN708
Link: https://bugs.passt.top/show_bug.cgi?id=106
Signed-off-by: Stefano Brivio <sbrivio(a)redhat.com>
---
passt.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/passt.c b/passt.c
index 957f3d0..1a0c404 100644
--- a/passt.c
+++ b/passt.c
@@ -180,7 +180,7 @@ void exit_handler(int signal)
* #syscalls socket getsockopt setsockopt s390x:socketcall i686:socketcall close
* #syscalls bind connect recvfrom sendto shutdown
* #syscalls arm:recv ppc64le:recv arm:send ppc64le:send
- * #syscalls accept4|accept listen epoll_ctl epoll_wait|epoll_pwait epoll_pwait
+ * #syscalls accept4 accept listen epoll_ctl epoll_wait|epoll_pwait epoll_pwait
* #syscalls clock_gettime arm:clock_gettime64 i686:clock_gettime64
*/
int main(int argc, char **argv)
--
2.43.0
2
1
This... is not any of the things I said I would be working on. I can
only say that a herd of very hairy yaks led me astray. Looking at bug
66 I spotted some problems with our handling of MTUs / maximum frame
sizes. Looking at that I found some weirdness and some real, if
minor, bugs in the sizing and handling of the packet pools.
David Gibson (3):
packet: Use flexible array member in struct pool
packet: Don't have struct pool specify its buffer
tap: Don't size pool_tap[46] for the maximum number of packets
packet.c | 63 ++++++----------------------------------------------
packet.h | 41 ++++++++++++----------------------
passt.h | 2 --
tap.c | 63 +++++++++++++++++++++++++---------------------------
tap.h | 4 ++--
vhost_user.c | 2 --
vu_common.c | 31 ++------------------------
7 files changed, 55 insertions(+), 151 deletions(-)
--
2.47.1
2
11
20 Dec '24
It was reported that SSDP notifications sent from a container (with
e.g. minidlna) stopped appearing on the network starting from commit
1db4f773e87f ("udp: Improve detail of UDP endpoint sanity checking").
As a minimal reproducer using minidlnad(8):
$ mkdir /tmp/minidlna
$ cat conf
media_dir=/tmp/minidlna
db_dir=/tmp/minidlna
$ ./pasta -d --config-net -- sh -c '/usr/sbin/minidlnad -p 31337 -S -f conf -P /dev/null & (sleep 1; killall minidlnad)'
[...]
1.0327: Flow 0 (NEW): FREE -> NEW
1.0327: Flow 0 (INI): NEW -> INI
1.0327: Flow 0 (INI): TAP [88.198.0.164]:54185 -> [239.255.255.250]:1900 => ?
1.0327: Flow 0 (INI): Invalid endpoint on UDP packet
1.0327: Flow 0 (FREE): INI -> FREE
1.0328: Flow 0 (FREE): TAP [88.198.0.164]:54185 -> [239.255.255.250]:1900 => ?
1.0328: Dropping datagram with no flow TAP 88.198.0.164:54185 -> 239.255.255.250:1900
This is an actual regression as there's no particular reason to block
outbound multicast UDP packets.
And even if we don't handle multicast groups in any particular way
(https://bugs.passt.top/show_bug.cgi?id=2, "Add IGMP/MLD proxy"),
there's no reason to block inbound multicast or broadcast packets
either, should they ever be somehow delivered to passt or pasta.
Let multicast and broadcast packets through, refusing only to
establish flows with unspecified endpoint, as those would actually
cause havoc in the flow table.
IP-wise, SSDP notifications look like this (after this patch), inside
and outside:
$ pasta -p /tmp/minidlna.pcap --config-net -- sh -c '/usr/sbin/minidlnad -p 31337 -S -f minidlna.conf -P /dev/null & (sleep 1; killall minidlnad)'
[...]
$ tshark -a packets:1 -r /tmp/minidlna.pcap ssdp
2 0.074808 88.198.0.164 ? 239.255.255.250 SSDP 200 NOTIFY * HTTP/1.1
# tshark -i ens3 -a packets:1 multicast 2>/dev/null
1 0.000000000 88.198.0.164 ? 239.255.255.250 SSDP 200 NOTIFY * HTTP/1.1
Link: https://github.com/containers/podman/issues/24871
Fixes: 1db4f773e87f ("udp: Improve detail of UDP endpoint sanity checking")
Signed-off-by: Stefano Brivio <sbrivio(a)redhat.com>
---
udp_flow.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/udp_flow.c b/udp_flow.c
index 343caae..9fd7d06 100644
--- a/udp_flow.c
+++ b/udp_flow.c
@@ -209,7 +209,7 @@ flow_sidx_t udp_flow_from_sock(const struct ctx *c, union epoll_ref ref,
if (!inany_is_unicast(&ini->eaddr) ||
ini->eport == 0 || ini->oport == 0) {
- /* In principle ini->oddr also must be unicast, but when we've
+ /* In principle ini->oddr also must be specified, but when we've
* been initiated from a socket bound to 0.0.0.0 or ::, we don't
* know our address, so we have to leave it unpopulated.
*/
@@ -267,8 +267,8 @@ flow_sidx_t udp_flow_from_tap(const struct ctx *c,
ini = flow_initiate_af(flow, PIF_TAP, af, saddr, srcport,
daddr, dstport);
- if (!inany_is_unicast(&ini->eaddr) || ini->eport == 0 ||
- !inany_is_unicast(&ini->oaddr) || ini->oport == 0) {
+ if (inany_is_unspecified(&ini->eaddr) || ini->eport == 0 ||
+ inany_is_unspecified(&ini->oaddr) || ini->oport == 0) {
flow_dbg(flow, "Invalid endpoint on UDP packet");
flow_alloc_cancel(flow);
return FLOW_SIDX_NONE;
--
2.43.0
1
0
20 Dec '24
I don't think it's necessarily productive to check all the possible
error conditions in the Makefile, but this one is annoying: issue
'make' without a C compiler, then install one, and build again.
Then run passt and it will mysteriously terminate on epoll_wait(),
because seccomp.h is good enough to build against, but the resulting
seccomp filter doesn't allow any system call. Not really fun to debug.
Signed-off-by: Stefano Brivio <sbrivio(a)redhat.com>
---
Makefile | 1 +
1 file changed, 1 insertion(+)
diff --git a/Makefile b/Makefile
index 1fce737..464eef1 100644
--- a/Makefile
+++ b/Makefile
@@ -16,6 +16,7 @@ VERSION ?= $(shell git describe --tags HEAD 2>/dev/null || echo "unknown\ versio
DUAL_STACK_SOCKETS := 1
TARGET ?= $(shell $(CC) -dumpmachine)
+$(if $(TARGET),,$(error Failed to get target architecture))
# Get 'uname -m'-like architecture description for target
TARGET_ARCH := $(firstword $(subst -, ,$(TARGET)))
TARGET_ARCH := $(patsubst [:upper:],[:lower:],$(TARGET_ARCH))
--
2.43.0
2
1
Both DHCPv4 and DHCPv6 has the capability to pass the hostname to
clients, the DHCPv4 uses option 12 (hostname) while the DHCPv6 uses option 39
(client fqdn), for some virt deployments like kubevirt is expected to
have the VirtualMachine name as the guest hostname.
This change add the following arguments:
- -H --hostname NAME to configure the hostname DHCPv4 option(12)
- --fqdn NAME to configure client fqdn option for both DHCPv4(81) and
DHCPv6(39)
Signed-off-by: Enrique Llorente <ellorent(a)redhat.com>
---
conf.c | 23 ++++++++++++++--
dhcp.c | 39 +++++++++++++++++++++++---
dhcpv6.c | 72 +++++++++++++++++++++++++++++++++++++++---------
passt.1 | 11 ++++++++
passt.h | 6 ++++
pasta.c | 15 ++++++++--
test/lib/setup | 10 +++----
test/passt.mbuto | 6 ++--
test/passt/dhcp | 15 +++++++++-
util.c | 22 +++++++++++++++
util.h | 3 ++
11 files changed, 191 insertions(+), 31 deletions(-)
diff --git a/conf.c b/conf.c
index df2b016..29e4bed 100644
--- a/conf.c
+++ b/conf.c
@@ -854,7 +854,9 @@ static void usage(const char *name, FILE *f, int status)
FPRINTF(f, " default: use addresses from /etc/resolv.conf\n");
FPRINTF(f,
" -S, --search LIST Space-separated list, search domains\n"
- " a single, empty option disables the DNS search list\n");
+ " a single, empty option disables the DNS search list\n"
+ " -H, --hostname NAME Hostname to configure client with\n"
+ " --fqdn NAME FQDN to configure client with\n");
if (strstr(name, "pasta"))
FPRINTF(f, " default: don't use any search list\n");
else
@@ -1313,6 +1315,7 @@ void conf(struct ctx *c, int argc, char **argv)
{"outbound", required_argument, NULL, 'o' },
{"dns", required_argument, NULL, 'D' },
{"search", required_argument, NULL, 'S' },
+ {"hostname", required_argument, NULL, 'H' },
{"no-tcp", no_argument, &c->no_tcp, 1 },
{"no-udp", no_argument, &c->no_udp, 1 },
{"no-icmp", no_argument, &c->no_icmp, 1 },
@@ -1357,6 +1360,7 @@ void conf(struct ctx *c, int argc, char **argv)
/* vhost-user backend program convention */
{"print-capabilities", no_argument, NULL, 26 },
{"socket-path", required_argument, NULL, 's' },
+ {"fqdn", required_argument, NULL, 27 },
{ 0 },
};
const char *logname = (c->mode == MODE_PASTA) ? "pasta" : "passt";
@@ -1379,9 +1383,9 @@ void conf(struct ctx *c, int argc, char **argv)
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:H: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:H:461t:u:";
}
c->tcp.fwd_in.mode = c->tcp.fwd_out.mode = FWD_UNSET;
@@ -1558,6 +1562,12 @@ void conf(struct ctx *c, int argc, char **argv)
case 26:
vu_print_capabilities();
break;
+ case 27:
+ if (snprintf_check(c->fqdn, PASST_MAXDNAME,
+ "%s", optarg)) {
+ die("Invalid fqdn: %s", optarg);
+ }
+ break;
case 'd':
c->debug = 1;
c->quiet = 0;
@@ -1727,6 +1737,12 @@ void conf(struct ctx *c, int argc, char **argv)
die("Cannot use DNS search domain %s", optarg);
break;
+ case 'H':
+ if (snprintf_check(c->hostname, PASST_MAXDNAME,
+ "%s", optarg)) {
+ die("Invalid hostname: %s", optarg);
+ }
+ break;
case '4':
v4_only = true;
v6_only = false;
@@ -1741,6 +1757,7 @@ void conf(struct ctx *c, int argc, char **argv)
c->one_off = true;
break;
+
case 't':
case 'u':
case 'T':
diff --git a/dhcp.c b/dhcp.c
index d8515aa..a04462b 100644
--- a/dhcp.c
+++ b/dhcp.c
@@ -62,6 +62,7 @@ static struct opt opts[255];
#define DHCPFORCERENEW 9
#define OPT_MIN 60 /* RFC 951 */
+#define OPT_MAX 302 /* UDP packet limit - dhcp headers */
/**
* dhcp_init() - Initialise DHCP options
@@ -122,7 +123,7 @@ struct msg {
uint8_t sname[64];
uint8_t file[128];
uint32_t magic;
- uint8_t o[308];
+ uint8_t o[OPT_MAX+2];
} __attribute__((__packed__));
/**
@@ -131,14 +132,28 @@ struct msg {
* @o: Option number
* @offset: Current offset within options field, updated on insertion
*/
-static void fill_one(struct msg *m, int o, int *offset)
+static int fill_one(struct msg *m, int o, int *offset)
{
+ size_t idx, slen = 0;
+
+ // We cannot write even enum + len + one byte then just skip
+ if (*offset + 2 > OPT_MAX) {
+ return OPT_MAX;
+ }
m->o[*offset] = o;
m->o[*offset + 1] = opts[o].slen;
- memcpy(&m->o[*offset + 2], opts[o].s, opts[o].slen);
+ idx=*offset + 2;
+ slen=opts[o].slen;
+
+ // Truncate if it goes beyond OPT_MAX
+ if (idx + slen > OPT_MAX) {
+ slen = OPT_MAX - idx;
+ }
+ memcpy(&m->o[*offset + 2], opts[o].s, slen);
opts[o].sent = 1;
*offset += 2 + opts[o].slen;
+ return *offset;
}
/**
@@ -172,7 +187,9 @@ static int fill(struct msg *m)
for (o = 0; o < 255; o++) {
if (opts[o].slen != -1 && !opts[o].sent)
- fill_one(m, o, &offset);
+ if (fill_one(m, o, &offset) == OPT_MAX) {
+ break;
+ }
}
m->o[offset++] = 255;
@@ -398,6 +415,20 @@ int dhcp(const struct ctx *c, const struct pool *p)
if (!opts[6].slen)
opts[6].slen = -1;
+ size_t hostname_len = strlen(c->hostname);
+ if (hostname_len > 0) {
+ opts[12].slen = hostname_len;
+ memcpy(opts[12].s, &c->hostname, hostname_len);
+ }
+
+ size_t fqdn_len = strlen(c->fqdn);
+ if (fqdn_len > 0) {
+ opts[81].s[0] = 0x4; // flags (E)
+ size_t encoded_fqdn_len = encode_domain_name(c->fqdn, fqdn_len,
+ (char *) opts[81].s + 3);
+ opts[81].slen = encoded_fqdn_len + 3;
+ }
+
if (!c->no_dhcp_dns_search)
opt_set_dns_search(c, sizeof(m->o));
diff --git a/dhcpv6.c b/dhcpv6.c
index 0523bba..b61a41d 100644
--- a/dhcpv6.c
+++ b/dhcpv6.c
@@ -48,6 +48,7 @@ struct opt_hdr {
# define STATUS_NOTONLINK htons_constant(4)
# define OPT_DNS_SERVERS htons_constant(23)
# define OPT_DNS_SEARCH htons_constant(24)
+# define OPT_CLIENT_FQDN htons_constant(39)
#define STR_NOTONLINK "Prefix not appropriate for link."
uint16_t l;
@@ -163,6 +164,18 @@ struct opt_dns_search {
char list[MAXDNSRCH * NS_MAXDNAME];
} __attribute__((packed));
+/**
+ * struct opt_client_fqdn - Client FQDN option (RFC 4704)
+ * @hdr: Option header
+ * @flags: Flags as stated at RFC 4704 (always zero for us)
+ * @domain_name: Client FQDN
+ */
+struct opt_client_fqdn{
+ struct opt_hdr hdr;
+ uint8_t flags;
+ char domain_name[PASST_MAXDNAME];
+} __attribute__((packed));
+
/**
* struct msg_hdr - DHCPv6 client/server message header
* @type: DHCP message type
@@ -193,6 +206,7 @@ struct msg_hdr {
* @client_id: Client Identifier, variable length
* @dns_servers: DNS Recursive Name Server, here just for storage size
* @dns_search: Domain Search List, here just for storage size
+ * @client_fqdn: Client FQDN, variable length
*/
static struct resp_t {
struct msg_hdr hdr;
@@ -203,6 +217,7 @@ static struct resp_t {
struct opt_client_id client_id;
struct opt_dns_servers dns_servers;
struct opt_dns_search dns_search;
+ struct opt_client_fqdn client_fqdn;
} __attribute__((__packed__)) resp = {
{ 0 },
SERVER_ID,
@@ -228,6 +243,10 @@ static struct resp_t {
{ { OPT_DNS_SEARCH, 0, },
{ 0 },
},
+
+ { { OPT_CLIENT_FQDN, 0, },
+ 0, { 0 },
+ },
};
static const struct opt_status_code sc_not_on_link = {
@@ -346,7 +365,6 @@ static size_t dhcpv6_dns_fill(const struct ctx *c, char *buf, int offset)
{
struct opt_dns_servers *srv = NULL;
struct opt_dns_search *srch = NULL;
- char *p = NULL;
int i;
if (c->no_dhcp_dns)
@@ -388,29 +406,56 @@ search:
offset += sizeof(struct opt_hdr);
srch->hdr.t = OPT_DNS_SEARCH;
srch->hdr.l = 0;
- p = srch->list;
}
- *p = '.';
- p = stpncpy(p + 1, c->dns_search[i].n, name_len);
- p++;
- srch->hdr.l += name_len + 2;
- offset += name_len + 2;
+ size_t encoded_name_len = encode_domain_name(c->dns_search[i].n, name_len, srch->list);
+ srch->hdr.l += encoded_name_len;
+ offset += encoded_name_len;
}
if (srch) {
- for (i = 0; i < srch->hdr.l; i++) {
- if (srch->list[i] == '.') {
- srch->list[i] = strcspn(srch->list + i + 1,
- ".");
- }
- }
srch->hdr.l = htons(srch->hdr.l);
}
return offset;
}
+/**
+ * dhcpv6_client_fqdn_fill() - Fill in client FQDN option
+ * @c: Execution context
+ * @buf: Response message buffer where options will be appended
+ * @offset: Offset in message buffer for new options
+ *
+ * Return: updated length of response message buffer.
+ */
+static size_t dhcpv6_client_fqdn_fill(const struct ctx *c, char *buf, int offset)
+{
+ size_t fqdn_len, opt_hdr_len = 0;
+
+ fqdn_len = strlen(c->fqdn);
+ opt_hdr_len = sizeof(struct opt_hdr);
+
+ if (offset + opt_hdr_len + fqdn_len + 1 > 508) {
+ fqdn_len = 508 - (offset + opt_hdr_len + 1);
+ }
+
+ if (fqdn_len == 0) {
+ return offset;
+ }
+
+
+ struct opt_client_fqdn *o = (struct opt_client_fqdn *)(buf + offset);
+ size_t encoded_fqdn_len = encode_domain_name(c->fqdn, fqdn_len,
+ o->domain_name);
+ size_t opt_len = encoded_fqdn_len + 1;
+
+ o->hdr.t = OPT_CLIENT_FQDN;
+ o->hdr.l = htons(opt_len);
+ o->flags = 0x00;
+
+ return offset + opt_hdr_len + opt_len;
+}
+
/**
* dhcpv6() - Check if this is a DHCPv6 message, reply as needed
* @c: Execution context
@@ -544,6 +589,7 @@ int dhcpv6(struct ctx *c, const struct pool *p,
n = offsetof(struct resp_t, client_id) +
sizeof(struct opt_hdr) + ntohs(client_id->l);
n = dhcpv6_dns_fill(c, (char *)&resp, n);
+ n = dhcpv6_client_fqdn_fill(c, (char*)&resp, n);
resp.hdr.xid = mh->xid;
diff --git a/passt.1 b/passt.1
index d9cd33e..27e70a7 100644
--- a/passt.1
+++ b/passt.1
@@ -401,6 +401,17 @@ Enable IPv6-only operation. IPv4 traffic will be ignored.
By default, IPv4 operation is enabled as long as at least an IPv4 route and an
interface address are configured on a given host interface.
+.TP
+.BR \-H ", " \-\-hostname " " \fIname
+Hostname to configure client with.
+For dhcp client aware encode \fIname as DHCPv4 option 12 (hostname).
+
+.TP
+.BR \-\-fqdn " " \fIname
+FQDN to configure client with.
+For dhcp client aware encode \fIname as dhcp client fqdn option, for DHCPv4 option 81 and for
+DHCPv6 option 39.
+
.SS \fBpasst\fR-only options
.TP
diff --git a/passt.h b/passt.h
index 0dd4efa..577e734 100644
--- a/passt.h
+++ b/passt.h
@@ -91,6 +91,7 @@ struct fqdn {
char n[NS_MAXDNAME];
};
+
#include <net/if.h>
#include <linux/un.h>
@@ -209,6 +210,8 @@ struct ip6_ctx {
* @ifi4: Template interface for IPv4, -1: none, 0: IPv4 disabled
* @ip: IPv4 configuration
* @dns_search: DNS search list
+ * @hostname: Guest hostname
+ * @fqdn: Guest FQDN
* @ifi6: Template interface for IPv6, -1: none, 0: IPv6 disabled
* @ip6: IPv6 configuration
* @pasta_ifn: Name of namespace interface for pasta
@@ -268,6 +271,9 @@ struct ctx {
struct ip4_ctx ip4;
struct fqdn dns_search[MAXDNSRCH];
+
+ char hostname[PASST_MAXDNAME];
+ char fqdn[PASST_MAXDNAME];
int ifi6;
struct ip6_ctx ip6;
diff --git a/pasta.c b/pasta.c
index ff41c95..cdc9aa6 100644
--- a/pasta.c
+++ b/pasta.c
@@ -173,6 +173,7 @@ void pasta_open_ns(struct ctx *c, const char *netns)
struct pasta_spawn_cmd_arg {
const char *exe;
char *const *argv;
+ struct ctx *c;
};
/**
@@ -187,6 +188,7 @@ static int pasta_spawn_cmd(void *arg)
char hostname[HOST_NAME_MAX + 1] = HOSTNAME_PREFIX;
const struct pasta_spawn_cmd_arg *a;
sigset_t set;
+ size_t conf_hostname_len = 0;
/* We run in a detached PID and mount namespace: mount /proc over */
if (mount("", "/proc", "proc", 0, NULL))
@@ -194,8 +196,15 @@ static int pasta_spawn_cmd(void *arg)
if (write_file("/proc/sys/net/ipv4/ping_group_range", "0 0"))
warn("Cannot set ping_group_range, ICMP requests might fail");
-
- if (!gethostname(hostname + sizeof(HOSTNAME_PREFIX) - 1,
+
+ a = (const struct pasta_spawn_cmd_arg *)arg;
+
+ conf_hostname_len = strlen(a->c->hostname);
+ if (conf_hostname_len > 0) {
+ if (sethostname(a->c->hostname, conf_hostname_len))
+ warn("Unable to set configured hostname");
+ }
+ else if (!gethostname(hostname + sizeof(HOSTNAME_PREFIX) - 1,
HOST_NAME_MAX + 1 - sizeof(HOSTNAME_PREFIX)) ||
errno == ENAMETOOLONG) {
hostname[HOST_NAME_MAX] = '\0';
@@ -208,7 +217,6 @@ static int pasta_spawn_cmd(void *arg)
sigaddset(&set, SIGUSR1);
sigwaitinfo(&set, NULL);
- a = (const struct pasta_spawn_cmd_arg *)arg;
execvp(a->exe, a->argv);
die_perror("Failed to start command or shell");
@@ -230,6 +238,7 @@ void pasta_start_ns(struct ctx *c, uid_t uid, gid_t gid,
struct pasta_spawn_cmd_arg arg = {
.exe = argv[0],
.argv = argv,
+ .c = c,
};
char uidmap[BUFSIZ], gidmap[BUFSIZ];
char *sh_argv[] = { NULL, NULL };
diff --git a/test/lib/setup b/test/lib/setup
index 580825f..ee67152 100755
--- a/test/lib/setup
+++ b/test/lib/setup
@@ -49,7 +49,7 @@ setup_passt() {
context_run passt "make clean"
context_run passt "make valgrind"
- context_run_bg passt "valgrind --max-stackframe=$((4 * 1024 * 1024)) --trace-children=yes --vgdb=no --error-exitcode=1 --suppressions=test/valgrind.supp ./passt ${__opts} -s ${STATESETUP}/passt.socket -f -t 10001 -u 10001 -P ${STATESETUP}/passt.pid"
+ context_run_bg passt "valgrind --max-stackframe=$((4 * 1024 * 1024)) --trace-children=yes --vgdb=no --error-exitcode=1 --suppressions=test/valgrind.supp ./passt ${__opts} -s ${STATESETUP}/passt.socket -f -t 10001 -u 10001 -H hostname1 --fqdn fqdn1.passt.test -P ${STATESETUP}/passt.pid"
# pidfile isn't created until passt is listening
wait_for [ -f "${STATESETUP}/passt.pid" ]
@@ -160,11 +160,11 @@ setup_passt_in_ns() {
if [ ${VALGRIND} -eq 1 ]; then
context_run passt "make clean"
context_run passt "make valgrind"
- context_run_bg passt "valgrind --max-stackframe=$((4 * 1024 * 1024)) --trace-children=yes --vgdb=no --error-exitcode=1 --suppressions=test/valgrind.supp ./passt -f ${__opts} -s ${STATESETUP}/passt.socket -t 10001,10011,10021,10031 -u 10001,10011,10021,10031 -P ${STATESETUP}/passt.pid --map-host-loopback ${__map_ns4} --map-host-loopback ${__map_ns6}"
+ context_run_bg passt "valgrind --max-stackframe=$((4 * 1024 * 1024)) --trace-children=yes --vgdb=no --error-exitcode=1 --suppressions=test/valgrind.supp ./passt -f ${__opts} -s ${STATESETUP}/passt.socket -H hostname1 --fqdn fqdn1.passt.test -t 10001,10011,10021,10031 -u 10001,10011,10021,10031 -P ${STATESETUP}/passt.pid --map-host-loopback ${__map_ns4} --map-host-loopback ${__map_ns6}"
else
context_run passt "make clean"
context_run passt "make"
- context_run_bg passt "./passt -f ${__opts} -s ${STATESETUP}/passt.socket -t 10001,10011,10021,10031 -u 10001,10011,10021,10031 -P ${STATESETUP}/passt.pid --map-host-loopback ${__map_ns4} --map-host-loopback ${__map_ns6}"
+ context_run_bg passt "./passt -f ${__opts} -s ${STATESETUP}/passt.socket -H hostname1 --fqdn fqdn1.passt.test -t 10001,10011,10021,10031 -u 10001,10011,10021,10031 -P ${STATESETUP}/passt.pid --map-host-loopback ${__map_ns4} --map-host-loopback ${__map_ns6}"
fi
wait_for [ -f "${STATESETUP}/passt.pid" ]
@@ -243,7 +243,7 @@ setup_two_guests() {
[ ${TRACE} -eq 1 ] && __opts="${__opts} --trace"
[ ${VHOST_USER} -eq 1 ] && __opts="${__opts} --vhost-user"
- context_run_bg passt_1 "./passt -s ${STATESETUP}/passt_1.socket -P ${STATESETUP}/passt_1.pid -f ${__opts} -t 10001 -u 10001"
+ context_run_bg passt_1 "./passt -s ${STATESETUP}/passt_1.socket -P ${STATESETUP}/passt_1.pid -f ${__opts} --fqdn fqdn1.passt.test -H hostname1 -t 10001 -u 10001"
wait_for [ -f "${STATESETUP}/passt_1.pid" ]
__opts=
@@ -252,7 +252,7 @@ setup_two_guests() {
[ ${TRACE} -eq 1 ] && __opts="${__opts} --trace"
[ ${VHOST_USER} -eq 1 ] && __opts="${__opts} --vhost-user"
- context_run_bg passt_2 "./passt -s ${STATESETUP}/passt_2.socket -P ${STATESETUP}/passt_2.pid -f ${__opts} -t 10004 -u 10004"
+ context_run_bg passt_2 "./passt -s ${STATESETUP}/passt_2.socket -P ${STATESETUP}/passt_2.pid -f ${__opts} --hostname hostname2 --fqdn fqdn2 -t 10004 -u 10004"
wait_for [ -f "${STATESETUP}/passt_2.pid" ]
__vmem="$((${MEM_KIB} / 1024 / 4))"
diff --git a/test/passt.mbuto b/test/passt.mbuto
index 138d365..1e07693 100755
--- a/test/passt.mbuto
+++ b/test/passt.mbuto
@@ -13,7 +13,7 @@
PROGS="${PROGS:-ash,dash,bash ip mount ls insmod mkdir ln cat chmod lsmod
modprobe find grep mknod mv rm umount jq iperf3 dhclient hostname
sed tr chown sipcalc cut socat dd strace ping tail killall sleep sysctl
- nproc tcp_rr tcp_crr udp_rr which tee seq bc sshd ssh-keygen cmp}"
+ nproc tcp_rr tcp_crr udp_rr which tee seq bc sshd ssh-keygen cmp env}"
# OpenSSH 9.8 introduced split binaries, with sshd being the daemon, and
# sshd-session the per-session program. We need the latter as well, and the path
@@ -41,6 +41,7 @@ FIXUP="${FIXUP}"'
#!/bin/sh
LOG=/var/log/dhclient-script.log
echo \${reason} \${interface} >> \$LOG
+env >> \$LOG
set >> \$LOG
[ -n "\${new_interface_mtu}" ] && ip link set dev \${interface} mtu \${new_interface_mtu}
@@ -54,7 +55,8 @@ set >> \$LOG
[ -n "\${new_ip6_address}" ] && ip addr add \${new_ip6_address}/\${new_ip6_prefixlen} dev \${interface}
[ -n "\${new_dhcp6_name_servers}" ] && for d in \${new_dhcp6_name_servers}; do echo "nameserver \${d}%\${interface}" >> /etc/resolv.conf; done
[ -n "\${new_dhcp6_domain_search}" ] && (printf "search"; for d in \${new_dhcp6_domain_search}; do printf " %s" "\${d}"; done; printf "\n") >> /etc/resolv.conf
-[ -n "\${new_host_name}" ] && hostname "\${new_host_name}"
+[ -n "\${new_host_name}" ] && echo "\${new_host_name}" > /tmp/new_host_name
+[ -n "\${new_fqdn_fqdn}" ] && echo "\${new_fqdn_fqdn}" > /tmp/new_fqdn_fqdn
exit 0
EOF
chmod 755 /sbin/dhclient-script
diff --git a/test/passt/dhcp b/test/passt/dhcp
index 9925ab9..145f1ba 100644
--- a/test/passt/dhcp
+++ b/test/passt/dhcp
@@ -11,7 +11,7 @@
# Copyright (c) 2021 Red Hat GmbH
# Author: Stefano Brivio <sbrivio(a)redhat.com>
-gtools ip jq dhclient sed tr
+gtools ip jq dhclient sed tr hostname
htools ip jq sed tr head
test Interface name
@@ -47,7 +47,16 @@ gout SEARCH sed 's/\. / /g' /etc/resolv.conf | sed 's/\.$//g' | sed -n 's/^searc
hout HOST_SEARCH sed 's/\. / /g' /etc/resolv.conf | sed 's/\.$//g' | sed -n 's/^search \(.*\)/\1/p' | tr ' \n' ',' | sed 's/,$//;s/$/\n/'
check [ "__SEARCH__" = "__HOST_SEARCH__" ]
+test DHCP: Hostname
+gout NEW_HOST_NAME cat /tmp/new_host_name
+check [ "__NEW_HOST_NAME__" = "hostname1" ]
+
+test DHCP: Client FQDN
+gout NEW_FQDN_FQDN cat /tmp/new_fqdn_fqdn
+check [ "__NEW_FQDN_FQDN__" = "fqdn1.passt.test" ]
+
test DHCPv6: address
+guest rm /tmp/new_fqdn_fqdn
guest /sbin/dhclient -6 __IFNAME__
# Wait for DAD to complete
guest while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
@@ -70,3 +79,7 @@ test DHCPv6: search list
gout SEARCH6 sed 's/\. / /g' /etc/resolv.conf | sed 's/\.$//g' | sed -n 's/^search \(.*\)/\1/p' | tr ' \n' ',' | sed 's/,$//;s/$/\n/'
hout HOST_SEARCH6 sed 's/\. / /g' /etc/resolv.conf | sed 's/\.$//g' | sed -n 's/^search \(.*\)/\1/p' | tr ' \n' ',' | sed 's/,$//;s/$/\n/'
check [ "__SEARCH6__" = "__HOST_SEARCH6__" ]
+
+test DHCPv6: Hostname
+gout NEW_FQDN_FQDN cat /tmp/new_fqdn_fqdn
+check [ "__NEW_FQDN_FQDN__" = "fqdn1.passt.test" ]
diff --git a/util.c b/util.c
index 11973c4..f3cf7b1 100644
--- a/util.c
+++ b/util.c
@@ -837,3 +837,25 @@ void raw_random(void *buf, size_t buflen)
if (random_read < buflen)
die("Unexpected EOF on random data source");
}
+/**
+ * encode_domain_name() - Encode domain name according to RFC 1035, section 3.1
+ * @domain_name: Input domain name to encode
+ * @len: Domain name length
+ * @buf: Buffer to fill in with encoded domain name
+ *
+ * Return: encoded domain name length
+ */
+size_t encode_domain_name(const char *domain_name, size_t len, char *buf){
+ char *p = NULL;
+ size_t i = 0;
+
+ buf[0] = strcspn(domain_name, ".");
+ p = buf + 1;
+ for (i = 0; i < len; i++) {
+ if (domain_name[i] == '.')
+ p[i] = strcspn(domain_name + i + 1, ".");
+ else
+ p[i] = domain_name[i];
+ }
+ return len + 2;
+}
diff --git a/util.h b/util.h
index 3fa1d12..d7751a8 100644
--- a/util.h
+++ b/util.h
@@ -346,4 +346,7 @@ static inline int wrap_accept4(int sockfd, struct sockaddr *addr,
#define accept4(s, addr, addrlen, flags) \
wrap_accept4((s), (addr), (addrlen), (flags))
+#define PASST_MAXDNAME 253 /* RFC 1035 */
+size_t encode_domain_name(const char* domain_name, size_t len, char* buf);
+
#endif /* UTIL_H */
--
2.47.0
3
5
We don't modify the structure in some virtio functions.
Signed-off-by: Laurent Vivier <lvivier(a)redhat.com>
---
virtio.c | 14 +++++++++-----
virtio.h | 2 +-
vu_common.c | 2 +-
vu_common.h | 2 +-
4 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/virtio.c b/virtio.c
index a76de5e00222..625bac385f0c 100644
--- a/virtio.c
+++ b/virtio.c
@@ -92,7 +92,8 @@
*
* Return: virtual address in our address space of the guest physical address
*/
-static void *vu_gpa_to_va(struct vu_dev *dev, uint64_t *plen, uint64_t guest_addr)
+static void *vu_gpa_to_va(const struct vu_dev *dev, uint64_t *plen,
+ uint64_t guest_addr)
{
unsigned int i;
@@ -210,7 +211,8 @@ static void virtqueue_get_head(const struct vu_virtq *vq,
*
* Return: -1 if there is an error, 0 otherwise
*/
-static int virtqueue_read_indirect_desc(struct vu_dev *dev, struct vring_desc *desc,
+static int virtqueue_read_indirect_desc(const struct vu_dev *dev,
+ struct vring_desc *desc,
uint64_t addr, size_t len)
{
uint64_t read_len;
@@ -390,7 +392,7 @@ static inline void vring_set_avail_event(const struct vu_virtq *vq,
*
* Return: false on error, true otherwise
*/
-static bool virtqueue_map_desc(struct vu_dev *dev,
+static bool virtqueue_map_desc(const struct vu_dev *dev,
unsigned int *p_num_sg, struct iovec *iov,
unsigned int max_num_sg,
uint64_t pa, size_t sz)
@@ -426,7 +428,8 @@ static bool virtqueue_map_desc(struct vu_dev *dev,
*
* Return: -1 if there is an error, 0 otherwise
*/
-static int vu_queue_map_desc(struct vu_dev *dev, struct vu_virtq *vq, unsigned int idx,
+static int vu_queue_map_desc(const struct vu_dev *dev,
+ struct vu_virtq *vq, unsigned int idx,
struct vu_virtq_element *elem)
{
const struct vring_desc *desc = vq->vring.desc;
@@ -504,7 +507,8 @@ static int vu_queue_map_desc(struct vu_dev *dev, struct vu_virtq *vq, unsigned i
*
* Return: -1 if there is an error, 0 otherwise
*/
-int vu_queue_pop(struct vu_dev *dev, struct vu_virtq *vq, struct vu_virtq_element *elem)
+int vu_queue_pop(const struct vu_dev *dev, struct vu_virtq *vq,
+ struct vu_virtq_element *elem)
{
unsigned int head;
int ret;
diff --git a/virtio.h b/virtio.h
index 6410d60f9b3f..0af259df7dac 100644
--- a/virtio.h
+++ b/virtio.h
@@ -170,7 +170,7 @@ static inline bool vu_has_protocol_feature(const struct vu_dev *vdev,
bool vu_queue_empty(struct vu_virtq *vq);
void vu_queue_notify(const struct vu_dev *dev, struct vu_virtq *vq);
-int vu_queue_pop(struct vu_dev *dev, struct vu_virtq *vq,
+int vu_queue_pop(const struct vu_dev *dev, struct vu_virtq *vq,
struct vu_virtq_element *elem);
void vu_queue_detach_element(struct vu_virtq *vq);
void vu_queue_unpop(struct vu_virtq *vq);
diff --git a/vu_common.c b/vu_common.c
index 299b5a32e244..6d365bea5fe2 100644
--- a/vu_common.c
+++ b/vu_common.c
@@ -73,7 +73,7 @@ void vu_init_elem(struct vu_virtq_element *elem, struct iovec *iov, int elem_cnt
*
* Return: number of elements used to contain the frame
*/
-int vu_collect(struct vu_dev *vdev, struct vu_virtq *vq,
+int vu_collect(const struct vu_dev *vdev, struct vu_virtq *vq,
struct vu_virtq_element *elem, int max_elem,
size_t size, size_t *frame_size)
{
diff --git a/vu_common.h b/vu_common.h
index 901d97216c67..bd70faf3e226 100644
--- a/vu_common.h
+++ b/vu_common.h
@@ -46,7 +46,7 @@ static inline void vu_set_element(struct vu_virtq_element *elem,
void vu_init_elem(struct vu_virtq_element *elem, struct iovec *iov,
int elem_cnt);
-int vu_collect(struct vu_dev *vdev, struct vu_virtq *vq,
+int vu_collect(const struct vu_dev *vdev, struct vu_virtq *vq,
struct vu_virtq_element *elem, int max_elem, size_t size,
size_t *frame_size);
void vu_set_vnethdr(const struct vu_dev *vdev,
--
2.47.1
2
1
The new version with tag 2024_12_11.09478d5 includes the following changes:
09478d5 treewide: Dodge dynamic memory allocation in strerror() from glibc > 2.40
e24f026 pasta: make it possible to disable socket splicing
947f5cd tap: Call vu_init() with --fd
2139ad3 tap: Use a common function to start a new connection
8996d18 udp_vu: update segment size
1908297 flow: Remove over-zealous sanity checks in flow_sidx_hash()
1db4f77 udp: Improve detail of UDP endpoint sanity checking
966fdc8 perf/passt_vu_tcp: Make it shine
020c8b7 tcp_vu: Compute IPv4 header checksum if dlen changes
d9c0f8e Makefile: Use make internal string functions
b6e79ef tcp_vu: Remove unnecessary tcp_vu_update_check() function
a6348ca tcp: Merge tcp_fill_headers[46]() with each other
2abf5ab tcp: Merge tcp_update_check_tcp[46]()
08ea3cc tcp: Pass TCP header and payload separately to tcp_fill_headers[46]()
2ee0769 tcp: Pass TCP header and payload separately to tcp_update_check_tcp[46]()
6715109 iov, checksum: Replace csum_iov() with csum_iov_tail()
f931103 iov: iov tail helpers
804a7ce tcp_vu: Change 'dlen' to ssize_t in tcp_vu_data_from_sock()
00cc230 Fix build on 32bit target
6fae899 virtio: check if avail ring is configured
7e131e9 tcp: Move tcp_l2_buf_fill_headers() to tcp_buf.c
676bf54 test: Add tests for passt in vhost-user mode
28997fc vhost-user: add vhost-user
b2e62f7 passt: rename tap_sock_init() to tap_backend_init()
b7c292b tcp: Export headers functions
5a8b33c udp: Prepare udp.c to be shared with vhost-user
31117b2 vhost-user: introduce vhost-user API
7d1cd4d vhost-user: introduce virtio API
dd143e3 packet: replace struct desc by struct iovec
https://passt.top/passt/log/?qt=range&q=2024_11_27.c0fbc7e..2024_12_11.0947…
Packages:
- Alpine Linux:
https://pkgs.alpinelinux.org/packages?name=passt
- Arch Linux:
https://www.archlinux.org/packages/extra/x86_64/passt/
https://archlinuxarm.org/packages/aarch64/passt
https://archlinuxarm.org/packages/armv7h/passt
- Chimera:
https://pkgs.chimera-linux.org/packages?name=passt
- Clear Linux:
https://github.com/clearlinux-pkgs/passt/
- Copr (CentOS Stream, EPEL, Fedora, Mageia):
https://copr.fedorainfracloud.org/coprs/sbrivio/passt/build/8382137/
permanent mirror: https://passt.top/builds/copr/0^20241211.g09478d5/
- Debian tracker:
https://tracker.debian.org/pkg/passt
- Fedora updates:
https://bodhi.fedoraproject.org/updates/?packages=passt
- Gentoo versions:
https://packages.gentoo.org/packages/net-misc/passt
- GNU Guix:
https://packages.guix.gnu.org/packages/passt/
- Homebrew:
https://formulae.brew.sh/formula/passt
- NixOS:
https://github.com/NixOS/nixpkgs/tree/nixos-unstable/pkgs/by-name/pa/passt
- openSUSE:
https://software.opensuse.org/package/passt
- OpenMandriva:
https://github.com/OpenMandrivaAssociation/passt/tree/master
- PLD Linux:
https://git.pld-linux.org/cgi-bin/gitweb.cgi?p=packages/passt.git
- Solus:
https://github.com/getsolus/packages/tree/main/packages/p/passt
- Ubuntu tracker:
https://launchpad.net/ubuntu/+source/passt
- Void Linux:
https://voidlinux.org/packages/?q=passt
- Static builds:
- Package for other RPM-based distributions, x86_64 only:
https://passt.top/builds/latest/x86_64/passt-g09478d5-1.x86_64.rpm
- x86_64 static binaries:
https://passt.top/builds/latest/x86_64/
- Debian package, from x86_64 static build:
https://passt.top/builds/latest/x86_64/passt_09478d5-1_all.deb
--
Stefano
1
0
[PATCH] treewide: Dodge dynamic memory allocation in strerror() from glibc > 2.40
by Stefano Brivio 11 Dec '24
by Stefano Brivio 11 Dec '24
11 Dec '24
With glibc commit 25a5eb4010df ("string: strerror, strsignal cannot
use buffer after dlmopen (bug 32026)"), strerror() now needs, at least
on x86, the getrandom() and brk() system calls, in order to fill in
the locale-translated error message. But getrandom() and brk() are not
allowed by our seccomp profiles.
This became visible on Fedora Rawhide with the "podman login and
logout" Podman tests, defined at test/e2e/login_logout_test.go in the
Podman source tree, where pasta would terminate upon printing error
descriptions (at least the ones related to the SO_ERROR queue for
spliced connections).
Avoid dynamic memory allocation by calling strerrordesc_np() instead,
which is a GNU function returning a static, untranslated version of
the error description. If it's not available, keep calling strerror(),
which at that point should be simple enough as to be usable (at least,
that's currently the case for musl).
Reported-by: Paul Holzinger <pholzing(a)redhat.com>
Link: https://github.com/containers/podman/issues/24804
Analysed-by: Paul Holzinger <pholzing(a)redhat.com>
Signed-off-by: Stefano Brivio <sbrivio(a)redhat.com>
---
conf.c | 10 +++++-----
icmp.c | 4 ++--
log.c | 2 +-
netlink.c | 2 +-
pasta.c | 22 +++++++++++-----------
tcp.c | 22 +++++++++++-----------
tcp_splice.c | 16 ++++++++--------
udp.c | 5 +++--
udp_flow.c | 8 ++++----
util.c | 6 +++---
util.h | 32 ++++++++++++++++++++++++++++++++
11 files changed, 81 insertions(+), 48 deletions(-)
diff --git a/conf.c b/conf.c
index eaa7d99..6d03111 100644
--- a/conf.c
+++ b/conf.c
@@ -365,7 +365,7 @@ mode_conflict:
die("Port forwarding mode '%s' conflicts with previous mode", optarg);
bind_fail:
die("Failed to bind port %u (%s) for option '-%c %s', exiting",
- i, strerror(-ret), optname, optarg);
+ i, __strerror(-ret), optname, optarg);
bind_all_fail:
die("Failed to bind any port for '-%c %s', exiting", optname, optarg);
}
@@ -655,7 +655,7 @@ static unsigned int conf_ip4(unsigned int ifi, struct ip4_ctx *ip4)
&ip4->guest_gw);
if (rc < 0) {
debug("Couldn't discover IPv4 gateway address: %s",
- strerror(-rc));
+ __strerror(-rc));
return 0;
}
}
@@ -665,7 +665,7 @@ static unsigned int conf_ip4(unsigned int ifi, struct ip4_ctx *ip4)
&ip4->addr, &ip4->prefix_len, NULL);
if (rc < 0) {
debug("Couldn't discover IPv4 address: %s",
- strerror(-rc));
+ __strerror(-rc));
return 0;
}
}
@@ -729,7 +729,7 @@ static unsigned int conf_ip6(unsigned int ifi, struct ip6_ctx *ip6)
rc = nl_route_get_def(nl_sock, ifi, AF_INET6, &ip6->guest_gw);
if (rc < 0) {
debug("Couldn't discover IPv6 gateway address: %s",
- strerror(-rc));
+ __strerror(-rc));
return 0;
}
}
@@ -738,7 +738,7 @@ static unsigned int conf_ip6(unsigned int ifi, struct ip6_ctx *ip6)
IN6_IS_ADDR_UNSPECIFIED(&ip6->addr) ? &ip6->addr : NULL,
&prefix_len, &ip6->our_tap_ll);
if (rc < 0) {
- debug("Couldn't discover IPv6 address: %s", strerror(-rc));
+ debug("Couldn't discover IPv6 address: %s", __strerror(-rc));
return 0;
}
diff --git a/icmp.c b/icmp.c
index f514dbc..bb0e30b 100644
--- a/icmp.c
+++ b/icmp.c
@@ -85,7 +85,7 @@ void icmp_sock_handler(const struct ctx *c, union epoll_ref ref)
n = recvfrom(ref.fd, buf, sizeof(buf), 0, &sr.sa, &sl);
if (n < 0) {
- flow_err(pingf, "recvfrom() error: %s", strerror(errno));
+ flow_err(pingf, "recvfrom() error: %s", __strerror(errno));
return;
}
@@ -301,7 +301,7 @@ int icmp_tap_handler(const struct ctx *c, uint8_t pif, sa_family_t af,
pif_sockaddr(c, &sa, &sl, PIF_HOST, &tgt->eaddr, 0);
if (sendto(pingf->sock, pkt, l4len, MSG_NOSIGNAL, &sa.sa, sl) < 0) {
flow_dbg(pingf, "failed to relay request to socket: %s",
- strerror(errno));
+ __strerror(errno));
} else {
flow_dbg(pingf,
"echo request to socket, ID: %"PRIu16", seq: %"PRIu16,
diff --git a/log.c b/log.c
index 239c8ce..c0eae4f 100644
--- a/log.c
+++ b/log.c
@@ -322,7 +322,7 @@ void logmsg_perror(int pri, const char *format, ...)
vlogmsg(false, false, pri, format, ap);
va_end(ap);
- logmsg(true, true, pri, ": %s", strerror(errno_copy));
+ logmsg(true, true, pri, ": %s", __strerror(errno_copy));
}
/**
diff --git a/netlink.c b/netlink.c
index 4aba2a3..983f45b 100644
--- a/netlink.c
+++ b/netlink.c
@@ -320,7 +320,7 @@ unsigned int nl_get_ext_if(int s, sa_family_t af)
}
if (status < 0)
- warn("netlink: RTM_GETROUTE failed: %s", strerror(-status));
+ warn("netlink: RTM_GETROUTE failed: %s", __strerror(-status));
if (defifi) {
if (ndef > 1) {
diff --git a/pasta.c b/pasta.c
index 96dacc3..d3fb36c 100644
--- a/pasta.c
+++ b/pasta.c
@@ -296,7 +296,7 @@ void pasta_ns_conf(struct ctx *c)
rc = nl_link_set_flags(nl_sock_ns, 1 /* lo */, IFF_UP, IFF_UP);
if (rc < 0)
die("Couldn't bring up loopback interface in namespace: %s",
- strerror(-rc));
+ __strerror(-rc));
/* Get or set MAC in target namespace */
if (MAC_IS_ZERO(c->guest_mac))
@@ -305,7 +305,7 @@ void pasta_ns_conf(struct ctx *c)
rc = nl_link_set_mac(nl_sock_ns, c->pasta_ifi, c->guest_mac);
if (rc < 0)
die("Couldn't set MAC address in namespace: %s",
- strerror(-rc));
+ __strerror(-rc));
if (c->pasta_conf_ns) {
unsigned int flags = IFF_UP;
@@ -332,7 +332,7 @@ void pasta_ns_conf(struct ctx *c)
if (rc < 0) {
die("Couldn't set IPv4 address(es) in namespace: %s",
- strerror(-rc));
+ __strerror(-rc));
}
if (c->ip4.no_copy_routes) {
@@ -346,7 +346,7 @@ void pasta_ns_conf(struct ctx *c)
if (rc < 0) {
die("Couldn't set IPv4 route(s) in guest: %s",
- strerror(-rc));
+ __strerror(-rc));
}
}
@@ -355,13 +355,13 @@ void pasta_ns_conf(struct ctx *c)
&c->ip6.addr_ll_seen);
if (rc < 0) {
warn("Can't get LL address from namespace: %s",
- strerror(-rc));
+ __strerror(-rc));
}
rc = nl_addr_set_ll_nodad(nl_sock_ns, c->pasta_ifi);
if (rc < 0) {
warn("Can't set nodad for LL in namespace: %s",
- strerror(-rc));
+ __strerror(-rc));
}
/* We dodged DAD: re-enable neighbour solicitations */
@@ -382,7 +382,7 @@ void pasta_ns_conf(struct ctx *c)
if (rc < 0) {
die("Couldn't set IPv6 address(es) in namespace: %s",
- strerror(-rc));
+ __strerror(-rc));
}
if (c->ip6.no_copy_routes) {
@@ -397,7 +397,7 @@ void pasta_ns_conf(struct ctx *c)
if (rc < 0) {
die("Couldn't set IPv6 route(s) in guest: %s",
- strerror(-rc));
+ __strerror(-rc));
}
}
}
@@ -446,18 +446,18 @@ void pasta_netns_quit_init(const struct ctx *c)
return;
if ((dir_fd = open(c->netns_dir, O_CLOEXEC | O_RDONLY)) < 0)
- die("netns dir open: %s, exiting", strerror(errno));
+ die("netns dir open: %s, exiting", __strerror(errno));
if (fstatfs(dir_fd, &s) || s.f_type == DEVPTS_SUPER_MAGIC ||
s.f_type == PROC_SUPER_MAGIC || s.f_type == SYSFS_MAGIC)
try_inotify = false;
if (try_inotify && (fd = inotify_init1(flags)) < 0)
- warn("inotify_init1(): %s, use a timer", strerror(errno));
+ warn("inotify_init1(): %s, use a timer", __strerror(errno));
if (fd >= 0 && inotify_add_watch(fd, c->netns_dir, IN_DELETE) < 0) {
warn("inotify_add_watch(): %s, use a timer",
- strerror(errno));
+ __strerror(errno));
close(fd);
fd = -1;
}
diff --git a/tcp.c b/tcp.c
index 1872ccb..640b25a 100644
--- a/tcp.c
+++ b/tcp.c
@@ -516,7 +516,7 @@ static void tcp_timer_ctl(const struct ctx *c, struct tcp_tap_conn *conn)
fd = timerfd_create(CLOCK_MONOTONIC, 0);
if (fd == -1 || fd > FD_REF_MAX) {
flow_dbg(conn, "failed to get timer: %s",
- strerror(errno));
+ __strerror(errno));
if (fd > -1)
close(fd);
conn->timer = -1;
@@ -526,7 +526,7 @@ static void tcp_timer_ctl(const struct ctx *c, struct tcp_tap_conn *conn)
if (epoll_ctl(c->epollfd, EPOLL_CTL_ADD, conn->timer, &ev)) {
flow_dbg(conn, "failed to add timer: %s",
- strerror(errno));
+ __strerror(errno));
close(conn->timer);
conn->timer = -1;
return;
@@ -551,7 +551,7 @@ static void tcp_timer_ctl(const struct ctx *c, struct tcp_tap_conn *conn)
(unsigned long long)it.it_value.tv_nsec / 1000 / 1000);
if (timerfd_settime(conn->timer, 0, &it, NULL))
- flow_err(conn, "failed to set timer: %s", strerror(errno));
+ flow_err(conn, "failed to set timer: %s", __strerror(errno));
}
/**
@@ -1307,7 +1307,7 @@ int tcp_conn_sock(const struct ctx *c, sa_family_t af)
return s;
err("TCP: Unable to open socket for new connection: %s",
- strerror(-s));
+ __strerror(-s));
return -1;
}
@@ -1360,7 +1360,7 @@ static void tcp_bind_outbound(const struct ctx *c,
flow_dbg(conn,
"Can't bind TCP outbound socket to %s:%hu: %s",
inany_ntop(&tgt->oaddr, sstr, sizeof(sstr)),
- tgt->oport, strerror(errno));
+ tgt->oport, __strerror(errno));
}
}
@@ -1371,7 +1371,7 @@ static void tcp_bind_outbound(const struct ctx *c,
strlen(c->ip4.ifname_out))) {
flow_dbg(conn, "Can't bind IPv4 TCP socket to"
" interface %s: %s", c->ip4.ifname_out,
- strerror(errno));
+ __strerror(errno));
}
}
} else if (bind_sa.sa_family == AF_INET6) {
@@ -1381,7 +1381,7 @@ static void tcp_bind_outbound(const struct ctx *c,
strlen(c->ip6.ifname_out))) {
flow_dbg(conn, "Can't bind IPv6 TCP socket to"
" interface %s: %s", c->ip6.ifname_out,
- strerror(errno));
+ __strerror(errno));
}
}
}
@@ -2113,7 +2113,7 @@ void tcp_timer_handler(const struct ctx *c, union epoll_ref ref)
* and we just set the timer to a new point in the future: discard it.
*/
if (timerfd_gettime(conn->timer, &check_armed))
- flow_err(conn, "failed to read timer: %s", strerror(errno));
+ flow_err(conn, "failed to read timer: %s", __strerror(errno));
if (check_armed.it_value.tv_sec || check_armed.it_value.tv_nsec)
return;
@@ -2154,7 +2154,7 @@ void tcp_timer_handler(const struct ctx *c, union epoll_ref ref)
*/
if (timerfd_settime(conn->timer, 0, &new, &old))
flow_err(conn, "failed to set timer: %s",
- strerror(errno));
+ __strerror(errno));
if (old.it_value.tv_sec == ACT_TIMEOUT) {
flow_dbg(conn, "activity timeout");
@@ -2422,13 +2422,13 @@ static void tcp_sock_refill_init(const struct ctx *c)
int rc = tcp_sock_refill_pool(c, init_sock_pool4, AF_INET);
if (rc < 0)
warn("TCP: Error refilling IPv4 host socket pool: %s",
- strerror(-rc));
+ __strerror(-rc));
}
if (c->ifi6) {
int rc = tcp_sock_refill_pool(c, init_sock_pool6, AF_INET6);
if (rc < 0)
warn("TCP: Error refilling IPv6 host socket pool: %s",
- strerror(-rc));
+ __strerror(-rc));
}
}
diff --git a/tcp_splice.c b/tcp_splice.c
index 93f8bce..d35bfd5 100644
--- a/tcp_splice.c
+++ b/tcp_splice.c
@@ -160,7 +160,7 @@ static int tcp_splice_epoll_ctl(const struct ctx *c,
if (epoll_ctl(c->epollfd, m, conn->s[0], &ev[0]) ||
epoll_ctl(c->epollfd, m, conn->s[1], &ev[1])) {
int ret = -errno;
- flow_err(conn, "ERROR on epoll_ctl(): %s", strerror(errno));
+ flow_err(conn, "ERROR on epoll_ctl(): %s", __strerror(errno));
return ret;
}
@@ -314,7 +314,7 @@ static int tcp_splice_connect_finish(const struct ctx *c,
if (conn->pipe[sidei][0] < 0) {
if (pipe2(conn->pipe[sidei], O_NONBLOCK | O_CLOEXEC)) {
flow_err(conn, "cannot create %d->%d pipe: %s",
- sidei, !sidei, strerror(errno));
+ sidei, !sidei, __strerror(errno));
conn_flag(c, conn, CLOSING);
return -EIO;
}
@@ -370,7 +370,7 @@ static int tcp_splice_connect(const struct ctx *c, struct tcp_splice_conn *conn)
if (connect(conn->s[1], &sa.sa, sl)) {
if (errno != EINPROGRESS) {
flow_trace(conn, "Couldn't connect socket for splice: %s",
- strerror(errno));
+ __strerror(errno));
return -errno;
}
@@ -469,10 +469,10 @@ void tcp_splice_sock_handler(struct ctx *c, union epoll_ref ref,
rc = getsockopt(ref.fd, SOL_SOCKET, SO_ERROR, &err, &sl);
if (rc)
flow_err(conn, "Error retrieving SO_ERROR: %s",
- strerror(errno));
+ __strerror(errno));
else
flow_trace(conn, "Error event on socket: %s",
- strerror(err));
+ __strerror(err));
goto close;
}
@@ -551,7 +551,7 @@ eintr:
&lowat, sizeof(lowat))) {
flow_trace(conn,
"Setting SO_RCVLOWAT %i: %s",
- lowat, strerror(errno));
+ lowat, __strerror(errno));
} else {
conn_flag(c, conn, lowat_set_flag);
conn_flag(c, conn, lowat_act_flag);
@@ -696,13 +696,13 @@ static int tcp_sock_refill_ns(void *arg)
int rc = tcp_sock_refill_pool(c, ns_sock_pool4, AF_INET);
if (rc < 0)
warn("TCP: Error refilling IPv4 ns socket pool: %s",
- strerror(-rc));
+ __strerror(-rc));
}
if (c->ifi6) {
int rc = tcp_sock_refill_pool(c, ns_sock_pool6, AF_INET6);
if (rc < 0)
warn("TCP: Error refilling IPv6 ns socket pool: %s",
- strerror(-rc));
+ __strerror(-rc));
}
return 0;
diff --git a/udp.c b/udp.c
index c89f031..0154a1a 100644
--- a/udp.c
+++ b/udp.c
@@ -453,7 +453,7 @@ static int udp_sock_recverr(int s)
/* TODO: When possible propagate and otherwise handle errors */
debug("%s error on UDP socket %i: %s",
- str_ee_origin(ee), s, strerror(ee->ee_errno));
+ str_ee_origin(ee), s, __strerror(ee->ee_errno));
return 1;
}
@@ -492,7 +492,8 @@ int udp_sock_errs(const struct ctx *c, int s, uint32_t events)
}
if (err) {
- debug("Unqueued error on UDP socket %i: %s", s, strerror(err));
+ debug("Unqueued error on UDP socket %i: %s", s,
+ __strerror(err));
n_err++;
}
diff --git a/udp_flow.c b/udp_flow.c
index c8fdb5f..627cc15 100644
--- a/udp_flow.c
+++ b/udp_flow.c
@@ -95,7 +95,7 @@ static flow_sidx_t udp_flow_new(const struct ctx *c, union flow *flow,
if (uflow->s[INISIDE] < 0) {
flow_err(uflow,
"Couldn't duplicate listening socket: %s",
- strerror(errno));
+ __strerror(errno));
goto cancel;
}
}
@@ -115,14 +115,14 @@ static flow_sidx_t udp_flow_new(const struct ctx *c, union flow *flow,
if (uflow->s[TGTSIDE] < 0) {
flow_dbg(uflow,
"Couldn't open socket for spliced flow: %s",
- strerror(errno));
+ __strerror(errno));
goto cancel;
}
if (flowside_connect(c, uflow->s[TGTSIDE], tgtpif, tgt) < 0) {
flow_dbg(uflow,
"Couldn't connect flow socket: %s",
- strerror(errno));
+ __strerror(errno));
goto cancel;
}
@@ -144,7 +144,7 @@ static flow_sidx_t udp_flow_new(const struct ctx *c, union flow *flow,
} else if (errno != EAGAIN) {
flow_err(uflow,
"Unexpected error discarding datagrams: %s",
- strerror(errno));
+ __strerror(errno));
}
}
diff --git a/util.c b/util.c
index 55cae3f..386bf76 100644
--- a/util.c
+++ b/util.c
@@ -90,7 +90,7 @@ int sock_l4_sa(const struct ctx *c, enum epoll_type type,
ret = -errno;
if (fd < 0) {
- warn("L4 socket: %s", strerror(-ret));
+ warn("L4 socket: %s", __strerror(-ret));
return ret;
}
@@ -162,7 +162,7 @@ int sock_l4_sa(const struct ctx *c, enum epoll_type type,
if (type == EPOLL_TYPE_TCP_LISTEN && listen(fd, 128) < 0) {
ret = -errno;
- warn("TCP socket listen: %s", strerror(-ret));
+ warn("TCP socket listen: %s", __strerror(-ret));
close(fd);
return ret;
}
@@ -171,7 +171,7 @@ int sock_l4_sa(const struct ctx *c, enum epoll_type type,
ev.data.u64 = ref.u64;
if (epoll_ctl(c->epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
ret = -errno;
- warn("L4 epoll_ctl: %s", strerror(-ret));
+ warn("L4 epoll_ctl: %s", __strerror(-ret));
return ret;
}
diff --git a/util.h b/util.h
index 41bbd60..b766dc1 100644
--- a/util.h
+++ b/util.h
@@ -274,6 +274,38 @@ static inline bool mod_between(unsigned x, unsigned i, unsigned j, unsigned m)
void raw_random(void *buf, size_t buflen);
+/*
+ * Starting from glibc 2.40.9000 and commit 25a5eb4010df ("string: strerror,
+ * strsignal cannot use buffer after dlmopen (bug 32026)"), strerror() needs
+ * getrandom(2) and brk(2) as it allocates memory for the locale-translated
+ * error description, but our seccomp profiles forbid both.
+ *
+ * Use the __strerror() wrapper instead, calling into strerrordesc_np() to get
+ * a static untranslated string. It's a GNU implementation, but also defined by
+ * bionic.
+ *
+ * If strerrordesc_np() is not defined (e.g. musl), call strerror(). C libraries
+ * not defining strerrordesc_np() are expected to provide strerror()
+ * implementations that are simple enough for us to call.
+ */
+__attribute__ ((weak)) const char *strerrordesc_np(int errnum);
+
+/**
+ * __strerror() - strerror() wrapper calling strerrordesc_np() if available
+ * @errnum: Error code
+ *
+ * Return: error description string
+ */
+static inline const char *__strerror(int errnum)
+{
+ if (strerrordesc_np)
+ return strerrordesc_np(errnum);
+
+ return strerror(errnum);
+}
+
+#define strerror(x) @ "Don't call strerror() directly, use __strerror() instead"
+
/*
* Workarounds for https://github.com/llvm/llvm-project/issues/58992
*
--
2.43.0
3
5
[PATCH v2] treewide: Dodge dynamic memory allocation in strerror() from glibc > 2.40
by Stefano Brivio 11 Dec '24
by Stefano Brivio 11 Dec '24
11 Dec '24
With glibc commit 25a5eb4010df ("string: strerror, strsignal cannot
use buffer after dlmopen (bug 32026)"), strerror() now needs, at least
on x86, the getrandom() and brk() system calls, in order to fill in
the locale-translated error message. But getrandom() and brk() are not
allowed by our seccomp profiles.
This became visible on Fedora Rawhide with the "podman login and
logout" Podman tests, defined at test/e2e/login_logout_test.go in the
Podman source tree, where pasta would terminate upon printing error
descriptions (at least the ones related to the SO_ERROR queue for
spliced connections).
Avoid dynamic memory allocation by calling strerrordesc_np() instead,
which is a GNU function returning a static, untranslated version of
the error description. If it's not available, keep calling strerror(),
which at that point should be simple enough as to be usable (at least,
that's currently the case for musl).
Reported-by: Paul Holzinger <pholzing(a)redhat.com>
Link: https://github.com/containers/podman/issues/24804
Analysed-by: Paul Holzinger <pholzing(a)redhat.com>
Signed-off-by: Stefano Brivio <sbrivio(a)redhat.com>
---
v2: Name the wrapper strerror_() instead of __strerror()
conf.c | 10 +++++-----
icmp.c | 4 ++--
log.c | 2 +-
netlink.c | 2 +-
pasta.c | 22 +++++++++++-----------
tcp.c | 22 +++++++++++-----------
tcp_splice.c | 16 ++++++++--------
udp.c | 4 ++--
udp_flow.c | 8 ++++----
util.c | 6 +++---
util.h | 32 ++++++++++++++++++++++++++++++++
11 files changed, 80 insertions(+), 48 deletions(-)
diff --git a/conf.c b/conf.c
index eaa7d99..43daeca 100644
--- a/conf.c
+++ b/conf.c
@@ -365,7 +365,7 @@ mode_conflict:
die("Port forwarding mode '%s' conflicts with previous mode", optarg);
bind_fail:
die("Failed to bind port %u (%s) for option '-%c %s', exiting",
- i, strerror(-ret), optname, optarg);
+ i, strerror_(-ret), optname, optarg);
bind_all_fail:
die("Failed to bind any port for '-%c %s', exiting", optname, optarg);
}
@@ -655,7 +655,7 @@ static unsigned int conf_ip4(unsigned int ifi, struct ip4_ctx *ip4)
&ip4->guest_gw);
if (rc < 0) {
debug("Couldn't discover IPv4 gateway address: %s",
- strerror(-rc));
+ strerror_(-rc));
return 0;
}
}
@@ -665,7 +665,7 @@ static unsigned int conf_ip4(unsigned int ifi, struct ip4_ctx *ip4)
&ip4->addr, &ip4->prefix_len, NULL);
if (rc < 0) {
debug("Couldn't discover IPv4 address: %s",
- strerror(-rc));
+ strerror_(-rc));
return 0;
}
}
@@ -729,7 +729,7 @@ static unsigned int conf_ip6(unsigned int ifi, struct ip6_ctx *ip6)
rc = nl_route_get_def(nl_sock, ifi, AF_INET6, &ip6->guest_gw);
if (rc < 0) {
debug("Couldn't discover IPv6 gateway address: %s",
- strerror(-rc));
+ strerror_(-rc));
return 0;
}
}
@@ -738,7 +738,7 @@ static unsigned int conf_ip6(unsigned int ifi, struct ip6_ctx *ip6)
IN6_IS_ADDR_UNSPECIFIED(&ip6->addr) ? &ip6->addr : NULL,
&prefix_len, &ip6->our_tap_ll);
if (rc < 0) {
- debug("Couldn't discover IPv6 address: %s", strerror(-rc));
+ debug("Couldn't discover IPv6 address: %s", strerror_(-rc));
return 0;
}
diff --git a/icmp.c b/icmp.c
index f514dbc..143e93b 100644
--- a/icmp.c
+++ b/icmp.c
@@ -85,7 +85,7 @@ void icmp_sock_handler(const struct ctx *c, union epoll_ref ref)
n = recvfrom(ref.fd, buf, sizeof(buf), 0, &sr.sa, &sl);
if (n < 0) {
- flow_err(pingf, "recvfrom() error: %s", strerror(errno));
+ flow_err(pingf, "recvfrom() error: %s", strerror_(errno));
return;
}
@@ -301,7 +301,7 @@ int icmp_tap_handler(const struct ctx *c, uint8_t pif, sa_family_t af,
pif_sockaddr(c, &sa, &sl, PIF_HOST, &tgt->eaddr, 0);
if (sendto(pingf->sock, pkt, l4len, MSG_NOSIGNAL, &sa.sa, sl) < 0) {
flow_dbg(pingf, "failed to relay request to socket: %s",
- strerror(errno));
+ strerror_(errno));
} else {
flow_dbg(pingf,
"echo request to socket, ID: %"PRIu16", seq: %"PRIu16,
diff --git a/log.c b/log.c
index 239c8ce..95e4576 100644
--- a/log.c
+++ b/log.c
@@ -322,7 +322,7 @@ void logmsg_perror(int pri, const char *format, ...)
vlogmsg(false, false, pri, format, ap);
va_end(ap);
- logmsg(true, true, pri, ": %s", strerror(errno_copy));
+ logmsg(true, true, pri, ": %s", strerror_(errno_copy));
}
/**
diff --git a/netlink.c b/netlink.c
index 4aba2a3..0407692 100644
--- a/netlink.c
+++ b/netlink.c
@@ -320,7 +320,7 @@ unsigned int nl_get_ext_if(int s, sa_family_t af)
}
if (status < 0)
- warn("netlink: RTM_GETROUTE failed: %s", strerror(-status));
+ warn("netlink: RTM_GETROUTE failed: %s", strerror_(-status));
if (defifi) {
if (ndef > 1) {
diff --git a/pasta.c b/pasta.c
index 96dacc3..ff41c95 100644
--- a/pasta.c
+++ b/pasta.c
@@ -296,7 +296,7 @@ void pasta_ns_conf(struct ctx *c)
rc = nl_link_set_flags(nl_sock_ns, 1 /* lo */, IFF_UP, IFF_UP);
if (rc < 0)
die("Couldn't bring up loopback interface in namespace: %s",
- strerror(-rc));
+ strerror_(-rc));
/* Get or set MAC in target namespace */
if (MAC_IS_ZERO(c->guest_mac))
@@ -305,7 +305,7 @@ void pasta_ns_conf(struct ctx *c)
rc = nl_link_set_mac(nl_sock_ns, c->pasta_ifi, c->guest_mac);
if (rc < 0)
die("Couldn't set MAC address in namespace: %s",
- strerror(-rc));
+ strerror_(-rc));
if (c->pasta_conf_ns) {
unsigned int flags = IFF_UP;
@@ -332,7 +332,7 @@ void pasta_ns_conf(struct ctx *c)
if (rc < 0) {
die("Couldn't set IPv4 address(es) in namespace: %s",
- strerror(-rc));
+ strerror_(-rc));
}
if (c->ip4.no_copy_routes) {
@@ -346,7 +346,7 @@ void pasta_ns_conf(struct ctx *c)
if (rc < 0) {
die("Couldn't set IPv4 route(s) in guest: %s",
- strerror(-rc));
+ strerror_(-rc));
}
}
@@ -355,13 +355,13 @@ void pasta_ns_conf(struct ctx *c)
&c->ip6.addr_ll_seen);
if (rc < 0) {
warn("Can't get LL address from namespace: %s",
- strerror(-rc));
+ strerror_(-rc));
}
rc = nl_addr_set_ll_nodad(nl_sock_ns, c->pasta_ifi);
if (rc < 0) {
warn("Can't set nodad for LL in namespace: %s",
- strerror(-rc));
+ strerror_(-rc));
}
/* We dodged DAD: re-enable neighbour solicitations */
@@ -382,7 +382,7 @@ void pasta_ns_conf(struct ctx *c)
if (rc < 0) {
die("Couldn't set IPv6 address(es) in namespace: %s",
- strerror(-rc));
+ strerror_(-rc));
}
if (c->ip6.no_copy_routes) {
@@ -397,7 +397,7 @@ void pasta_ns_conf(struct ctx *c)
if (rc < 0) {
die("Couldn't set IPv6 route(s) in guest: %s",
- strerror(-rc));
+ strerror_(-rc));
}
}
}
@@ -446,18 +446,18 @@ void pasta_netns_quit_init(const struct ctx *c)
return;
if ((dir_fd = open(c->netns_dir, O_CLOEXEC | O_RDONLY)) < 0)
- die("netns dir open: %s, exiting", strerror(errno));
+ die("netns dir open: %s, exiting", strerror_(errno));
if (fstatfs(dir_fd, &s) || s.f_type == DEVPTS_SUPER_MAGIC ||
s.f_type == PROC_SUPER_MAGIC || s.f_type == SYSFS_MAGIC)
try_inotify = false;
if (try_inotify && (fd = inotify_init1(flags)) < 0)
- warn("inotify_init1(): %s, use a timer", strerror(errno));
+ warn("inotify_init1(): %s, use a timer", strerror_(errno));
if (fd >= 0 && inotify_add_watch(fd, c->netns_dir, IN_DELETE) < 0) {
warn("inotify_add_watch(): %s, use a timer",
- strerror(errno));
+ strerror_(errno));
close(fd);
fd = -1;
}
diff --git a/tcp.c b/tcp.c
index 1872ccb..ec433f7 100644
--- a/tcp.c
+++ b/tcp.c
@@ -516,7 +516,7 @@ static void tcp_timer_ctl(const struct ctx *c, struct tcp_tap_conn *conn)
fd = timerfd_create(CLOCK_MONOTONIC, 0);
if (fd == -1 || fd > FD_REF_MAX) {
flow_dbg(conn, "failed to get timer: %s",
- strerror(errno));
+ strerror_(errno));
if (fd > -1)
close(fd);
conn->timer = -1;
@@ -526,7 +526,7 @@ static void tcp_timer_ctl(const struct ctx *c, struct tcp_tap_conn *conn)
if (epoll_ctl(c->epollfd, EPOLL_CTL_ADD, conn->timer, &ev)) {
flow_dbg(conn, "failed to add timer: %s",
- strerror(errno));
+ strerror_(errno));
close(conn->timer);
conn->timer = -1;
return;
@@ -551,7 +551,7 @@ static void tcp_timer_ctl(const struct ctx *c, struct tcp_tap_conn *conn)
(unsigned long long)it.it_value.tv_nsec / 1000 / 1000);
if (timerfd_settime(conn->timer, 0, &it, NULL))
- flow_err(conn, "failed to set timer: %s", strerror(errno));
+ flow_err(conn, "failed to set timer: %s", strerror_(errno));
}
/**
@@ -1307,7 +1307,7 @@ int tcp_conn_sock(const struct ctx *c, sa_family_t af)
return s;
err("TCP: Unable to open socket for new connection: %s",
- strerror(-s));
+ strerror_(-s));
return -1;
}
@@ -1360,7 +1360,7 @@ static void tcp_bind_outbound(const struct ctx *c,
flow_dbg(conn,
"Can't bind TCP outbound socket to %s:%hu: %s",
inany_ntop(&tgt->oaddr, sstr, sizeof(sstr)),
- tgt->oport, strerror(errno));
+ tgt->oport, strerror_(errno));
}
}
@@ -1371,7 +1371,7 @@ static void tcp_bind_outbound(const struct ctx *c,
strlen(c->ip4.ifname_out))) {
flow_dbg(conn, "Can't bind IPv4 TCP socket to"
" interface %s: %s", c->ip4.ifname_out,
- strerror(errno));
+ strerror_(errno));
}
}
} else if (bind_sa.sa_family == AF_INET6) {
@@ -1381,7 +1381,7 @@ static void tcp_bind_outbound(const struct ctx *c,
strlen(c->ip6.ifname_out))) {
flow_dbg(conn, "Can't bind IPv6 TCP socket to"
" interface %s: %s", c->ip6.ifname_out,
- strerror(errno));
+ strerror_(errno));
}
}
}
@@ -2113,7 +2113,7 @@ void tcp_timer_handler(const struct ctx *c, union epoll_ref ref)
* and we just set the timer to a new point in the future: discard it.
*/
if (timerfd_gettime(conn->timer, &check_armed))
- flow_err(conn, "failed to read timer: %s", strerror(errno));
+ flow_err(conn, "failed to read timer: %s", strerror_(errno));
if (check_armed.it_value.tv_sec || check_armed.it_value.tv_nsec)
return;
@@ -2154,7 +2154,7 @@ void tcp_timer_handler(const struct ctx *c, union epoll_ref ref)
*/
if (timerfd_settime(conn->timer, 0, &new, &old))
flow_err(conn, "failed to set timer: %s",
- strerror(errno));
+ strerror_(errno));
if (old.it_value.tv_sec == ACT_TIMEOUT) {
flow_dbg(conn, "activity timeout");
@@ -2422,13 +2422,13 @@ static void tcp_sock_refill_init(const struct ctx *c)
int rc = tcp_sock_refill_pool(c, init_sock_pool4, AF_INET);
if (rc < 0)
warn("TCP: Error refilling IPv4 host socket pool: %s",
- strerror(-rc));
+ strerror_(-rc));
}
if (c->ifi6) {
int rc = tcp_sock_refill_pool(c, init_sock_pool6, AF_INET6);
if (rc < 0)
warn("TCP: Error refilling IPv6 host socket pool: %s",
- strerror(-rc));
+ strerror_(-rc));
}
}
diff --git a/tcp_splice.c b/tcp_splice.c
index 93f8bce..3a0f868 100644
--- a/tcp_splice.c
+++ b/tcp_splice.c
@@ -160,7 +160,7 @@ static int tcp_splice_epoll_ctl(const struct ctx *c,
if (epoll_ctl(c->epollfd, m, conn->s[0], &ev[0]) ||
epoll_ctl(c->epollfd, m, conn->s[1], &ev[1])) {
int ret = -errno;
- flow_err(conn, "ERROR on epoll_ctl(): %s", strerror(errno));
+ flow_err(conn, "ERROR on epoll_ctl(): %s", strerror_(errno));
return ret;
}
@@ -314,7 +314,7 @@ static int tcp_splice_connect_finish(const struct ctx *c,
if (conn->pipe[sidei][0] < 0) {
if (pipe2(conn->pipe[sidei], O_NONBLOCK | O_CLOEXEC)) {
flow_err(conn, "cannot create %d->%d pipe: %s",
- sidei, !sidei, strerror(errno));
+ sidei, !sidei, strerror_(errno));
conn_flag(c, conn, CLOSING);
return -EIO;
}
@@ -370,7 +370,7 @@ static int tcp_splice_connect(const struct ctx *c, struct tcp_splice_conn *conn)
if (connect(conn->s[1], &sa.sa, sl)) {
if (errno != EINPROGRESS) {
flow_trace(conn, "Couldn't connect socket for splice: %s",
- strerror(errno));
+ strerror_(errno));
return -errno;
}
@@ -469,10 +469,10 @@ void tcp_splice_sock_handler(struct ctx *c, union epoll_ref ref,
rc = getsockopt(ref.fd, SOL_SOCKET, SO_ERROR, &err, &sl);
if (rc)
flow_err(conn, "Error retrieving SO_ERROR: %s",
- strerror(errno));
+ strerror_(errno));
else
flow_trace(conn, "Error event on socket: %s",
- strerror(err));
+ strerror_(err));
goto close;
}
@@ -551,7 +551,7 @@ eintr:
&lowat, sizeof(lowat))) {
flow_trace(conn,
"Setting SO_RCVLOWAT %i: %s",
- lowat, strerror(errno));
+ lowat, strerror_(errno));
} else {
conn_flag(c, conn, lowat_set_flag);
conn_flag(c, conn, lowat_act_flag);
@@ -696,13 +696,13 @@ static int tcp_sock_refill_ns(void *arg)
int rc = tcp_sock_refill_pool(c, ns_sock_pool4, AF_INET);
if (rc < 0)
warn("TCP: Error refilling IPv4 ns socket pool: %s",
- strerror(-rc));
+ strerror_(-rc));
}
if (c->ifi6) {
int rc = tcp_sock_refill_pool(c, ns_sock_pool6, AF_INET6);
if (rc < 0)
warn("TCP: Error refilling IPv6 ns socket pool: %s",
- strerror(-rc));
+ strerror_(-rc));
}
return 0;
diff --git a/udp.c b/udp.c
index c89f031..923cc38 100644
--- a/udp.c
+++ b/udp.c
@@ -453,7 +453,7 @@ static int udp_sock_recverr(int s)
/* TODO: When possible propagate and otherwise handle errors */
debug("%s error on UDP socket %i: %s",
- str_ee_origin(ee), s, strerror(ee->ee_errno));
+ str_ee_origin(ee), s, strerror_(ee->ee_errno));
return 1;
}
@@ -492,7 +492,7 @@ int udp_sock_errs(const struct ctx *c, int s, uint32_t events)
}
if (err) {
- debug("Unqueued error on UDP socket %i: %s", s, strerror(err));
+ debug("Unqueued error on UDP socket %i: %s", s, strerror_(err));
n_err++;
}
diff --git a/udp_flow.c b/udp_flow.c
index c8fdb5f..343caae 100644
--- a/udp_flow.c
+++ b/udp_flow.c
@@ -95,7 +95,7 @@ static flow_sidx_t udp_flow_new(const struct ctx *c, union flow *flow,
if (uflow->s[INISIDE] < 0) {
flow_err(uflow,
"Couldn't duplicate listening socket: %s",
- strerror(errno));
+ strerror_(errno));
goto cancel;
}
}
@@ -115,14 +115,14 @@ static flow_sidx_t udp_flow_new(const struct ctx *c, union flow *flow,
if (uflow->s[TGTSIDE] < 0) {
flow_dbg(uflow,
"Couldn't open socket for spliced flow: %s",
- strerror(errno));
+ strerror_(errno));
goto cancel;
}
if (flowside_connect(c, uflow->s[TGTSIDE], tgtpif, tgt) < 0) {
flow_dbg(uflow,
"Couldn't connect flow socket: %s",
- strerror(errno));
+ strerror_(errno));
goto cancel;
}
@@ -144,7 +144,7 @@ static flow_sidx_t udp_flow_new(const struct ctx *c, union flow *flow,
} else if (errno != EAGAIN) {
flow_err(uflow,
"Unexpected error discarding datagrams: %s",
- strerror(errno));
+ strerror_(errno));
}
}
diff --git a/util.c b/util.c
index 55cae3f..11973c4 100644
--- a/util.c
+++ b/util.c
@@ -90,7 +90,7 @@ int sock_l4_sa(const struct ctx *c, enum epoll_type type,
ret = -errno;
if (fd < 0) {
- warn("L4 socket: %s", strerror(-ret));
+ warn("L4 socket: %s", strerror_(-ret));
return ret;
}
@@ -162,7 +162,7 @@ int sock_l4_sa(const struct ctx *c, enum epoll_type type,
if (type == EPOLL_TYPE_TCP_LISTEN && listen(fd, 128) < 0) {
ret = -errno;
- warn("TCP socket listen: %s", strerror(-ret));
+ warn("TCP socket listen: %s", strerror_(-ret));
close(fd);
return ret;
}
@@ -171,7 +171,7 @@ int sock_l4_sa(const struct ctx *c, enum epoll_type type,
ev.data.u64 = ref.u64;
if (epoll_ctl(c->epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
ret = -errno;
- warn("L4 epoll_ctl: %s", strerror(-ret));
+ warn("L4 epoll_ctl: %s", strerror_(-ret));
return ret;
}
diff --git a/util.h b/util.h
index 41bbd60..3fa1d12 100644
--- a/util.h
+++ b/util.h
@@ -274,6 +274,38 @@ static inline bool mod_between(unsigned x, unsigned i, unsigned j, unsigned m)
void raw_random(void *buf, size_t buflen);
+/*
+ * Starting from glibc 2.40.9000 and commit 25a5eb4010df ("string: strerror,
+ * strsignal cannot use buffer after dlmopen (bug 32026)"), strerror() needs
+ * getrandom(2) and brk(2) as it allocates memory for the locale-translated
+ * error description, but our seccomp profiles forbid both.
+ *
+ * Use the strerror_() wrapper instead, calling into strerrordesc_np() to get
+ * a static untranslated string. It's a GNU implementation, but also defined by
+ * bionic.
+ *
+ * If strerrordesc_np() is not defined (e.g. musl), call strerror(). C libraries
+ * not defining strerrordesc_np() are expected to provide strerror()
+ * implementations that are simple enough for us to call.
+ */
+__attribute__ ((weak)) const char *strerrordesc_np(int errnum);
+
+/**
+ * strerror_() - strerror() wrapper calling strerrordesc_np() if available
+ * @errnum: Error code
+ *
+ * Return: error description string
+ */
+static inline const char *strerror_(int errnum)
+{
+ if (strerrordesc_np)
+ return strerrordesc_np(errnum);
+
+ return strerror(errnum);
+}
+
+#define strerror(x) @ "Don't call strerror() directly, use strerror_() instead"
+
/*
* Workarounds for https://github.com/llvm/llvm-project/issues/58992
*
--
2.43.0
3
2