From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: passt.top; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=RaKVcQGv; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by passt.top (Postfix) with ESMTPS id E33B85A0776 for ; Mon, 24 Feb 2025 00:00:19 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1740351618; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=MmdJLCAlGG6E+NInsJ4kOxoZcPmnXP9pbVAwR4RoKVU=; b=RaKVcQGvwemYB9AL32qUt4LpRYKoCTXcY4lzNOhh0NI2Gr1thkVwDC9PzQRyug2NPkUdtO 2iJR2LAE2nEd+o83hiqWv2gCqiJeOxRcWggHcOwDU/ZDWRJc5VQt2pYmkpwXTy/kMQieEj U62SY6EHMJgp/DG4iv8PUTjr5ovAMZ8= Received: from mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-483-IiwWBJIeM_Wr1v0eukUgzA-1; Sun, 23 Feb 2025 18:00:17 -0500 X-MC-Unique: IiwWBJIeM_Wr1v0eukUgzA-1 X-Mimecast-MFC-AGG-ID: IiwWBJIeM_Wr1v0eukUgzA_1740351616 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 87A6619039CA for ; Sun, 23 Feb 2025 23:00:16 +0000 (UTC) Received: from jmaloy-thinkpadp16vgen1.rmtcaqc.csb (unknown [10.22.65.244]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 70B631800359; Sun, 23 Feb 2025 23:00:13 +0000 (UTC) From: Jon Maloy To: passt-dev@passt.top, sbrivio@redhat.com, lvivier@redhat.com, dgibson@redhat.com, jmaloy@redhat.com Subject: [PATCH v5 4/4] udp: create and send ICMPv6 to local peer when applicable Date: Sun, 23 Feb 2025 17:59:51 -0500 Message-ID: <20250223225951.3534010-5-jmaloy@redhat.com> In-Reply-To: <20250223225951.3534010-1-jmaloy@redhat.com> References: <20250223225951.3534010-1-jmaloy@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: juPRMopXip71t_S4DuZpZw8qrGWrYfxZUOjw80Jb2jg_1740351616 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit content-type: text/plain; charset="US-ASCII"; x-default=true Message-ID-Hash: MWCUISJW7PZ3VZKK757HZXQXMGOLXA6A X-Message-ID-Hash: MWCUISJW7PZ3VZKK757HZXQXMGOLXA6A X-MailFrom: jmaloy@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.8 Precedence: list List-Id: Development discussion and patches for passt Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: When a local peer sends a UDP message to a non-existing port on an existing remote host, that host will return an ICMP message containing the error code ICMP_PORT_UNREACH, plus the header and the first eight bytes of the original message. If the sender socket has been connected, it uses this message to issue a "Connection Refused" event to the user. Until now, we have only read such events from the externally facing socket, but we don't forward them back to the local sender because we cannot read the ICMP message directly to user space. Because of this, the local peer will hang and wait for a response that never arrives. We now fix this for IPv6 by recreating and forwarding a correct ICMP message back to the internal sender. We synthesize the message based on the information in the extended error structure, plus the returned part of the original message body. Note that for the sake of completeness, we even produce ICMP messages for other error codes. We have noticed that at least ICMP_PROT_UNREACH is propagated as an error event back to the user. Signed-off-by: Jon Maloy --- tap.c | 8 ++++---- tap.h | 4 ++++ udp.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/tap.c b/tap.c index 380a7b9..31aec62 100644 --- a/tap.c +++ b/tap.c @@ -247,10 +247,10 @@ void tap_icmp4_send(const struct ctx *c, struct in_addr src, struct in_addr dst, * * Return: pointer at which to write the packet's payload */ -static void *tap_push_ip6h(struct ipv6hdr *ip6h, - const struct in6_addr *src, - const struct in6_addr *dst, - size_t l4len, uint8_t proto, uint32_t flow) +void *tap_push_ip6h(struct ipv6hdr *ip6h, + const struct in6_addr *src, + const struct in6_addr *dst, + size_t l4len, uint8_t proto, uint32_t flow) { ip6h->payload_len = htons(l4len); ip6h->priority = 0; diff --git a/tap.h b/tap.h index b7b8cef..67aa9e6 100644 --- a/tap.h +++ b/tap.h @@ -53,6 +53,10 @@ void *tap_push_uh6(struct udphdr *uh, void *in, size_t dlen); void *tap_push_ip4h(struct iphdr *ip4h, struct in_addr src, struct in_addr dst, size_t l4len, uint8_t proto); +void *tap_push_ip6h(struct ipv6hdr *ip6h, + const struct in6_addr *src, + const struct in6_addr *dst, + size_t l4len, uint8_t proto, uint32_t flow); void tap_udp4_send(const struct ctx *c, struct in_addr src, in_port_t sport, struct in_addr dst, in_port_t dport, const void *in, size_t dlen); diff --git a/udp.c b/udp.c index 3dfd478..0e37fb3 100644 --- a/udp.c +++ b/udp.c @@ -88,6 +88,7 @@ #include #include #include +#include #include #include #include @@ -440,6 +441,46 @@ static void udp_send_conn_fail_icmp4(const struct ctx *c, tap_icmp4_send(c, oaddr, eaddr, &msg, msglen); } + +/** + * udp_send_conn_fail_icmp6() - Construct and send ICMP to local peer + * @c: Execution context + * @ee: Extended error descriptor + * @ref: epoll reference + * @in: First bytes (max 8) of original UDP message body + * @dlen: Length of the read part of original UDP message body + * @flow: IPv6 flow identifier + */ +static void udp_send_conn_fail_icmp6(const struct ctx *c, + const struct sock_extended_err *ee, + const struct flowside *toside, + void *in, size_t dlen, uint32_t flow) +{ + const struct in6_addr *oaddr = &toside->oaddr.a6; + const struct in6_addr *eaddr = &toside->eaddr.a6; + in_port_t eport = toside->eport; + in_port_t oport = toside->oport; + struct { + struct icmp6_hdr icmp6h; + struct ipv6hdr ip6h; + struct udphdr uh; + char data[8]; + } __attribute__((packed, aligned(__alignof__(max_align_t)))) msg; + size_t msglen = sizeof(msg) - sizeof(msg.data) + dlen; + size_t l4len = dlen + sizeof(struct udphdr); + + memset(&msg, 0, sizeof(msg)); + msg.icmp6h.icmp6_type = ee->ee_type; + msg.icmp6h.icmp6_code = ee->ee_code; + + /* Reconstruct the original headers as returned in the ICMP message */ + tap_push_ip6h(&msg.ip6h, eaddr, oaddr, l4len, IPPROTO_UDP, flow); + tap_push_uh6(&msg.uh, eaddr, eport, oaddr, oport, in, dlen); + memcpy(&msg.data, in, dlen); + + tap_icmp6_send(c, oaddr, eaddr, &msg, msglen); +} + /** * udp_sock_recverr() - Receive and clear an error from a socket * @c: Execution context @@ -498,8 +539,12 @@ static int udp_sock_recverr(const struct ctx *c, union epoll_ref ref) if (ref.type == EPOLL_TYPE_UDP_REPLY) { flow_sidx_t sidx = flow_sidx_opposite(ref.flowside); const struct flowside *toside = flowside_at_sidx(sidx); + uint32_t flow = sidx.flowi; - udp_send_conn_fail_icmp4(c, ee, toside, data, rc); + if (ee->ee_type == ICMP_DEST_UNREACH) + udp_send_conn_fail_icmp4(c, ee, toside, data, rc); + else if (ee->ee_type == ICMP6_DST_UNREACH) + udp_send_conn_fail_icmp6(c, ee, toside, data, rc, flow); } else { trace("Ignoring received cmsg on listener socket"); } -- 2.48.1