commit 292a41ef26299d8df6d6060c0f8eb5e683ff6899 Author: CentOS Sources Date: Tue May 9 05:30:14 2023 +0000 import passt-0^20230222.g4ddbcb9-2.el9_2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..58e8109 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/passt-4ddbcb9c0c555838b123c018a9ebc9b7e14a87e5.tar.xz diff --git a/.passt.metadata b/.passt.metadata new file mode 100644 index 0000000..2092824 --- /dev/null +++ b/.passt.metadata @@ -0,0 +1 @@ +f0d7edae2c421217fd15a72ce7b552656ce4ed16 SOURCES/passt-4ddbcb9c0c555838b123c018a9ebc9b7e14a87e5.tar.xz diff --git a/SOURCES/0001-udp-Actually-use-host-resolver-to-forward-DNS-querie.patch b/SOURCES/0001-udp-Actually-use-host-resolver-to-forward-DNS-querie.patch new file mode 100644 index 0000000..ced20f9 --- /dev/null +++ b/SOURCES/0001-udp-Actually-use-host-resolver-to-forward-DNS-querie.patch @@ -0,0 +1,47 @@ +From 0dfc25b917f7e94ac56ea4285a5d394305787b06 Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Thu, 23 Feb 2023 12:21:29 +0000 +Subject: [PATCH 01/20] udp: Actually use host resolver to forward DNS queries + +Instead of the address of the first resolver we advertise to +the guest or namespace. + +This was one of the intentions behind commit 3a2afde87dd1 ("conf, +udp: Drop mostly duplicated dns_send arrays, rename related fields"), +but I forgot to implement this part. In practice, they are usually +the same thing, unless /etc/resolv.conf points to a loopback address. + +Fixes: 3a2afde87dd1 ("conf, udp: Drop mostly duplicated dns_send arrays, rename related fields") +Signed-off-by: Stefano Brivio +Tested-by: Andrea Bolognani +Reviewed-by: David Gibson +(cherry picked from commit ddf7097a718095e879428667f2d56ec7d4f027e5) +--- + udp.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/udp.c b/udp.c +index c913d27..1d65559 100644 +--- a/udp.c ++++ b/udp.c +@@ -867,7 +867,7 @@ int udp_tap_handler(struct ctx *c, int af, const void *addr, + } else if (IN4_ARE_ADDR_EQUAL(&s_in.sin_addr, + &c->ip4.dns_match) && + ntohs(s_in.sin_port) == 53) { +- s_in.sin_addr = c->ip4.dns[0]; ++ s_in.sin_addr = c->ip4.dns_host; + } + } else { + s_in6 = (struct sockaddr_in6) { +@@ -890,7 +890,7 @@ int udp_tap_handler(struct ctx *c, int af, const void *addr, + s_in6.sin6_addr = c->ip6.addr_seen; + } else if (IN6_ARE_ADDR_EQUAL(addr, &c->ip6.dns_match) && + ntohs(s_in6.sin6_port) == 53) { +- s_in6.sin6_addr = c->ip6.dns[0]; ++ s_in6.sin6_addr = c->ip6.dns_host; + } else if (IN6_IS_ADDR_LINKLOCAL(&s_in6.sin6_addr)) { + bind_addr = &c->ip6.addr_ll; + } +-- +2.39.2 + diff --git a/SOURCES/0002-conf-Split-add_dns-4-6-out-of-get_dns.patch b/SOURCES/0002-conf-Split-add_dns-4-6-out-of-get_dns.patch new file mode 100644 index 0000000..9b0d1c4 --- /dev/null +++ b/SOURCES/0002-conf-Split-add_dns-4-6-out-of-get_dns.patch @@ -0,0 +1,129 @@ +From bf19154051b6c920f702c8394cd6821bb00f531a Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Thu, 23 Feb 2023 13:32:30 +0000 +Subject: [PATCH 02/20] conf: Split add_dns{4,6}() out of get_dns() + +The logic handling which resolvers we add, and whether to add them, +is getting rather cramped in get_dns(): split it into separate +functions. + +No functional changes intended. + +Signed-off-by: Stefano Brivio +Tested-by: Andrea Bolognani +Reviewed-by: David Gibson +(cherry picked from commit 8ca907a3f0a095691cdaf56ad610fd802df88146) +--- + conf.c | 86 ++++++++++++++++++++++++++++++++++------------------------ + 1 file changed, 51 insertions(+), 35 deletions(-) + +diff --git a/conf.c b/conf.c +index 4dc0660..ed25e35 100644 +--- a/conf.c ++++ b/conf.c +@@ -382,6 +382,53 @@ bind_fail: + die("Failed to bind any port for '-%c %s', exiting", optname, optarg); + } + ++/** ++ * add_dns4() - Possibly add the IPv4 address of a DNS resolver to configuration ++ * @c: Execution context ++ * @addr: Address found in /etc/resolv.conf ++ * @conf: Pointer to reference of current entry in array of IPv4 resolvers ++ */ ++static void add_dns4(struct ctx *c, struct in_addr *addr, struct in_addr **conf) ++{ ++ /* Guest or container can only access local addresses via redirect */ ++ if (IN4_IS_ADDR_LOOPBACK(addr)) { ++ if (!c->no_map_gw) { ++ **conf = c->ip4.gw; ++ (*conf)++; ++ } ++ } else { ++ **conf = *addr; ++ (*conf)++; ++ } ++ ++ if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns_host)) ++ c->ip4.dns_host = *addr; ++} ++ ++/** ++ * add_dns6() - Possibly add the IPv6 address of a DNS resolver to configuration ++ * @c: Execution context ++ * @addr: Address found in /etc/resolv.conf ++ * @conf: Pointer to reference of current entry in array of IPv6 resolvers ++ */ ++static void add_dns6(struct ctx *c, ++ struct in6_addr *addr, struct in6_addr **conf) ++{ ++ /* Guest or container can only access local addresses via redirect */ ++ if (IN6_IS_ADDR_LOOPBACK(addr)) { ++ if (!c->no_map_gw) { ++ memcpy(*conf, &c->ip6.gw, sizeof(**conf)); ++ (*conf)++; ++ } ++ } else { ++ memcpy(*conf, addr, sizeof(**conf)); ++ (*conf)++; ++ } ++ ++ if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns_host)) ++ c->ip6.dns_host = *addr; ++} ++ + /** + * get_dns() - Get nameserver addresses from local /etc/resolv.conf + * @c: Execution context +@@ -420,44 +467,13 @@ static void get_dns(struct ctx *c) + + if (!dns4_set && + dns4 - &c->ip4.dns[0] < ARRAY_SIZE(c->ip4.dns) - 1 +- && inet_pton(AF_INET, p + 1, &dns4_tmp)) { +- /* Guest or container can only access local +- * addresses via local redirect +- */ +- if (IN4_IS_ADDR_LOOPBACK(&dns4_tmp)) { +- if (!c->no_map_gw) { +- *dns4 = c->ip4.gw; +- dns4++; +- } +- } else { +- *dns4 = dns4_tmp; +- dns4++; +- } +- +- if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns_host)) +- c->ip4.dns_host = dns4_tmp; +- } ++ && inet_pton(AF_INET, p + 1, &dns4_tmp)) ++ add_dns4(c, &dns4_tmp, &dns4); + + if (!dns6_set && + dns6 - &c->ip6.dns[0] < ARRAY_SIZE(c->ip6.dns) - 1 +- && inet_pton(AF_INET6, p + 1, &dns6_tmp)) { +- /* Guest or container can only access local +- * addresses via local redirect +- */ +- if (IN6_IS_ADDR_LOOPBACK(&dns6_tmp)) { +- if (!c->no_map_gw) { +- memcpy(dns6, &c->ip6.gw, +- sizeof(*dns6)); +- dns6++; +- } +- } else { +- memcpy(dns6, &dns6_tmp, sizeof(*dns6)); +- dns6++; +- } +- +- if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns_host)) +- c->ip6.dns_host = dns6_tmp; +- } ++ && inet_pton(AF_INET6, p + 1, &dns6_tmp)) ++ add_dns6(c, &dns6_tmp, &dns6); + } else if (!dnss_set && strstr(line, "search ") == line && + s == c->dns_search) { + end = strpbrk(line, "\n"); +-- +2.39.2 + diff --git a/SOURCES/0003-conf-udp-Allow-any-loopback-address-to-be-used-as-re.patch b/SOURCES/0003-conf-udp-Allow-any-loopback-address-to-be-used-as-re.patch new file mode 100644 index 0000000..a98f0ac --- /dev/null +++ b/SOURCES/0003-conf-udp-Allow-any-loopback-address-to-be-used-as-re.patch @@ -0,0 +1,119 @@ +From ae0004f591d816f1b6e78c57c3d9530098f123b0 Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Thu, 23 Feb 2023 16:41:47 +0000 +Subject: [PATCH 03/20] conf, udp: Allow any loopback address to be used as + resolver + +Andrea reports that with a Fedora 37 guest running on a Fedora 37 +host, both using systemd-resolved, with passt connecting them, +running with default options, DNS queries don't work. + +systemd-resolved on the host is reachable only at the loopback +address 127.0.0.53. + +We advertise the default gateway address to the guest as resolver, +because our local address is of course unreachable from there, which +means we see DNS queries directed to the default gateway, and we +redirect them to 127.0.0.1. However, systemd-resolved doesn't answer +on 127.0.0.1. + +To fix this, set @dns_match to the address of the default gateway, +unless a different resolver address is explicitly configured, so that +we know we explicitly have to map DNS queries, in this case, to the +address of the local resolver. + +This means that in udp_tap_handler() we need to check, first, if +the destination address of packets matches @dns_match: even if it's +the address of the local gateway, we want to map that to a specific +address, which isn't necessarily 127.0.0.1. + +Do the same for IPv6 for consistency, even though IPv6 defines a +single loopback address. + +Reported-by: Andrea Bolognani +Signed-off-by: Stefano Brivio +Tested-by: Andrea Bolognani +Reviewed-by: David Gibson +(cherry picked from commit bad252687271c0255f6a077f19cbc19aa0427f8d) +--- + conf.c | 6 ++++++ + udp.c | 20 ++++++++++---------- + 2 files changed, 16 insertions(+), 10 deletions(-) + +diff --git a/conf.c b/conf.c +index ed25e35..37f25d6 100644 +--- a/conf.c ++++ b/conf.c +@@ -395,6 +395,9 @@ static void add_dns4(struct ctx *c, struct in_addr *addr, struct in_addr **conf) + if (!c->no_map_gw) { + **conf = c->ip4.gw; + (*conf)++; ++ ++ if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns_match)) ++ c->ip4.dns_match = c->ip4.gw; + } + } else { + **conf = *addr; +@@ -419,6 +422,9 @@ static void add_dns6(struct ctx *c, + if (!c->no_map_gw) { + memcpy(*conf, &c->ip6.gw, sizeof(**conf)); + (*conf)++; ++ ++ if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns_match)) ++ memcpy(&c->ip6.dns_match, addr, sizeof(*addr)); + } + } else { + memcpy(*conf, addr, sizeof(**conf)); +diff --git a/udp.c b/udp.c +index 1d65559..20a9ea0 100644 +--- a/udp.c ++++ b/udp.c +@@ -857,17 +857,16 @@ int udp_tap_handler(struct ctx *c, int af, const void *addr, + + udp_tap_map[V4][src].ts = now->tv_sec; + +- if (IN4_ARE_ADDR_EQUAL(&s_in.sin_addr, &c->ip4.gw) && +- !c->no_map_gw) { ++ if (IN4_ARE_ADDR_EQUAL(&s_in.sin_addr, &c->ip4.dns_match) && ++ ntohs(s_in.sin_port) == 53) { ++ s_in.sin_addr = c->ip4.dns_host; ++ } else if (IN4_ARE_ADDR_EQUAL(&s_in.sin_addr, &c->ip4.gw) && ++ !c->no_map_gw) { + if (!(udp_tap_map[V4][dst].flags & PORT_LOCAL) || + (udp_tap_map[V4][dst].flags & PORT_LOOPBACK)) + s_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + else + s_in.sin_addr = c->ip4.addr_seen; +- } else if (IN4_ARE_ADDR_EQUAL(&s_in.sin_addr, +- &c->ip4.dns_match) && +- ntohs(s_in.sin_port) == 53) { +- s_in.sin_addr = c->ip4.dns_host; + } + } else { + s_in6 = (struct sockaddr_in6) { +@@ -880,7 +879,11 @@ int udp_tap_handler(struct ctx *c, int af, const void *addr, + sa = (struct sockaddr *)&s_in6; + sl = sizeof(s_in6); + +- if (IN6_ARE_ADDR_EQUAL(addr, &c->ip6.gw) && !c->no_map_gw) { ++ if (IN6_ARE_ADDR_EQUAL(addr, &c->ip6.dns_match) && ++ ntohs(s_in6.sin6_port) == 53) { ++ s_in6.sin6_addr = c->ip6.dns_host; ++ } else if (IN6_ARE_ADDR_EQUAL(addr, &c->ip6.gw) && ++ !c->no_map_gw) { + if (!(udp_tap_map[V6][dst].flags & PORT_LOCAL) || + (udp_tap_map[V6][dst].flags & PORT_LOOPBACK)) + s_in6.sin6_addr = in6addr_loopback; +@@ -888,9 +891,6 @@ int udp_tap_handler(struct ctx *c, int af, const void *addr, + s_in6.sin6_addr = c->ip6.addr; + else + s_in6.sin6_addr = c->ip6.addr_seen; +- } else if (IN6_ARE_ADDR_EQUAL(addr, &c->ip6.dns_match) && +- ntohs(s_in6.sin6_port) == 53) { +- s_in6.sin6_addr = c->ip6.dns_host; + } else if (IN6_IS_ADDR_LINKLOCAL(&s_in6.sin6_addr)) { + bind_addr = &c->ip6.addr_ll; + } +-- +2.39.2 + diff --git a/SOURCES/0004-tcp-tcp_splice-Get-rid-of-false-positive-CWE-394-Cov.patch b/SOURCES/0004-tcp-tcp_splice-Get-rid-of-false-positive-CWE-394-Cov.patch new file mode 100644 index 0000000..5ab1eab --- /dev/null +++ b/SOURCES/0004-tcp-tcp_splice-Get-rid-of-false-positive-CWE-394-Cov.patch @@ -0,0 +1,130 @@ +From 850bb9c15d39dcbefb0849955f4f09382f587c20 Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Mon, 27 Feb 2023 02:45:42 +0100 +Subject: [PATCH 04/20] tcp, tcp_splice: Get rid of false positive CWE-394 + Coverity warning from fls() + +We use the return value of fls() as array index for debug strings. + +While fls() can return -1 (if no bit is set), Coverity Scan doesn't +see that we're first checking the return value of another fls() call +with the same bitmask, before using it. + +Call fls() once, store its return value, check it, and use the stored +value as array index. + +Signed-off-by: Stefano Brivio +Reviewed-by: David Gibson +(cherry picked from commit 5474bc5485d814acae19961f9a9cd4b541722a5e) +--- + tcp.c | 12 ++++++++---- + tcp_splice.c | 24 ++++++++++++++++-------- + 2 files changed, 24 insertions(+), 12 deletions(-) + +diff --git a/tcp.c b/tcp.c +index 803c2c4..c62fe44 100644 +--- a/tcp.c ++++ b/tcp.c +@@ -743,15 +743,19 @@ static void conn_flag_do(const struct ctx *c, struct tcp_tap_conn *conn, + unsigned long flag) + { + if (flag & (flag - 1)) { ++ int flag_index = fls(~flag); ++ + if (!(conn->flags & ~flag)) + return; + + conn->flags &= flag; +- if (fls(~flag) >= 0) { ++ if (flag_index >= 0) { + debug("TCP: index %li: %s dropped", CONN_IDX(conn), +- tcp_flag_str[fls(~flag)]); ++ tcp_flag_str[flag_index]); + } + } else { ++ int flag_index = fls(~flag); ++ + if (conn->flags & flag) { + /* Special case: setting ACK_FROM_TAP_DUE on a + * connection where it's already set is used to +@@ -766,9 +770,9 @@ static void conn_flag_do(const struct ctx *c, struct tcp_tap_conn *conn, + } + + conn->flags |= flag; +- if (fls(flag) >= 0) { ++ if (flag_index >= 0) { + debug("TCP: index %li: %s", CONN_IDX(conn), +- tcp_flag_str[fls(flag)]); ++ tcp_flag_str[flag_index]); + } + } + +diff --git a/tcp_splice.c b/tcp_splice.c +index 84f855e..67af46b 100644 +--- a/tcp_splice.c ++++ b/tcp_splice.c +@@ -127,22 +127,26 @@ static void conn_flag_do(const struct ctx *c, struct tcp_splice_conn *conn, + unsigned long flag) + { + if (flag & (flag - 1)) { ++ int flag_index = fls(~flag); ++ + if (!(conn->flags & ~flag)) + return; + + conn->flags &= flag; +- if (fls(~flag) >= 0) { ++ if (flag_index >= 0) { + debug("TCP (spliced): index %li: %s dropped", CONN_IDX(conn), +- tcp_splice_flag_str[fls(~flag)]); ++ tcp_splice_flag_str[flag_index]); + } + } else { ++ int flag_index = fls(flag); ++ + if (conn->flags & flag) + return; + + conn->flags |= flag; +- if (fls(flag) >= 0) { ++ if (flag_index >= 0) { + debug("TCP (spliced): index %li: %s", CONN_IDX(conn), +- tcp_splice_flag_str[fls(flag)]); ++ tcp_splice_flag_str[flag_index]); + } + } + +@@ -207,22 +211,26 @@ static void conn_event_do(const struct ctx *c, struct tcp_splice_conn *conn, + unsigned long event) + { + if (event & (event - 1)) { ++ int flag_index = fls(~event); ++ + if (!(conn->events & ~event)) + return; + + conn->events &= event; +- if (fls(~event) >= 0) { ++ if (flag_index >= 0) { + debug("TCP (spliced): index %li, ~%s", CONN_IDX(conn), +- tcp_splice_event_str[fls(~event)]); ++ tcp_splice_event_str[flag_index]); + } + } else { ++ int flag_index = fls(event); ++ + if (conn->events & event) + return; + + conn->events |= event; +- if (fls(event) >= 0) { ++ if (flag_index >= 0) { + debug("TCP (spliced): index %li, %s", CONN_IDX(conn), +- tcp_splice_event_str[fls(event)]); ++ tcp_splice_event_str[flag_index]); + } + } + +-- +2.39.2 + diff --git a/SOURCES/0005-tcp-Avoid-false-but-convoluted-positive-Coverity-CWE.patch b/SOURCES/0005-tcp-Avoid-false-but-convoluted-positive-Coverity-CWE.patch new file mode 100644 index 0000000..f474163 --- /dev/null +++ b/SOURCES/0005-tcp-Avoid-false-but-convoluted-positive-Coverity-CWE.patch @@ -0,0 +1,39 @@ +From 65d8d329620973319b577e9a1b0ffad528a4df1f Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Mon, 27 Feb 2023 03:05:26 +0100 +Subject: [PATCH 05/20] tcp: Avoid false (but convoluted) positive Coverity + CWE-476 warning + +If there are no TCP options in the header, tcp_tap_handler() will +pass the corresponding pointer, fetched via packet_get(), as NULL to +tcp_conn_from_sock_finish(), which in turn indirectly calls +tcp_opt_get(). + +If there are no options, tcp_opt_get() will stop right away because +the option length is indicated as zero. However, if the logic is +complicated enough to follow for static checkers, adding an explicit +check against NULL in tcp_opt_get() is probably a good idea. + +Signed-off-by: Stefano Brivio +Reviewed-by: David Gibson +(cherry picked from commit a1d5537741679c117b4c1a9b736ea2540a976eee) +--- + tcp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tcp.c b/tcp.c +index c62fe44..a811b5e 100644 +--- a/tcp.c ++++ b/tcp.c +@@ -1114,7 +1114,7 @@ static int tcp_opt_get(const char *opts, size_t len, uint8_t type_find, + { + uint8_t type, optlen; + +- if (!len) ++ if (!opts || !len) + return -1; + + for (; len >= 2; opts += optlen, len -= optlen) { +-- +2.39.2 + diff --git a/SOURCES/0006-tcp-Avoid-theoretical-resource-leak-CWE-772-Coverity.patch b/SOURCES/0006-tcp-Avoid-theoretical-resource-leak-CWE-772-Coverity.patch new file mode 100644 index 0000000..a258843 --- /dev/null +++ b/SOURCES/0006-tcp-Avoid-theoretical-resource-leak-CWE-772-Coverity.patch @@ -0,0 +1,35 @@ +From cd950e6b7f85f4f8f80284ba79a027dee57bfa61 Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Mon, 27 Feb 2023 03:13:31 +0100 +Subject: [PATCH 06/20] tcp: Avoid (theoretical) resource leak (CWE-772) + Coverity warning + +If tcp_timer_ctl() gets a socket number greater than SOCKET_MAX +(2 ^ 24), we return error but we don't close the socket. This is a +rather formal issue given that, at least on Linux, socket numbers are +monotonic and we're in general not allowed to open so many sockets. + +Signed-off-by: Stefano Brivio +Reviewed-by: David Gibson +(cherry picked from commit 4f523c3276741781346478328f863e60f30cba8e) +--- + tcp.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/tcp.c b/tcp.c +index a811b5e..fe6e458 100644 +--- a/tcp.c ++++ b/tcp.c +@@ -702,6 +702,9 @@ static void tcp_timer_ctl(const struct ctx *c, struct tcp_tap_conn *conn) + fd = timerfd_create(CLOCK_MONOTONIC, 0); + if (fd == -1 || fd > SOCKET_MAX) { + debug("TCP: failed to get timer: %s", strerror(errno)); ++ if (fd > -1) ++ close(fd); ++ conn->timer = -1; + return; + } + conn->timer = fd; +-- +2.39.2 + diff --git a/SOURCES/0007-Fix-definitions-of-SOCKET_MAX-TCP_MAX_CONNS.patch b/SOURCES/0007-Fix-definitions-of-SOCKET_MAX-TCP_MAX_CONNS.patch new file mode 100644 index 0000000..e0fa3e0 --- /dev/null +++ b/SOURCES/0007-Fix-definitions-of-SOCKET_MAX-TCP_MAX_CONNS.patch @@ -0,0 +1,84 @@ +From 44d35498a98da3d3234b0084033f6dcd6450e22b Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Mon, 27 Feb 2023 03:30:01 +0100 +Subject: [PATCH 07/20] Fix definitions of SOCKET_MAX, TCP_MAX_CONNS + +...and, given that I keep getting this wrong, add a convenience +macro, MAX_FROM_BITS(). + +Signed-off-by: Stefano Brivio +Reviewed-by: David Gibson +(cherry picked from commit 26a0e4d6ee17fa174a401d8e8d9a4c189f11f258) +--- + passt.h | 4 ++-- + tcp.h | 4 ++-- + tcp_conn.h | 2 +- + util.h | 2 ++ + 4 files changed, 7 insertions(+), 5 deletions(-) + +diff --git a/passt.h b/passt.h +index 3d7e567..e0383eb 100644 +--- a/passt.h ++++ b/passt.h +@@ -42,7 +42,7 @@ union epoll_ref; + /** + * union epoll_ref - Breakdown of reference for epoll socket bookkeeping + * @proto: IP protocol number +- * @s: Socket number (implies 2^24 limit on number of descriptors) ++ * @s: Socket number (implies 2^24-1 limit on number of descriptors) + * @tcp: TCP-specific reference part + * @udp: UDP-specific reference part + * @icmp: ICMP-specific reference part +@@ -53,7 +53,7 @@ union epoll_ref { + struct { + int32_t proto:8, + #define SOCKET_REF_BITS 24 +-#define SOCKET_MAX (1 << SOCKET_REF_BITS) ++#define SOCKET_MAX MAX_FROM_BITS(SOCKET_REF_BITS) + s:SOCKET_REF_BITS; + union { + union tcp_epoll_ref tcp; +diff --git a/tcp.h b/tcp.h +index 5527c5b..36e5391 100644 +--- a/tcp.h ++++ b/tcp.h +@@ -8,8 +8,8 @@ + + #define TCP_TIMER_INTERVAL 1000 /* ms */ + +-#define TCP_CONN_INDEX_BITS 17 /* 128k */ +-#define TCP_MAX_CONNS (1 << TCP_CONN_INDEX_BITS) ++#define TCP_CONN_INDEX_BITS 17 /* 128k - 1 */ ++#define TCP_MAX_CONNS MAX_FROM_BITS(TCP_CONN_INDEX_BITS) + + struct ctx; + +diff --git a/tcp_conn.h b/tcp_conn.h +index a499f34..c22632b 100644 +--- a/tcp_conn.h ++++ b/tcp_conn.h +@@ -54,7 +54,7 @@ struct tcp_tap_conn { + + #define TCP_RETRANS_BITS 3 + unsigned int retrans :TCP_RETRANS_BITS; +-#define TCP_MAX_RETRANS ((1U << TCP_RETRANS_BITS) - 1) ++#define TCP_MAX_RETRANS MAX_FROM_BITS(TCP_RETRANS_BITS) + + #define TCP_WS_BITS 4 /* RFC 7323 */ + #define TCP_WS_MAX 14 +diff --git a/util.h b/util.h +index 6303c17..570094c 100644 +--- a/util.h ++++ b/util.h +@@ -40,6 +40,8 @@ + #define ROUND_DOWN(x, y) ((x) & ~((y) - 1)) + #define ROUND_UP(x, y) (((x) + (y) - 1) & ~((y) - 1)) + ++#define MAX_FROM_BITS(n) ((int)((1U << (n)) - 1)) ++ + #define BIT(n) (1UL << (n)) + #define BITMAP_BIT(n) (BIT((n) % (sizeof(long) * 8))) + #define BITMAP_WORD(n) (n / (sizeof(long) * 8)) +-- +2.39.2 + diff --git a/SOURCES/0008-doc-demo-Fix-and-suppress-ShellCheck-warnings.patch b/SOURCES/0008-doc-demo-Fix-and-suppress-ShellCheck-warnings.patch new file mode 100644 index 0000000..0f87d21 --- /dev/null +++ b/SOURCES/0008-doc-demo-Fix-and-suppress-ShellCheck-warnings.patch @@ -0,0 +1,44 @@ +From 8d631f17e11d6c9f1c01eb0dc1cb104ac45e63ac Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Mon, 27 Feb 2023 03:44:25 +0100 +Subject: [PATCH 08/20] doc/demo: Fix and suppress ShellCheck warnings + +ShellCheck reports (SC2034) that __qemu_arch is not used. Use it, +and silence the resulting SC2086 warning as we want word splitting on +options we pass with it. + +While at it, silence SC2317 warnings for commands in cleanup() that +appear to be unreachable: cleanup() is only called as trap. + +Signed-off-by: Stefano Brivio +Reviewed-by: David Gibson +(cherry picked from commit fb05a713789fd9c20d0432f023ce26f3c5b94251) +--- + doc/demo.sh | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/doc/demo.sh b/doc/demo.sh +index ed71ffb..5b05ddf 100755 +--- a/doc/demo.sh ++++ b/doc/demo.sh +@@ -110,6 +110,7 @@ next() { + } + + # cleanup() - Terminate pasta and passt, clean up, restore TTY settings ++# shellcheck disable=SC2317 + cleanup() { + [ -f "${DEMO_DIR}/pasta.pid" ] && kill "$(cat "${DEMO_DIR}/pasta.pid")" + [ -f "${DEMO_DIR}/passt.pid" ] && kill "$(cat "${DEMO_DIR}/passt.pid")" +@@ -223,7 +224,8 @@ into_ns() { + echo "Use ^C to terminate it." + next + +- cmd qrap 5 qemu-system-x86_64 -M pc,accel=kvm:tcg \ ++ # shellcheck disable=SC2086 ++ cmd qrap 5 ${__qemu_arch} \ + -smp "$(nproc)" -m 1024 \ + -nographic -serial stdio -nodefaults -no-reboot -vga none \ + -initrd "${DEMO_DIR}/demo.img" \ +-- +2.39.2 + diff --git a/SOURCES/0009-contrib-selinux-Drop-duplicate-init_daemon_domain-ru.patch b/SOURCES/0009-contrib-selinux-Drop-duplicate-init_daemon_domain-ru.patch new file mode 100644 index 0000000..5f7e274 --- /dev/null +++ b/SOURCES/0009-contrib-selinux-Drop-duplicate-init_daemon_domain-ru.patch @@ -0,0 +1,29 @@ +From bbeecf78cf598f24bd2f1434dfbb3fad7b549f59 Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Mon, 6 Mar 2023 22:48:21 +0000 +Subject: [PATCH 09/20] contrib/selinux: Drop duplicate init_daemon_domain() + rule + +Signed-off-by: Stefano Brivio +Tested-by: Laine Stump +Reviewed-by: Laine Stump +(cherry picked from commit 009af75e450aae1d4e9e031a9e42a0e74ce1adf7) +--- + contrib/selinux/passt.te | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/contrib/selinux/passt.te b/contrib/selinux/passt.te +index 7fa4fb9..593b346 100644 +--- a/contrib/selinux/passt.te ++++ b/contrib/selinux/passt.te +@@ -57,7 +57,6 @@ type passt_t; + domain_type(passt_t); + type passt_exec_t; + files_type(passt_exec_t); +-init_daemon_domain(passt_t, passt_exec_t) + type passt_log_t; + logging_log_file(passt_log_t); + type passt_etc_t; +-- +2.39.2 + diff --git a/SOURCES/0010-contrib-selinux-Let-passt-write-to-stdout-and-stderr.patch b/SOURCES/0010-contrib-selinux-Let-passt-write-to-stdout-and-stderr.patch new file mode 100644 index 0000000..9f04dd4 --- /dev/null +++ b/SOURCES/0010-contrib-selinux-Let-passt-write-to-stdout-and-stderr.patch @@ -0,0 +1,33 @@ +From 2fcc8f2b519facb139df5e412379f991d8322bf4 Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Mon, 6 Mar 2023 22:49:39 +0000 +Subject: [PATCH 10/20] contrib/selinux: Let passt write to stdout and stderr + when it starts + +Otherwise, it's unusable as stand-alone tool, or in foreground mode, +and it's also impossible to get output from --help or --version, +because for SELinux it's just a daemon. + +Signed-off-by: Stefano Brivio +Tested-by: Laine Stump +Reviewed-by: Laine Stump +(cherry picked from commit 41bc669866b9e408d8d4966ee06e01784949b98d) +--- + contrib/selinux/passt.te | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/contrib/selinux/passt.te b/contrib/selinux/passt.te +index 593b346..6cd61f1 100644 +--- a/contrib/selinux/passt.te ++++ b/contrib/selinux/passt.te +@@ -72,6 +72,7 @@ type_transition unconfined_t passt_exec_t : process passt_t; + allow unconfined_t passt_t : process transition ; + + init_daemon_domain(passt_t, passt_exec_t) ++term_use_all_inherited_terms(passt_t) + + allow passt_t bin_t:file { execute execute_no_trans map }; + allow passt_t user_home_dir_t:dir { search add_name write }; +-- +2.39.2 + diff --git a/SOURCES/0011-contrib-selinux-Allow-binding-and-connecting-to-all-.patch b/SOURCES/0011-contrib-selinux-Allow-binding-and-connecting-to-all-.patch new file mode 100644 index 0000000..36f68aa --- /dev/null +++ b/SOURCES/0011-contrib-selinux-Allow-binding-and-connecting-to-all-.patch @@ -0,0 +1,78 @@ +From 8cb64a237ed6183c492dea04da7c8d3ca064a155 Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Mon, 6 Mar 2023 23:05:36 +0000 +Subject: [PATCH 11/20] contrib/selinux: Allow binding and connecting to all + UDP and TCP ports + +Laine reports that with a simple: + + + + + +in libvirt's domain XML, passt won't start as it fails to bind +arbitrary ports. That was actually the intention behind passt_port_t: +the user or system administrator should have explicitly configured +allowed ports on a given machine. But it's probably not realistic, so +just allow any port to be bound and forwarded. + +Also fix up some missing operations on sockets. + +Reported-by: Laine Stump +Signed-off-by: Stefano Brivio +Tested-by: Laine Stump +Reviewed-by: Laine Stump +(cherry picked from commit de9b0cb5fee2ea00ed7e7877ef9be8c446bca134) +--- + contrib/selinux/passt.te | 27 +++++++++++++++------------ + 1 file changed, 15 insertions(+), 12 deletions(-) + +diff --git a/contrib/selinux/passt.te b/contrib/selinux/passt.te +index 6cd61f1..438155d 100644 +--- a/contrib/selinux/passt.te ++++ b/contrib/selinux/passt.te +@@ -62,9 +62,6 @@ logging_log_file(passt_log_t); + type passt_etc_t; + files_config_file(passt_etc_t); + +-type passt_port_t; +-typeattribute passt_port_t port_type; +- + role unconfined_r types passt_t; + + allow passt_t passt_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open } ; +@@ -100,16 +97,22 @@ allow passt_t net_conf_t:lnk_file read; + allow passt_t tmp_t:sock_file { create unlink write }; + allow passt_t self:netlink_route_socket { bind create nlmsg_read read write setopt }; + +-allow passt_t self:tcp_socket create_stream_socket_perms; +-corenet_tcp_sendrecv_generic_node(passt_t) +-corenet_tcp_bind_generic_node(passt_t) +-allow passt_t passt_port_t:tcp_socket { name_bind name_connect }; +-allow passt_t http_port_t:tcp_socket { name_bind name_connect }; +-allow passt_t self:udp_socket create_stream_socket_perms; +-corenet_udp_sendrecv_generic_node(passt_t) +-corenet_udp_bind_generic_node(passt_t) +-allow passt_t passt_port_t:udp_socket { name_bind }; ++corenet_tcp_bind_all_nodes(passt_t) ++corenet_udp_bind_all_nodes(passt_t) ++ ++corenet_tcp_bind_all_ports(passt_t) ++corenet_udp_bind_all_ports(passt_t) ++ ++corenet_tcp_connect_all_ports(passt_t) ++ ++corenet_tcp_sendrecv_all_ports(passt_t) ++corenet_udp_sendrecv_all_ports(passt_t) ++ + allow passt_t node_t:icmp_socket { name_bind node_bind }; ++allow passt_t port_t:icmp_socket name_bind; ++ ++allow passt_t self:tcp_socket { create getopt setopt connect bind listen accept shutdown read write }; ++allow passt_t self:udp_socket { create getopt setopt connect bind read write }; + allow passt_t self:icmp_socket { bind create setopt read write }; + + allow passt_t user_tmp_t:dir { add_name write }; +-- +2.39.2 + diff --git a/SOURCES/0012-contrib-selinux-Let-interface-users-set-paths-for-lo.patch b/SOURCES/0012-contrib-selinux-Let-interface-users-set-paths-for-lo.patch new file mode 100644 index 0000000..ee43f22 --- /dev/null +++ b/SOURCES/0012-contrib-selinux-Let-interface-users-set-paths-for-lo.patch @@ -0,0 +1,65 @@ +From 7c01aa69bcd52197af708b9e08ce3067624288ef Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Mon, 6 Mar 2023 23:19:18 +0000 +Subject: [PATCH 12/20] contrib/selinux: Let interface users set paths for log, + PID, socket files + +Even libvirt itself will configure passt to write log, PID and socket +files to different locations depending on whether the domain is +started as root (/var/log/libvirt/...) or as a regular user +(/var/log//libvirt/...), and user_tmp_t would only cover the +latter. + +Create interfaces for log and PID files, so that callers can specify +different file contexts for those, and modify the interface for the +UNIX socket file to allow different paths as well. + +Signed-off-by: Stefano Brivio +Tested-by: Laine Stump +Reviewed-by: Laine Stump +(cherry picked from commit d361fe6e809bdf3539d764cfa5058f46ce51bcbf) +--- + contrib/selinux/passt.if | 26 +++++++++++++++++++++++++- + 1 file changed, 25 insertions(+), 1 deletion(-) + +diff --git a/contrib/selinux/passt.if b/contrib/selinux/passt.if +index 893395b..6a6105c 100644 +--- a/contrib/selinux/passt.if ++++ b/contrib/selinux/passt.if +@@ -30,8 +30,32 @@ interface(`passt_socket',` + type passt_t; + ') + +- allow $1 user_tmp_t:sock_file write; ++ allow $1 $2:sock_file write; + allow $1 passt_t:unix_stream_socket connectto; ++ ++ allow passt_t $2:sock_file { create read write unlink }; ++') ++ ++interface(`passt_logfile',` ++ gen_require(` ++ type passt_t; ++ ') ++ ++ logging_log_file($1); ++ allow passt_t $1:dir { search write add_name }; ++ allow passt_t $1:file { create open read write }; ++') ++ ++interface(`passt_pidfile',` ++ gen_require(` ++ type passt_t; ++ ') ++ ++ allow $1 $2:file { open read unlink }; ++ ++ files_pid_file($2); ++ allow passt_t $2:dir { search write add_name }; ++ allow passt_t $2:file { create open write }; + ') + + interface(`passt_kill',` +-- +2.39.2 + diff --git a/SOURCES/0013-tcp-udp-util-Pass-socket-creation-errors-all-the-way.patch b/SOURCES/0013-tcp-udp-util-Pass-socket-creation-errors-all-the-way.patch new file mode 100644 index 0000000..71b7819 --- /dev/null +++ b/SOURCES/0013-tcp-udp-util-Pass-socket-creation-errors-all-the-way.patch @@ -0,0 +1,243 @@ +From 812d24c91aecff78369f32f6593045f24d578d38 Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Wed, 8 Mar 2023 12:14:29 +0100 +Subject: [PATCH 13/20] tcp, udp, util: Pass socket creation errors all the way + up + +...starting from sock_l4(), pass negative error (errno) codes instead +of -1. They will only be used in two commits from now, no functional +changes intended here. + +Signed-off-by: Stefano Brivio +Reviewed-by: David Gibson +(cherry picked from commit 73992c42cea0df56f6ba0a3bef0f4a939f26ebad) +--- + tcp.c | 22 ++++++++++++---------- + udp.c | 18 +++++++++--------- + util.c | 31 ++++++++++++++++++------------- + 3 files changed, 39 insertions(+), 32 deletions(-) + +diff --git a/tcp.c b/tcp.c +index fe6e458..482c2f9 100644 +--- a/tcp.c ++++ b/tcp.c +@@ -2891,7 +2891,7 @@ void tcp_sock_handler(struct ctx *c, union epoll_ref ref, uint32_t events, + * @addr: Pointer to address for binding, NULL if not configured + * @ifname: Name of interface to bind to, NULL if not configured + * +- * Return: fd for the new listening socket, or -1 on failure ++ * Return: fd for the new listening socket, negative error code on failure + */ + static int tcp_sock_init_af(const struct ctx *c, int af, in_port_t port, + const struct in_addr *addr, const char *ifname) +@@ -2904,13 +2904,13 @@ static int tcp_sock_init_af(const struct ctx *c, int af, in_port_t port, + + if (c->tcp.fwd_in.mode == FWD_AUTO) { + if (af == AF_INET || af == AF_UNSPEC) +- tcp_sock_init_ext[port][V4] = s; ++ tcp_sock_init_ext[port][V4] = s < 0 ? -1 : s; + if (af == AF_INET6 || af == AF_UNSPEC) +- tcp_sock_init_ext[port][V6] = s; ++ tcp_sock_init_ext[port][V6] = s < 0 ? -1 : s; + } + + if (s < 0) +- return -1; ++ return s; + + tcp_sock_set_bufsize(c, s); + return s; +@@ -2924,12 +2924,12 @@ static int tcp_sock_init_af(const struct ctx *c, int af, in_port_t port, + * @ifname: Name of interface to bind to, NULL if not configured + * @port: Port, host order + * +- * Return: 0 on (partial) success, -1 on (complete) failure ++ * Return: 0 on (partial) success, negative error code on (complete) failure + */ + int tcp_sock_init(const struct ctx *c, sa_family_t af, const void *addr, + const char *ifname, in_port_t port) + { +- int ret = 0; ++ int ret = 0, af_ret; + + if (af == AF_UNSPEC && c->ifi4 && c->ifi6) + /* Attempt to get a dual stack socket */ +@@ -2938,13 +2938,15 @@ int tcp_sock_init(const struct ctx *c, sa_family_t af, const void *addr, + + /* Otherwise create a socket per IP version */ + if ((af == AF_INET || af == AF_UNSPEC) && c->ifi4) { +- if (tcp_sock_init_af(c, AF_INET, port, addr, ifname) < 0) +- ret = -1; ++ af_ret = tcp_sock_init_af(c, AF_INET, port, addr, ifname); ++ if (af_ret < 0) ++ ret = af_ret; + } + + if ((af == AF_INET6 || af == AF_UNSPEC) && c->ifi6) { +- if (tcp_sock_init_af(c, AF_INET6, port, addr, ifname) < 0) +- ret = -1; ++ af_ret = tcp_sock_init_af(c, AF_INET6, port, addr, ifname); ++ if (af_ret < 0) ++ ret = af_ret; + } + + return ret; +diff --git a/udp.c b/udp.c +index 20a9ea0..9a43835 100644 +--- a/udp.c ++++ b/udp.c +@@ -956,7 +956,7 @@ int udp_tap_handler(struct ctx *c, int af, const void *addr, + * @ifname: Name of interface to bind to, NULL if not configured + * @port: Port, host order + * +- * Return: 0 on (partial) success, -1 on (complete) failure ++ * Return: 0 on (partial) success, negative error code on (complete) failure + */ + int udp_sock_init(const struct ctx *c, int ns, sa_family_t af, + const void *addr, const char *ifname, in_port_t port) +@@ -981,19 +981,19 @@ int udp_sock_init(const struct ctx *c, int ns, sa_family_t af, + s = sock_l4(c, AF_INET, IPPROTO_UDP, addr, ifname, + port, uref.u32); + +- udp_tap_map[V4][uref.udp.port].sock = s; +- udp_splice_init[V4][port].sock = s; ++ udp_tap_map[V4][uref.udp.port].sock = s < 0 ? -1 : s; ++ udp_splice_init[V4][port].sock = s < 0 ? -1 : s; + } else { + struct in_addr loopback = { htonl(INADDR_LOOPBACK) }; + uref.udp.ns = true; + + s = sock_l4(c, AF_INET, IPPROTO_UDP, &loopback, + ifname, port, uref.u32); +- udp_splice_ns[V4][port].sock = s; ++ udp_splice_ns[V4][port].sock = s < 0 ? -1 : s; + } + + if (s < 0) +- ret = -1; ++ ret = s; + } + + if ((af == AF_INET6 || af == AF_UNSPEC) && c->ifi6) { +@@ -1005,18 +1005,18 @@ int udp_sock_init(const struct ctx *c, int ns, sa_family_t af, + s = sock_l4(c, AF_INET6, IPPROTO_UDP, addr, ifname, + port, uref.u32); + +- udp_tap_map[V6][uref.udp.port].sock = s; +- udp_splice_init[V6][port].sock = s; ++ udp_tap_map[V6][uref.udp.port].sock = s < 0 ? -1 : s; ++ udp_splice_init[V6][port].sock = s < 0 ? -1 : s; + } else { + uref.udp.ns = true; + + s = sock_l4(c, AF_INET6, IPPROTO_UDP, &in6addr_loopback, + ifname, port, uref.u32); +- udp_splice_ns[V6][port].sock = s; ++ udp_splice_ns[V6][port].sock = s < 0 ? -1 : s; + } + + if (s < 0) +- ret = -1; ++ ret = s; + } + + return ret; +diff --git a/util.c b/util.c +index c5ee1c0..13f8fab 100644 +--- a/util.c ++++ b/util.c +@@ -95,7 +95,7 @@ found: + * @port: Port, host order + * @data: epoll reference portion for protocol handlers + * +- * Return: newly created socket, -1 on error ++ * Return: newly created socket, negative error code on failure + */ + int sock_l4(const struct ctx *c, int af, uint8_t proto, + const void *bind_addr, const char *ifname, uint16_t port, +@@ -114,16 +114,16 @@ int sock_l4(const struct ctx *c, int af, uint8_t proto, + }; + const struct sockaddr *sa; + bool dual_stack = false; ++ int fd, sl, y = 1, ret; + struct epoll_event ev; +- int fd, sl, y = 1; + + if (proto != IPPROTO_TCP && proto != IPPROTO_UDP && + proto != IPPROTO_ICMP && proto != IPPROTO_ICMPV6) +- return -1; /* Not implemented. */ ++ return -EPFNOSUPPORT; /* Not implemented. */ + + if (af == AF_UNSPEC) { + if (!DUAL_STACK_SOCKETS || bind_addr) +- return -1; ++ return -EINVAL; + dual_stack = true; + af = AF_INET6; + } +@@ -133,14 +133,15 @@ int sock_l4(const struct ctx *c, int af, uint8_t proto, + else + fd = socket(af, SOCK_DGRAM | SOCK_NONBLOCK, proto); + ++ ret = -errno; + if (fd < 0) { +- warn("L4 socket: %s", strerror(errno)); +- return -1; ++ warn("L4 socket: %s", strerror(-ret)); ++ return ret; + } + + if (fd > SOCKET_MAX) { + close(fd); +- return -1; ++ return -EBADF; + } + + ref.r.s = fd; +@@ -185,10 +186,11 @@ int sock_l4(const struct ctx *c, int af, uint8_t proto, + */ + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, + ifname, strlen(ifname))) { ++ ret = -errno; + warn("Can't bind socket for %s port %u to %s, closing", + ip_proto_str[proto], port, ifname); + close(fd); +- return -1; ++ return ret; + } + } + +@@ -199,22 +201,25 @@ int sock_l4(const struct ctx *c, int af, uint8_t proto, + * broken SELinux policy, see icmp_tap_handler(). + */ + if (proto != IPPROTO_ICMP && proto != IPPROTO_ICMPV6) { ++ ret = -errno; + close(fd); +- return -1; ++ return ret; + } + } + + if (proto == IPPROTO_TCP && listen(fd, 128) < 0) { +- warn("TCP socket listen: %s", strerror(errno)); ++ ret = -errno; ++ warn("TCP socket listen: %s", strerror(-ret)); + close(fd); +- return -1; ++ return ret; + } + + ev.events = EPOLLIN; + ev.data.u64 = ref.u64; + if (epoll_ctl(c->epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) { +- warn("L4 epoll_ctl: %s", strerror(errno)); +- return -1; ++ ret = -errno; ++ warn("L4 epoll_ctl: %s", strerror(-ret)); ++ return ret; + } + + return fd; +-- +2.39.2 + diff --git a/SOURCES/0014-tcp-udp-Fix-partial-success-return-codes-in-tcp-udp-.patch b/SOURCES/0014-tcp-udp-Fix-partial-success-return-codes-in-tcp-udp-.patch new file mode 100644 index 0000000..4e83621 --- /dev/null +++ b/SOURCES/0014-tcp-udp-Fix-partial-success-return-codes-in-tcp-udp-.patch @@ -0,0 +1,139 @@ +From 8f71f17cc087fc80f1cbe9b71852b95916a9afbc Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Wed, 8 Mar 2023 12:38:39 +0100 +Subject: [PATCH 14/20] tcp, udp: Fix partial success return codes in + {tcp,udp}_sock_init() + +The comments say we should return 0 on partial success, and an error +code on complete failure. Rationale: if the user configures a port +forwarding, and we succeed to bind that port for IPv4 or IPv6 only, +that might actually be what the user intended. + +Adjust the two functions to reflect the comments. + +Signed-off-by: Stefano Brivio +Reviewed-by: David Gibson +(cherry picked from commit 5aea2f88ab5f63f01885109a4afb1271607fc06b) +--- + tcp.c | 21 +++++++++------------ + udp.c | 30 ++++++++++++++---------------- + 2 files changed, 23 insertions(+), 28 deletions(-) + +diff --git a/tcp.c b/tcp.c +index 482c2f9..001fa50 100644 +--- a/tcp.c ++++ b/tcp.c +@@ -2929,7 +2929,7 @@ static int tcp_sock_init_af(const struct ctx *c, int af, in_port_t port, + int tcp_sock_init(const struct ctx *c, sa_family_t af, const void *addr, + const char *ifname, in_port_t port) + { +- int ret = 0, af_ret; ++ int r4 = SOCKET_MAX + 1, r6 = SOCKET_MAX + 1; + + if (af == AF_UNSPEC && c->ifi4 && c->ifi6) + /* Attempt to get a dual stack socket */ +@@ -2937,19 +2937,16 @@ int tcp_sock_init(const struct ctx *c, sa_family_t af, const void *addr, + return 0; + + /* Otherwise create a socket per IP version */ +- if ((af == AF_INET || af == AF_UNSPEC) && c->ifi4) { +- af_ret = tcp_sock_init_af(c, AF_INET, port, addr, ifname); +- if (af_ret < 0) +- ret = af_ret; +- } ++ if ((af == AF_INET || af == AF_UNSPEC) && c->ifi4) ++ r4 = tcp_sock_init_af(c, AF_INET, port, addr, ifname); + +- if ((af == AF_INET6 || af == AF_UNSPEC) && c->ifi6) { +- af_ret = tcp_sock_init_af(c, AF_INET6, port, addr, ifname); +- if (af_ret < 0) +- ret = af_ret; +- } ++ if ((af == AF_INET6 || af == AF_UNSPEC) && c->ifi6) ++ r6 = tcp_sock_init_af(c, AF_INET6, port, addr, ifname); + +- return ret; ++ if (IN_INTERVAL(0, SOCKET_MAX, r4) || IN_INTERVAL(0, SOCKET_MAX, r6)) ++ return 0; ++ ++ return r4 < 0 ? r4 : r6; + } + + /** +diff --git a/udp.c b/udp.c +index 9a43835..20ee0f2 100644 +--- a/udp.c ++++ b/udp.c +@@ -962,7 +962,7 @@ int udp_sock_init(const struct ctx *c, int ns, sa_family_t af, + const void *addr, const char *ifname, in_port_t port) + { + union udp_epoll_ref uref = { .u32 = 0 }; +- int s, ret = 0; ++ int s, r4 = SOCKET_MAX + 1, r6 = SOCKET_MAX + 1; + + if (ns) { + uref.udp.port = (in_port_t)(port + +@@ -978,8 +978,8 @@ int udp_sock_init(const struct ctx *c, int ns, sa_family_t af, + uref.udp.orig = true; + + if (!ns) { +- s = sock_l4(c, AF_INET, IPPROTO_UDP, addr, ifname, +- port, uref.u32); ++ r4 = s = sock_l4(c, AF_INET, IPPROTO_UDP, addr, ++ ifname, port, uref.u32); + + udp_tap_map[V4][uref.udp.port].sock = s < 0 ? -1 : s; + udp_splice_init[V4][port].sock = s < 0 ? -1 : s; +@@ -987,13 +987,10 @@ int udp_sock_init(const struct ctx *c, int ns, sa_family_t af, + struct in_addr loopback = { htonl(INADDR_LOOPBACK) }; + uref.udp.ns = true; + +- s = sock_l4(c, AF_INET, IPPROTO_UDP, &loopback, +- ifname, port, uref.u32); ++ r4 = s = sock_l4(c, AF_INET, IPPROTO_UDP, &loopback, ++ ifname, port, uref.u32); + udp_splice_ns[V4][port].sock = s < 0 ? -1 : s; + } +- +- if (s < 0) +- ret = s; + } + + if ((af == AF_INET6 || af == AF_UNSPEC) && c->ifi6) { +@@ -1002,24 +999,25 @@ int udp_sock_init(const struct ctx *c, int ns, sa_family_t af, + uref.udp.orig = true; + + if (!ns) { +- s = sock_l4(c, AF_INET6, IPPROTO_UDP, addr, ifname, +- port, uref.u32); ++ r6 = s = sock_l4(c, AF_INET6, IPPROTO_UDP, addr, ++ ifname, port, uref.u32); + + udp_tap_map[V6][uref.udp.port].sock = s < 0 ? -1 : s; + udp_splice_init[V6][port].sock = s < 0 ? -1 : s; + } else { + uref.udp.ns = true; + +- s = sock_l4(c, AF_INET6, IPPROTO_UDP, &in6addr_loopback, +- ifname, port, uref.u32); ++ r6 = s = sock_l4(c, AF_INET6, IPPROTO_UDP, ++ &in6addr_loopback, ++ ifname, port, uref.u32); + udp_splice_ns[V6][port].sock = s < 0 ? -1 : s; + } +- +- if (s < 0) +- ret = s; + } + +- return ret; ++ if (IN_INTERVAL(0, SOCKET_MAX, r4) || IN_INTERVAL(0, SOCKET_MAX, r6)) ++ return 0; ++ ++ return r4 < 0 ? r4 : r6; + } + + /** +-- +2.39.2 + diff --git a/SOURCES/0015-conf-Terminate-on-EMFILE-or-ENFILE-on-sockets-for-po.patch b/SOURCES/0015-conf-Terminate-on-EMFILE-or-ENFILE-on-sockets-for-po.patch new file mode 100644 index 0000000..1ffcb68 --- /dev/null +++ b/SOURCES/0015-conf-Terminate-on-EMFILE-or-ENFILE-on-sockets-for-po.patch @@ -0,0 +1,112 @@ +From 1df417752faf52f17d8ec14578ffde89ced32772 Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Wed, 8 Mar 2023 13:21:19 +0100 +Subject: [PATCH 15/20] conf: Terminate on EMFILE or ENFILE on sockets for port + mapping + +In general, we don't terminate or report failures if we fail to bind +to some ports out of a given port range specifier, to allow users to +conveniently specify big port ranges (or "all") without having to +care about ports that might already be in use. + +However, running out of the open file descriptors quota is a +different story: we can't do what the user requested in a very +substantial way. + +For example, if the user specifies '-t all' and we can only bind +1024 sockets, the behaviour is rather unexpected. + +Fail whenever socket creation returns -ENFILE or -EMFILE. + +Link: https://bugs.passt.top/show_bug.cgi?id=27 +Signed-off-by: Stefano Brivio +Reviewed-by: David Gibson +(cherry picked from commit bb2b67cb3549ea2509f5b7b88790e08d2e362351) +--- + conf.c | 36 +++++++++++++++++++++++++++++------- + 1 file changed, 29 insertions(+), 7 deletions(-) + +diff --git a/conf.c b/conf.c +index 37f25d6..7f25a22 100644 +--- a/conf.c ++++ b/conf.c +@@ -182,6 +182,7 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg, + bool exclude_only = true, bound_one = false; + uint8_t exclude[PORT_BITMAP_SIZE] = { 0 }; + sa_family_t af = AF_UNSPEC; ++ int ret; + + if (!strcmp(optarg, "none")) { + if (fwd->mode) +@@ -216,11 +217,18 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg, + + for (i = 0; i < PORT_EPHEMERAL_MIN; i++) { + if (optname == 't') { +- if (!tcp_sock_init(c, AF_UNSPEC, NULL, NULL, i)) ++ ret = tcp_sock_init(c, AF_UNSPEC, NULL, NULL, ++ i); ++ if (ret == -ENFILE || ret == -EMFILE) ++ goto enfile; ++ if (!ret) + bound_one = true; + } else if (optname == 'u') { +- if (!udp_sock_init(c, 0, AF_UNSPEC, NULL, NULL, +- i)) ++ ret = udp_sock_init(c, 0, AF_UNSPEC, NULL, NULL, ++ i); ++ if (ret == -ENFILE || ret == -EMFILE) ++ goto enfile; ++ if (!ret) + bound_one = true; + } + } +@@ -301,10 +309,16 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg, + bitmap_set(fwd->map, i); + + if (optname == 't') { +- if (!tcp_sock_init(c, af, addr, ifname, i)) ++ ret = tcp_sock_init(c, af, addr, ifname, i); ++ if (ret == -ENFILE || ret == -EMFILE) ++ goto enfile; ++ if (!ret) + bound_one = true; + } else if (optname == 'u') { +- if (!udp_sock_init(c, 0, af, addr, ifname, i)) ++ ret = udp_sock_init(c, 0, af, addr, ifname, i); ++ if (ret == -ENFILE || ret == -EMFILE) ++ goto enfile; ++ if (!ret) + bound_one = true; + } else { + /* No way to check in advance for -T and -U */ +@@ -356,10 +370,16 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg, + fwd->delta[i] = mapped_range.first - orig_range.first; + + if (optname == 't') { +- if (!tcp_sock_init(c, af, addr, ifname, i)) ++ ret = tcp_sock_init(c, af, addr, ifname, i); ++ if (ret == -ENFILE || ret == -EMFILE) ++ goto enfile; ++ if (!ret) + bound_one = true; + } else if (optname == 'u') { +- if (!udp_sock_init(c, 0, af, addr, ifname, i)) ++ ret = udp_sock_init(c, 0, af, addr, ifname, i); ++ if (ret == -ENFILE || ret == -EMFILE) ++ goto enfile; ++ if (!ret) + bound_one = true; + } else { + /* No way to check in advance for -T and -U */ +@@ -372,6 +392,8 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg, + goto bind_fail; + + return; ++enfile: ++ die("Can't open enough sockets for port specifier: %s", optarg); + bad: + die("Invalid port specifier %s", optarg); + overlap: +-- +2.39.2 + diff --git a/SOURCES/0016-tcp-Clamp-MSS-value-when-queueing-data-to-tap-also-f.patch b/SOURCES/0016-tcp-Clamp-MSS-value-when-queueing-data-to-tap-also-f.patch new file mode 100644 index 0000000..02c6c0a --- /dev/null +++ b/SOURCES/0016-tcp-Clamp-MSS-value-when-queueing-data-to-tap-also-f.patch @@ -0,0 +1,129 @@ +From 96bf75e8ebffe06d33b5dee20c44e16ce53ea663 Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Wed, 8 Mar 2023 18:07:42 +0100 +Subject: [PATCH 16/20] tcp: Clamp MSS value when queueing data to tap, also + for pasta + +Tom reports that a pattern of repated ~1 MiB chunks downloads over +NNTP over TLS, on Podman 4.4 using pasta as network back-end, results +in pasta taking one full CPU thread after a while, and the download +never succeeds. + +On that setup, we end up re-sending the same frame over and over, +with a consistent 65 534 bytes size, and never get an +acknowledgement from the tap-side client. This only happens for the +default MTU value (65 520 bytes) or for values that are slightly +smaller than that (down to 64 499 bytes). + +We hit this condition because the MSS value we use in +tcp_data_from_sock(), only in pasta mode, is simply clamped to +USHRT_MAX, and not to the actual size of the buffers we pre-cooked +for sending, which is a bit less than that. + +It looks like we got away with it until commit 0fb7b2b9080a ("tap: +Use different io vector bases depending on tap type") fixed the +setting of iov_len. + +Luckily, since it's pasta, we're queueing up to two frames at a time, +so the worst that can happen is a badly segmented TCP stream: we +always have some space at the tail of the buffer. + +Clamp the MSS value to the appropriate maximum given by struct +tcp{4,6}_buf_data_t, no matter if we're running in pasta or passt +mode. + +While at it, fix the comments to those structs to reflect the current +struct size. This is not really relevant for any further calculation +or consideration, but it's convenient to know while debugging this +kind of issues. + +Thanks to Tom for reporting the issue in a very detailed way and for +providing a test setup. + +Reported-by: Tom Mombourquette +Link: https://github.com/containers/podman/issues/17703 +Signed-off-by: Stefano Brivio +Reviewed-by: David Gibson +(cherry picked from commit d7272f1df89c099a7e98ae43d1ef9b936c7e46f7) +--- + tcp.c | 23 +++++++++-------------- + 1 file changed, 9 insertions(+), 14 deletions(-) + +diff --git a/tcp.c b/tcp.c +index 001fa50..1355b0e 100644 +--- a/tcp.c ++++ b/tcp.c +@@ -460,7 +460,7 @@ static struct tcp4_l2_buf_t { + struct iphdr iph; /* 44 28 */ + struct tcphdr th; /* 64 48 */ + uint8_t data[MSS4]; /* 84 68 */ +- /* 65541 65525 */ ++ /* 65536 65532 */ + #ifdef __AVX2__ + } __attribute__ ((packed, aligned(32))) + #else +@@ -488,7 +488,7 @@ struct tcp6_l2_buf_t { + struct ipv6hdr ip6h; /* 32 20 */ + struct tcphdr th; /* 72 60 */ + uint8_t data[MSS6]; /* 92 80 */ +- /* 65639 65627 */ ++ /* 65536 65532 */ + #ifdef __AVX2__ + } __attribute__ ((packed, aligned(32))) + #else +@@ -1913,15 +1913,13 @@ int tcp_conn_new_sock(const struct ctx *c, sa_family_t af) + + /** + * tcp_conn_tap_mss() - Get MSS value advertised by tap/guest +- * @c: Execution context + * @conn: Connection pointer + * @opts: Pointer to start of TCP options + * @optlen: Bytes in options: caller MUST ensure available length + * + * Return: clamped MSS value + */ +-static uint16_t tcp_conn_tap_mss(const struct ctx *c, +- const struct tcp_tap_conn *conn, ++static uint16_t tcp_conn_tap_mss(const struct tcp_tap_conn *conn, + const char *opts, size_t optlen) + { + unsigned int mss; +@@ -1932,13 +1930,10 @@ static uint16_t tcp_conn_tap_mss(const struct ctx *c, + else + mss = ret; + +- /* Don't upset qemu */ +- if (c->mode == MODE_PASST) { +- if (CONN_V4(conn)) +- mss = MIN(MSS4, mss); +- else +- mss = MIN(MSS6, mss); +- } ++ if (CONN_V4(conn)) ++ mss = MIN(MSS4, mss); ++ else ++ mss = MIN(MSS6, mss); + + return MIN(mss, USHRT_MAX); + } +@@ -2007,7 +2002,7 @@ static void tcp_conn_from_tap(struct ctx *c, int af, const void *addr, + + conn->wnd_to_tap = WINDOW_DEFAULT; + +- mss = tcp_conn_tap_mss(c, conn, opts, optlen); ++ mss = tcp_conn_tap_mss(conn, opts, optlen); + if (setsockopt(s, SOL_TCP, TCP_MAXSEG, &mss, sizeof(mss))) + trace("TCP: failed to set TCP_MAXSEG on socket %i", s); + MSS_SET(conn, mss); +@@ -2469,7 +2464,7 @@ static void tcp_conn_from_sock_finish(struct ctx *c, struct tcp_tap_conn *conn, + if (!(conn->wnd_from_tap >>= conn->ws_from_tap)) + conn->wnd_from_tap = 1; + +- MSS_SET(conn, tcp_conn_tap_mss(c, conn, opts, optlen)); ++ MSS_SET(conn, tcp_conn_tap_mss(conn, opts, optlen)); + + conn->seq_init_from_tap = ntohl(th->seq) + 1; + conn->seq_from_tap = conn->seq_init_from_tap; +-- +2.39.2 + diff --git a/SOURCES/0017-contrib-selinux-Drop-example-from-headers-this-is-th.patch b/SOURCES/0017-contrib-selinux-Drop-example-from-headers-this-is-th.patch new file mode 100644 index 0000000..933de64 --- /dev/null +++ b/SOURCES/0017-contrib-selinux-Drop-example-from-headers-this-is-th.patch @@ -0,0 +1,98 @@ +From def7d05a8babf5ea227f688f1f3a8bce286f97a3 Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Fri, 10 Mar 2023 14:53:14 +0000 +Subject: [PATCH 17/20] contrib/selinux: Drop "example" from headers: this is + the actual policy + +Signed-off-by: Stefano Brivio +(cherry picked from commit 9f35cf0b11891e9dfb12eeb5d52f728881f84967) +--- + contrib/selinux/passt.fc | 2 +- + contrib/selinux/passt.if | 2 +- + contrib/selinux/passt.te | 2 +- + contrib/selinux/pasta.fc | 2 +- + contrib/selinux/pasta.if | 2 +- + contrib/selinux/pasta.te | 2 +- + 6 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/contrib/selinux/passt.fc b/contrib/selinux/passt.fc +index 286c868..88e388e 100644 +--- a/contrib/selinux/passt.fc ++++ b/contrib/selinux/passt.fc +@@ -3,7 +3,7 @@ + # PASST - Plug A Simple Socket Transport + # for qemu/UNIX domain socket mode + # +-# contrib/selinux/passt.fc - SELinux profile example: File Context for passt ++# contrib/selinux/passt.fc - SELinux profile: File Context for passt + # + # Copyright (c) 2022 Red Hat GmbH + # Author: Stefano Brivio +diff --git a/contrib/selinux/passt.if b/contrib/selinux/passt.if +index 6a6105c..a79a8ec 100644 +--- a/contrib/selinux/passt.if ++++ b/contrib/selinux/passt.if +@@ -3,7 +3,7 @@ + # PASST - Plug A Simple Socket Transport + # for qemu/UNIX domain socket mode + # +-# contrib/selinux/passt.if - SELinux profile example: Interface File for passt ++# contrib/selinux/passt.if - SELinux profile: Interface File for passt + # + # Copyright (c) 2022 Red Hat GmbH + # Author: Stefano Brivio +diff --git a/contrib/selinux/passt.te b/contrib/selinux/passt.te +index 438155d..590ad40 100644 +--- a/contrib/selinux/passt.te ++++ b/contrib/selinux/passt.te +@@ -3,7 +3,7 @@ + # PASST - Plug A Simple Socket Transport + # for qemu/UNIX domain socket mode + # +-# contrib/selinux/passt.te - SELinux profile example: Type Enforcement for passt ++# contrib/selinux/passt.te - SELinux profile: Type Enforcement for passt + # + # Copyright (c) 2022 Red Hat GmbH + # Author: Stefano Brivio +diff --git a/contrib/selinux/pasta.fc b/contrib/selinux/pasta.fc +index f8fa0fa..e72b4ac 100644 +--- a/contrib/selinux/pasta.fc ++++ b/contrib/selinux/pasta.fc +@@ -3,7 +3,7 @@ + # PASTA - Pack A Subtle Tap Abstraction + # for network namespace/tap device mode + # +-# contrib/selinux/pasta.fc - SELinux profile example: File Context for pasta ++# contrib/selinux/pasta.fc - SELinux profile: File Context for pasta + # + # Copyright (c) 2022 Red Hat GmbH + # Author: Stefano Brivio +diff --git a/contrib/selinux/pasta.if b/contrib/selinux/pasta.if +index a42bfcd..149045a 100644 +--- a/contrib/selinux/pasta.if ++++ b/contrib/selinux/pasta.if +@@ -3,7 +3,7 @@ + # PASTA - Pack A Subtle Tap Abstraction + # for network namespace/tap device mode + # +-# contrib/selinux/pasta.if - SELinux profile example: Interface File for pasta ++# contrib/selinux/pasta.if - SELinux profile: Interface File for pasta + # + # Copyright (c) 2022 Red Hat GmbH + # Author: Stefano Brivio +diff --git a/contrib/selinux/pasta.te b/contrib/selinux/pasta.te +index 8986c0c..7856019 100644 +--- a/contrib/selinux/pasta.te ++++ b/contrib/selinux/pasta.te +@@ -3,7 +3,7 @@ + # PASTA - Pack A Subtle Tap Abstraction + # for network namespace/tap device mode + # +-# contrib/selinux/pasta.te - SELinux profile example: Type Enforcement for pasta ++# contrib/selinux/pasta.te - SELinux profile: Type Enforcement for pasta + # + # Copyright (c) 2022 Red Hat GmbH + # Author: Stefano Brivio +-- +2.39.2 + diff --git a/SOURCES/0018-contrib-selinux-Drop-unused-passt_read_data-interfac.patch b/SOURCES/0018-contrib-selinux-Drop-unused-passt_read_data-interfac.patch new file mode 100644 index 0000000..7d71b8c --- /dev/null +++ b/SOURCES/0018-contrib-selinux-Drop-unused-passt_read_data-interfac.patch @@ -0,0 +1,34 @@ +From 6b5f382f342ad4a0a46e30b5ba165713768e363a Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Fri, 10 Mar 2023 14:53:37 +0000 +Subject: [PATCH 18/20] contrib/selinux: Drop unused passt_read_data() + interface + +Signed-off-by: Stefano Brivio +(cherry picked from commit dcdc50fc2251339d6e929f708fad114e61b60627) +--- + contrib/selinux/passt.if | 8 -------- + 1 file changed, 8 deletions(-) + +diff --git a/contrib/selinux/passt.if b/contrib/selinux/passt.if +index a79a8ec..3e37c5b 100644 +--- a/contrib/selinux/passt.if ++++ b/contrib/selinux/passt.if +@@ -8,14 +8,6 @@ + # Copyright (c) 2022 Red Hat GmbH + # Author: Stefano Brivio + +-interface(`passt_read_data',` +- gen_require(` +- type passt_data_t; +- ') +- allow $1 passt_t:dir { search add_name }; +- allow $1 passt_t:file { open read getattr }; +-') +- + interface(`passt_domtrans',` + gen_require(` + type passt_t, passt_exec_t; +-- +2.39.2 + diff --git a/SOURCES/0019-contrib-selinux-Split-interfaces-into-smaller-bits.patch b/SOURCES/0019-contrib-selinux-Split-interfaces-into-smaller-bits.patch new file mode 100644 index 0000000..456f04f --- /dev/null +++ b/SOURCES/0019-contrib-selinux-Split-interfaces-into-smaller-bits.patch @@ -0,0 +1,120 @@ +From 80978901354b17cf3460d5d4451b48b59a9204ae Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Fri, 10 Mar 2023 17:00:31 +0000 +Subject: [PATCH 19/20] contrib/selinux: Split interfaces into smaller bits + +...to fit accepted Fedora practices. + +Link: https://github.com/fedora-selinux/selinux-policy/pull/1613 +Signed-off-by: Stefano Brivio +(cherry picked from commit 93105ea06619d4c199f8140f4b75ae359757dc6d) +--- + contrib/selinux/passt.if | 71 ++++++++++++++++++++++++++++++++++------ + 1 file changed, 61 insertions(+), 10 deletions(-) + +diff --git a/contrib/selinux/passt.if b/contrib/selinux/passt.if +index 3e37c5b..f7560a7 100644 +--- a/contrib/selinux/passt.if ++++ b/contrib/selinux/passt.if +@@ -17,37 +17,88 @@ interface(`passt_domtrans',` + domtrans_pattern($1, passt_exec_t, passt_t) + ') + +-interface(`passt_socket',` ++interface(`passt_socket_dir',` ++ gen_require(` ++ type passt_t; ++ ') ++ ++ allow passt_t $1:dir add_entry_dir_perms; ++') ++ ++interface(`passt_socket_create',` ++ gen_require(` ++ type passt_t; ++ ') ++ ++ allow passt_t $1:sock_file create; ++') ++ ++interface(`passt_socket_use',` + gen_require(` + type passt_t; + ') + +- allow $1 $2:sock_file write; + allow $1 passt_t:unix_stream_socket connectto; ++ allow $1 $2:sock_file { read write }; ++ allow passt_t $2:sock_file { read write }; ++') ++ ++interface(`passt_socket_delete',` ++ gen_require(` ++ type passt_t; ++ ') ++ ++ allow $1 $2:sock_file unlink; ++') ++ ++interface(`passt_logfile_dir',` ++ gen_require(` ++ type passt_t; ++ ') + +- allow passt_t $2:sock_file { create read write unlink }; ++ allow passt_t $1:dir add_entry_dir_perms; + ') + +-interface(`passt_logfile',` ++interface(`passt_logfile_use',` + gen_require(` + type passt_t; + ') + + logging_log_file($1); +- allow passt_t $1:dir { search write add_name }; + allow passt_t $1:file { create open read write }; + ') + +-interface(`passt_pidfile',` ++interface(`passt_pidfile_dir',` ++ gen_require(` ++ type passt_t; ++ ') ++ ++ allow passt_t $1:dir add_entry_dir_perms; ++') ++ ++interface(`passt_pidfile_write',` ++ gen_require(` ++ type passt_t; ++ ') ++ ++ files_pid_file($1); ++ allow passt_t $1:file { create open write }; ++') ++ ++interface(`passt_pidfile_read',` + gen_require(` + type passt_t; + ') + +- allow $1 $2:file { open read unlink }; ++ allow $1 $2:file { open read }; ++') ++ ++interface(`passt_pidfile_delete',` ++ gen_require(` ++ type passt_t; ++ ') + +- files_pid_file($2); +- allow passt_t $2:dir { search write add_name }; +- allow passt_t $2:file { create open write }; ++ allow $1 $2:file unlink; + ') + + interface(`passt_kill',` +-- +2.39.2 + diff --git a/SOURCES/0020-fedora-Install-SELinux-interface-files-to-shared-inc.patch b/SOURCES/0020-fedora-Install-SELinux-interface-files-to-shared-inc.patch new file mode 100644 index 0000000..c33821c --- /dev/null +++ b/SOURCES/0020-fedora-Install-SELinux-interface-files-to-shared-inc.patch @@ -0,0 +1,40 @@ +From 61676035ec5f58df4f4cca024c772d7e0b43d7dc Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Fri, 10 Mar 2023 19:10:01 +0100 +Subject: [PATCH 20/20] fedora: Install SELinux interface files to shared + include directory + +Link: https://github.com/fedora-selinux/selinux-policy/pull/1613 +Signed-off-by: Stefano Brivio +(cherry picked from commit 70c0765b49e19b76639908a7686d8f795ba3ed24) +--- + contrib/fedora/passt.spec | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/contrib/fedora/passt.spec b/contrib/fedora/passt.spec +index 7f67bee..51cad90 100644 +--- a/contrib/fedora/passt.spec ++++ b/contrib/fedora/passt.spec +@@ -61,7 +61,9 @@ ln -sr %{buildroot}%{_mandir}/man1/pasta.1 %{buildroot}%{_mandir}/man1/pasta.avx + pushd contrib/selinux + make -f %{_datadir}/selinux/devel/Makefile + install -p -m 644 -D passt.pp %{buildroot}%{_datadir}/selinux/packages/%{name}/passt.pp ++install -p -m 644 -D passt.if %{buildroot}%{_datadir}/selinux/devel/include/contrib/passt.if + install -p -m 644 -D pasta.pp %{buildroot}%{_datadir}/selinux/packages/%{name}/pasta.pp ++install -p -m 644 -D pasta.if %{buildroot}%{_datadir}/selinux/devel/include/contrib/pasta.if + popd + + %post selinux +@@ -93,7 +95,9 @@ semodule -r pasta 2>/dev/null || : + %files selinux + %dir %{_datadir}/selinux/packages/%{name} + %{_datadir}/selinux/packages/%{name}/passt.pp ++%{_datadir}/selinux/devel/include/contrib/passt.if + %{_datadir}/selinux/packages/%{name}/pasta.pp ++%{_datadir}/selinux/devel/include/contrib/pasta.if + + %changelog + {{{ passt_git_changelog }}} +-- +2.39.2 + diff --git a/SPECS/passt.spec b/SPECS/passt.spec new file mode 100644 index 0000000..6a9b56d --- /dev/null +++ b/SPECS/passt.spec @@ -0,0 +1,240 @@ +# PASST - Plug A Simple Socket Transport +# for qemu/UNIX domain socket mode +# +# PASTA - Pack A Subtle Tap Abstraction +# for network namespace/tap device mode +# +# Copyright (c) 2022 Red Hat GmbH +# Author: Stefano Brivio + +%global git_hash 4ddbcb9c0c555838b123c018a9ebc9b7e14a87e5 +%global selinuxtype targeted + +Name: passt +Version: 0^20230222.g4ddbcb9 +Release: 2%{?dist} +Summary: User-mode networking daemons for virtual machines and namespaces +License: AGPLv3+ and BSD +Group: System Environment/Daemons +URL: https://passt.top/ +Source: https://passt.top/passt/snapshot/passt-%{git_hash}.tar.xz + +Patch1: 0001-udp-Actually-use-host-resolver-to-forward-DNS-querie.patch +Patch2: 0002-conf-Split-add_dns-4-6-out-of-get_dns.patch +Patch3: 0003-conf-udp-Allow-any-loopback-address-to-be-used-as-re.patch +Patch4: 0004-tcp-tcp_splice-Get-rid-of-false-positive-CWE-394-Cov.patch +Patch5: 0005-tcp-Avoid-false-but-convoluted-positive-Coverity-CWE.patch +Patch6: 0006-tcp-Avoid-theoretical-resource-leak-CWE-772-Coverity.patch +Patch7: 0007-Fix-definitions-of-SOCKET_MAX-TCP_MAX_CONNS.patch +Patch8: 0008-doc-demo-Fix-and-suppress-ShellCheck-warnings.patch +Patch9: 0009-contrib-selinux-Drop-duplicate-init_daemon_domain-ru.patch +Patch10: 0010-contrib-selinux-Let-passt-write-to-stdout-and-stderr.patch +Patch11: 0011-contrib-selinux-Allow-binding-and-connecting-to-all-.patch +Patch12: 0012-contrib-selinux-Let-interface-users-set-paths-for-lo.patch +Patch13: 0013-tcp-udp-util-Pass-socket-creation-errors-all-the-way.patch +Patch14: 0014-tcp-udp-Fix-partial-success-return-codes-in-tcp-udp-.patch +Patch15: 0015-conf-Terminate-on-EMFILE-or-ENFILE-on-sockets-for-po.patch +Patch16: 0016-tcp-Clamp-MSS-value-when-queueing-data-to-tap-also-f.patch +Patch17: 0017-contrib-selinux-Drop-example-from-headers-this-is-th.patch +Patch18: 0018-contrib-selinux-Drop-unused-passt_read_data-interfac.patch +Patch19: 0019-contrib-selinux-Split-interfaces-into-smaller-bits.patch +Patch20: 0020-fedora-Install-SELinux-interface-files-to-shared-inc.patch + +BuildRequires: gcc, make, git, checkpolicy, selinux-policy-devel +Requires: (%{name}-selinux = %{version}-%{release} if selinux-policy-%{selinuxtype}) + +%description +passt implements a translation layer between a Layer-2 network interface and +native Layer-4 sockets (TCP, UDP, ICMP/ICMPv6 echo) on a host. It doesn't +require any capabilities or privileges, and it can be used as a simple +replacement for Slirp. + +pasta (same binary as passt, different command) offers equivalent functionality, +for network namespaces: traffic is forwarded using a tap interface inside the +namespace, without the need to create further interfaces on the host, hence not +requiring any capabilities or privileges. + +%package selinux +BuildArch: noarch +Summary: SELinux support for passt and pasta +Requires: %{name} = %{version}-%{release} +Requires: selinux-policy +Requires(post): %{name} +Requires(post): policycoreutils +Requires(preun): %{name} +Requires(preun): policycoreutils + +%description selinux +This package adds SELinux enforcement to passt(1) and pasta(1). + +%prep +%autosetup -S git_am -n passt-%{git_hash} + +%build +%set_build_flags +%make_build VERSION="%{version}-%{release}.%{_arch}" + +%install +%make_install DESTDIR=%{buildroot} prefix=%{_prefix} bindir=%{_bindir} mandir=%{_mandir} docdir=%{_docdir}/%{name} +%ifarch x86_64 +ln -sr %{buildroot}%{_mandir}/man1/passt.1 %{buildroot}%{_mandir}/man1/passt.avx2.1 +ln -sr %{buildroot}%{_mandir}/man1/pasta.1 %{buildroot}%{_mandir}/man1/pasta.avx2.1 +%endif + +pushd contrib/selinux +make -f %{_datadir}/selinux/devel/Makefile +install -p -m 644 -D passt.pp %{buildroot}%{_datadir}/selinux/packages/%{name}/passt.pp +install -p -m 644 -D passt.if %{buildroot}%{_datadir}/selinux/devel/include/contrib/passt.if +install -p -m 644 -D pasta.pp %{buildroot}%{_datadir}/selinux/packages/%{name}/pasta.pp +install -p -m 644 -D pasta.if %{buildroot}%{_datadir}/selinux/devel/include/contrib/pasta.if +popd + +%pre selinux +%selinux_relabel_pre -s %{selinuxtype} + +%post selinux +semodule -i %{_datadir}/selinux/packages/%{name}/passt.pp 2>/dev/null || : +semodule -i %{_datadir}/selinux/packages/%{name}/pasta.pp 2>/dev/null || : +%selinux_relabel_post -s %{selinuxtype} + +%preun selinux +semodule -r passt 2>/dev/null || : +semodule -r pasta 2>/dev/null || : +%selinux_relabel_post -s %{selinuxtype} + +%files +%license LICENSES/{AGPL-3.0-or-later.txt,BSD-3-Clause.txt} +%dir %{_docdir}/%{name} +%doc %{_docdir}/%{name}/README.md +%doc %{_docdir}/%{name}/demo.sh +%{_bindir}/passt +%{_bindir}/pasta +%{_bindir}/qrap +%{_mandir}/man1/passt.1* +%{_mandir}/man1/pasta.1* +%{_mandir}/man1/qrap.1* +%ifarch x86_64 +%{_bindir}/passt.avx2 +%{_mandir}/man1/passt.avx2.1* +%{_bindir}/pasta.avx2 +%{_mandir}/man1/pasta.avx2.1* +%endif + +%files selinux +%dir %{_datadir}/selinux/packages/%{name} +%{_datadir}/selinux/packages/%{name}/passt.pp +%{_datadir}/selinux/devel/include/contrib/passt.if +%{_datadir}/selinux/packages/%{name}/pasta.pp +%{_datadir}/selinux/devel/include/contrib/pasta.if + +%changelog +* Thu Mar 16 2023 Stefano Brivio - 0^20230222.g4ddbcb9-2 +- udp: Actually use host resolver to forward DNS queries (rhbz#2177075) +- conf: Split add_dns{4,6}() out of get_dns() (rhbz#2177075) +- conf, udp: Allow any loopback address to be used as resolver (rhbz#2177075) +- tcp, tcp_splice: Get rid of false positive CWE-394 Coverity warning from fls() (rhbz#2177084) +- tcp: Avoid false (but convoluted) positive Coverity CWE-476 warning (rhbz#2177084) +- tcp: Avoid (theoretical) resource leak (CWE-772) Coverity warning (rhbz#2177084) +- Fix definitions of SOCKET_MAX, TCP_MAX_CONNS (rhbz#2177084) +- doc/demo: Fix and suppress ShellCheck warnings (rhbz#2177084) +- contrib/selinux: Drop duplicate init_daemon_domain() rule (rhbz#2176813) +- contrib/selinux: Let passt write to stdout and stderr when it starts (rhbz#2176813) +- contrib/selinux: Allow binding and connecting to all UDP and TCP ports (rhbz#2176813) +- contrib/selinux: Let interface users set paths for log, PID, socket files (rhbz#2176813) +- contrib/selinux: Drop "example" from headers: this is the actual policy (rhbz#2176813) +- contrib/selinux: Drop unused passt_read_data() interface (rhbz#2176813) +- contrib/selinux: Split interfaces into smaller bits (rhbz#2176813) +- fedora: Install SELinux interface files to shared include directory (rhbz#2176813) +- tcp, udp, util: Pass socket creation errors all the way up (rhbz#2177080) +- tcp, udp: Fix partial success return codes in {tcp,udp}_sock_init() (rhbz#2177080) +- conf: Terminate on EMFILE or ENFILE on sockets for port mapping (rhbz#2177080) +- tcp: Clamp MSS value when queueing data to tap, also for pasta (rhbz#2177083) +- Fix up SELinux labels on install/uninstall, require matching -selinux package (rhbz#2176813) +- Resolves: rhbz#2177075 rhbz#2177084 rhbz#2177080 rhbz#2177083 rhbz#2176813 + +* Wed Feb 22 2023 Camilla Conte - 0^20230222.g4ddbcb9-1 +- Import from fedora to CentOS/RHEL +- Resolves: rhbz#2172244 + +* Wed Nov 16 2022 Miroslav Rezanina - 0^20221110.g4129764-1 +- Import from fedora to CentOS/RHEL +- Resolves: rhbz#2131015 + +* Thu Nov 10 2022 Stefano Brivio - 0^20221110.g4129764-1 +- Upstream changes: https://passt.top/passt/log/?qt=range&q=2022_11_04.e308018..2022_11_10.4129764 + +* Fri Nov 4 2022 Stefano Brivio - 0^20221104.ge308018-1 +- Upstream changes: https://passt.top/passt/log/?qt=range&q=2022_10_26.f212044..2022_11_04.e308018 + +* Wed Oct 26 2022 Stefano Brivio - 0^20221026.gf212044-1 +- Upstream changes: https://passt.top/passt/log/?qt=range&q=2022_10_26.e4df8b0..2022_10_26.f212044 + +* Wed Oct 26 2022 Stefano Brivio - 0^20221026.ge4df8b0-1 +- Upstream changes: https://passt.top/passt/log/?qt=range&q=2022_10_24.c11277b..2022_10_26.e4df8b0 + +* Mon Oct 24 2022 Stefano Brivio - 0^20221024.gc11277b-1 +- Upstream changes: https://passt.top/passt/log/?qt=range&q=2022_10_22.b68da10..2022_10_24.c11277b + +* Sat Oct 22 2022 Stefano Brivio - 0^20221022.gb68da10-1 +- Upstream changes: https://passt.top/passt/log/?qt=range&q=2022_10_15.b3f3591..2022_10_22.b68da10 + +* Sat Oct 15 2022 Stefano Brivio - 0^20221015.gb3f3591-1 +- Add versioning information +- Upstream changes: https://passt.top/passt/log/?qt=range&q=2022_09_29.06aa26f..2022_10_15.b3f3591 + +* Thu Sep 29 2022 Stefano Brivio - 0^20220929.g06aa26f-1 +- Upstream changes: https://passt.top/passt/log/?qt=range&q=2022_09_24.8978f65..2022_09_29.06aa26f + +* Sat Sep 24 2022 Stefano Brivio - 0^20220924.g8978f65-1 +- Upstream changes: https://passt.top/passt/log/?qt=range&q=2022_09_23.d6f865a..2022_09_24.8978f65 + +* Fri Sep 23 2022 Stefano Brivio - 0^20220923.gd6f865a-1 +- Upstream changes: https://passt.top/passt/log/?qt=range&q=2022_09_06.e2cae8f..2022_09_23.d6f865a + +* Wed Sep 7 2022 Stefano Brivio - 0^20220907.ge2cae8f-1 +- Escape %% characters in spec file's changelog +- Upstream changes: https://passt.top/passt/log/?qt=range&q=2022_09_01.7ce9fd1..2022_09_06.e2cae8f + +* Fri Sep 2 2022 Stefano Brivio - 0^20220902.g7ce9fd1-1 +- Add selinux-policy Requires: tag +- Add %%dir entries for own SELinux policy directory and documentation +- Upstream changes: https://passt.top/passt/log/?qt=range&q=2022_08_29.0cb795e..2022_09_01.7ce9fd1 + +* Tue Aug 30 2022 Stefano Brivio - 0^20220830.g0cb795e-1 +- Pass explicit bindir, mandir, docdir, and drop OpenSUSE override +- Use full versioning for SELinux subpackage Requires: tag +- Define git_hash in spec file and reuse it +- Drop comment stating the spec file is an example file +- Drop SPDX identifier from spec file +- Adopt versioning guideline for snapshots +- Upstream changes: https://passt.top/passt/log/?qt=range&q=2022_08_24.60ffc5b..2022_08_29.0cb795e + +* Wed Aug 24 2022 Stefano Brivio - 0^20220824.g60ffc5b-1 +- Upstream changes: https://passt.top/passt/log/?qt=range&q=2022_08_21.7b71094..2022_08_24.60ffc5b + +* Sun Aug 21 2022 Stefano Brivio - 0^20220821.g7b71094-1 +- Use more GNU-style directory variables, explicit docdir for OpenSUSE +- Upstream changes: https://passt.top/passt/log/?qt=range&q=2022_08_20.f233d6c..2022_08_21.7b71094 + +* Sat Aug 20 2022 Stefano Brivio - 0^20220820.gf233d6c-1 +- Fix man pages wildcards in spec file +- Don't hardcode CFLAGS setting, use %%set_build_flags macro instead +- Build SELinux subpackage as noarch +- Change source URL to HEAD link with explicit commit SHA +- Drop VCS tag from spec file +- Start Release tag from 1, not 0 +- Introduce own rpkg macro for changelog +- Install "plain" README, instead of web version, and demo script +- Upstream changes: https://passt.top/passt/log/?qt=range&q=2022_08_04.b516d15..2022_08_20.f233d6c + +* Mon Aug 1 2022 Stefano Brivio - 0^20220801.gb516d15-1 +- Upstream changes: https://passt.top/passt/log/?qt=range&q=2022_07_20.9af2e5d..2022_08_04.b516d15 + +* Wed Jul 20 2022 Stefano Brivio - 0^20220720.g9af2e5d-1 +- Upstream changes: https://passt.top/passt/log/?qt=range&q=2022_07_14.b86cd00..2022_07_20.9af2e5d + +* Thu Jul 14 2022 Stefano Brivio - 0^20220714.gb86cd00-1 +- Use pre-processing macros in spec file +- Drop dashes from version +- Add example spec file for Fedora +- Upstream changes: https://passt.top/passt/log/?qt=range&q=e653f9b3ed1b60037e3bc661d53b3f9407243fc2..2022_07_14.b86cd00