From patchwork Sat Feb 20 00:13:18 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Doug Evans X-Patchwork-Id: 12096435 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A1938C433DB for ; Sat, 20 Feb 2021 00:15:06 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 491F564EB4 for ; Sat, 20 Feb 2021 00:15:06 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 491F564EB4 Authentication-Results: mail.kernel.org; dmarc=pass (p=none dis=none) header.from=nongnu.org Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:58964 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lDFvN-0002OY-Ap for qemu-devel@archiver.kernel.org; Fri, 19 Feb 2021 19:15:05 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:37236) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <3KFQwYAMKCpk6C79HH9E7.5HFJ7FN-67O7EGHG9GN.HK9@flex--dje.bounces.google.com>) id 1lDFtt-0000W6-Vb for qemu-devel@nongnu.org; Fri, 19 Feb 2021 19:13:34 -0500 Received: from mail-pl1-x649.google.com ([2607:f8b0:4864:20::649]:46854) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3KFQwYAMKCpk6C79HH9E7.5HFJ7FN-67O7EGHG9GN.HK9@flex--dje.bounces.google.com>) id 1lDFtq-0005Px-L8 for qemu-devel@nongnu.org; Fri, 19 Feb 2021 19:13:33 -0500 Received: by mail-pl1-x649.google.com with SMTP id d11so3158898plh.13 for ; Fri, 19 Feb 2021 16:13:29 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=GNZYVmjsi1V1RFhcAfwUeZofo6RR1U4j3UTI/LwsE84=; b=VVz/gxx3dMhrtEi46FKgSxwcS6EK9UWDh4ub3kV5lg8x4WLykjAz6KHFnfaXhXP+Ym csnjIyxBH2E60on27V9lWoIvqLzUv8mw2uBNYEmGjndq3TyRkyF/wQ8KB+NTBhiUQ1k1 doL/nItMMAF7g/Qz+qLodB13cw/4fMTc0oZC/Sy0iujisU8oIdT3p9k9YF/mlAKmKg1U bYUEnBNiaQBR72iF3fHWF9Dvp4fID9jY2r10b5n4g5oxAJTiRA9OQywhbUOI8bM+vLbi z9Y5a2Csk5NdhCRV5qiz35livNtxPiytoZhQXeugD4ndQuRfaZF7lloHe4tY1BLStPJd Q8Nw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=GNZYVmjsi1V1RFhcAfwUeZofo6RR1U4j3UTI/LwsE84=; b=BBvT24Qgreg66nUEWY9Wp60/STpH76P8cSbrzD+6MbZr2qKWxz0hOyTX6cJVrjW6QX 55AQTvpisR9mdm2j8gjY+5x7yLRRBE7sdGIrxGcIKZJ0JMf8NbpdM1fATw/xcODsVrLm qXoaa+vYvJcCtfgVRqNbn2PiSp/6brE/g46oGW6xWKUcey1balxyFzIELSgsjG2Mslm3 YTn9VQeq8WZQ6QT7O84LaTW9i6zeuGX8RKZ7DpYf5j91/t8s1kmY1NkNKOjVu357oDDE ruQHeipsnd6u6R3hzcQ5yzfknFb+S6HNh8OIGi6lTrfCwr9zqc0RbMM71SvolsTtm3Xy TOBA== X-Gm-Message-State: AOAM532nfXVbIY2vNs6+vLs3jAgl09UwEpoSEk7jc+n8FBJjbqwtfgmW J/f97gg5PDlKOd4rKbfGEWjNabvh1Q2Fdu0Sx5k3JdABcp2/BZM3FqTKD7Nuc7PXTkpo7cG28Q8 uSN53zNjZD43mmjqRVqex4/he4O7YhgRFfXwXTcEa2y83Q/9+EuMV X-Google-Smtp-Source: ABdhPJywp6pm741wQLZ7h233qeSeZU/cmGPT+HEE0r4UDisF6Sjl5sZTFouFeKD7nIkzF74CEqG+W5U= X-Received: from ruffy.mtv.corp.google.com ([2620:0:1000:1412:7d06:9b98:ec95:3f70]) (user=dje job=sendgmr) by 2002:a17:90a:1992:: with SMTP id 18mr12009373pji.47.1613780008124; Fri, 19 Feb 2021 16:13:28 -0800 (PST) Date: Fri, 19 Feb 2021 16:13:18 -0800 In-Reply-To: <20210220001322.1311139-1-dje@google.com> Message-Id: <20210220001322.1311139-2-dje@google.com> Mime-Version: 1.0 References: <20210220001322.1311139-1-dje@google.com> X-Mailer: git-send-email 2.30.0.617.g56c4b15f3c-goog Subject: [PATCH v5 1/5] slirp: Advance libslirp submodule to add ipv6 host-forward support To: qemu-devel@nongnu.org Cc: Samuel Thibault , " =?utf-8?q?Daniel_P_?= =?utf-8?q?=2E_Berrang=C3=A9?= " , Doug Evans Received-SPF: pass client-ip=2607:f8b0:4864:20::649; envelope-from=3KFQwYAMKCpk6C79HH9E7.5HFJ7FN-67O7EGHG9GN.HK9@flex--dje.bounces.google.com; helo=mail-pl1-x649.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" Reply-to: Doug Evans X-Patchwork-Original-From: Doug Evans via From: Doug Evans Signed-off-by: Doug Evans --- Changes from v4: NOTE TO REVIEWERS: I need some hand-holding to know what The Right way to submit this particular patch is. - no change Changes from v3: - pick up latest libslirp patch to reject ipv6 addr-any for guest address - libslirp currently only provides a stateless DHCPv6 server, which means it can't know in advance what the guest's IP address is, and thus cannot do the "addr-any -> guest ip address" translation that is done for ipv4 Changes from v2: - this patch is new in v3, split out from v2 slirp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slirp b/slirp index 8f43a99191..26ae658a83 160000 --- a/slirp +++ b/slirp @@ -1 +1 @@ -Subproject commit 8f43a99191afb47ca3f3c6972f6306209f367ece +Subproject commit 26ae658a83eeca16780cf5615c8247cbb151c3fa From patchwork Sat Feb 20 00:13:19 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Doug Evans X-Patchwork-Id: 12096459 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5ABBAC433DB for ; Sat, 20 Feb 2021 00:22:52 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id E58E964EE0 for ; Sat, 20 Feb 2021 00:22:51 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org E58E964EE0 Authentication-Results: mail.kernel.org; dmarc=pass (p=none dis=none) header.from=nongnu.org Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:57094 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lDG2s-0004pU-S6 for qemu-devel@archiver.kernel.org; Fri, 19 Feb 2021 19:22:50 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:37238) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <3KlQwYAMKCps8E9BJJBG9.7JHL9HP-89Q9GIJIBIP.JMB@flex--dje.bounces.google.com>) id 1lDFtu-0000WJ-8q for qemu-devel@nongnu.org; Fri, 19 Feb 2021 19:13:34 -0500 Received: from mail-ua1-x949.google.com ([2607:f8b0:4864:20::949]:53014) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3KlQwYAMKCps8E9BJJBG9.7JHL9HP-89Q9GIJIBIP.JMB@flex--dje.bounces.google.com>) id 1lDFts-0005RD-A4 for qemu-devel@nongnu.org; Fri, 19 Feb 2021 19:13:34 -0500 Received: by mail-ua1-x949.google.com with SMTP id e15so3269758ual.19 for ; Fri, 19 Feb 2021 16:13:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=GcAFtjhbn9QPq1uf+qCU9C7hm0q4Mf37g1dQhxyj2vo=; b=En5Yv9sgOBuU73z1U6B9Em3AWKyTYTNIVxc0Fs6oZYK6kVs517LU8WBGDCawAewGED +N16s8KtHz3FLsoV+BIPuzGdH8TImI1XrGr0LWVVNB47bUzl/mmZrJFrF/V3/ZkEMU0W FzmLONNxtX6ZQqU5Q5LWx1oWqzDE8GM6lHngkkxwbpHxm0v3ya9ZQZu3KhBsBm+3RC6U 2njUp6tegpqoNc19CSs83B2bLv44Ka9GKaa0uRpXmL1Tl7AdUU7SWzuXr3AgdQBnb4td HlIpKgDpSlFf1fIKHDinMXQqqURU4AMVPfZoaOQn6N5jLUiEZK4YJfJTKBIHRNJJxs17 lhKQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=GcAFtjhbn9QPq1uf+qCU9C7hm0q4Mf37g1dQhxyj2vo=; b=aw+h6b1/k3Z5XRrRUvXAv3bYwzyKIJLvV+i1kySXjtVLvEjh5zisEbKdE1xrLDuUxE 0Y9SjcECO4bPE8L5gxRWwonv9L4874tmjIugpTWsKuU9mUb70qN8ImI278jHrnlipNcq L89GrzW7zt5e6R8XgoVUKgvuh83zQjOxpk7hn2vXN0hOZ6JFUx/Yflg8U65VN82VzbFW kvh5JXhLkp6Wz+rWAHQFtTWAIgVaomnxjis/WN6gPktACuGkzl4XuD5uRi+/eEVItEDS duSAeDH3g/ga6LLKv6h2zH+eJ9lHk1jG5B80L3mLYJB+FrsHfbeG5NhGxxKBKHRYWjji yuRg== X-Gm-Message-State: AOAM533+1wCWd253O+wZSyf8SCS339O8Jzk3qcaEHPMi/Utk/gJfF0s6 ea8iB9QF6kvNnW7Vop5CTodfb2ClhBmb3aNelTMd1e7NxhIOadn6uX48nooRH4dxKupf5nfAvCw CYxn2rQXa0g3RHm8qmUqlE3DPQph7MRdHOdxV8qBuICHNw/WHP9kd X-Google-Smtp-Source: ABdhPJwSNPJEN9ZAN2wQ25YqmoY6k5SWFJaRYo4H2CDMrRWLDHJ9JSaLSKsgTqig8ltIdiUV1GQwTdo= X-Received: from ruffy.mtv.corp.google.com ([2620:0:1000:1412:7d06:9b98:ec95:3f70]) (user=dje job=sendgmr) by 2002:a67:2b84:: with SMTP id r126mr8456242vsr.14.1613780010521; Fri, 19 Feb 2021 16:13:30 -0800 (PST) Date: Fri, 19 Feb 2021 16:13:19 -0800 In-Reply-To: <20210220001322.1311139-1-dje@google.com> Message-Id: <20210220001322.1311139-3-dje@google.com> Mime-Version: 1.0 References: <20210220001322.1311139-1-dje@google.com> X-Mailer: git-send-email 2.30.0.617.g56c4b15f3c-goog Subject: [PATCH v5 2/5] util/qemu-sockets.c: Split host:port parsing out of inet_parse To: qemu-devel@nongnu.org Cc: Samuel Thibault , " =?utf-8?q?Daniel_P_?= =?utf-8?q?=2E_Berrang=C3=A9?= " , Doug Evans Received-SPF: pass client-ip=2607:f8b0:4864:20::949; envelope-from=3KlQwYAMKCps8E9BJJBG9.7JHL9HP-89Q9GIJIBIP.JMB@flex--dje.bounces.google.com; helo=mail-ua1-x949.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" Reply-to: Doug Evans X-Patchwork-Original-From: Doug Evans via From: Doug Evans The parsing is moved into new function inet_parse_host_and_port. This is done in preparation for using the function in net/slirp.c. Signed-off-by: Doug Evans --- Changes from v4: - move recognition of "[]:port" to separate patch - allow passing NULL for ip_v6 - fix some formatting issues Changes from v3: - this patch is new in v4 - provides new utility: inet_parse_host_and_port, updates inet_parse to use it include/qemu/sockets.h | 3 ++ util/qemu-sockets.c | 80 +++++++++++++++++++++++++++++++----------- 2 files changed, 62 insertions(+), 21 deletions(-) diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h index 7d1f813576..b1448cfa24 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -31,6 +31,9 @@ int socket_set_fast_reuse(int fd); int inet_ai_family_from_address(InetSocketAddress *addr, Error **errp); +const char *inet_parse_host_and_port(const char *str, int terminator, + char **hostp, char **portp, bool *is_v6, + Error **errp); int inet_parse(InetSocketAddress *addr, const char *str, Error **errp); int inet_connect(const char *str, Error **errp); int inet_connect_saddr(InetSocketAddress *saddr, Error **errp); diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 8af0278f15..3ca6a6fb3d 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -615,44 +615,82 @@ static int inet_parse_flag(const char *flagname, const char *optstr, bool *val, return 0; } -int inet_parse(InetSocketAddress *addr, const char *str, Error **errp) +/* + * Parse an inet host and port as "host:port". + * Terminator may be '\0'. + * The syntax for IPv4 addresses is: address:port. "address" is optional, + * and may be empty (i.e., str is ":port"). + * The syntax for IPv6 addresses is: [address]:port. Upon return the wrapping + * [] brackets are removed. + * Host names are also supported as hostname:port. It is up to the caller to + * distinguish host names from numeric IPv4 addresses. + * On success, returns a pointer to the terminator. Space for the address and + * port is malloced and stored in *host, *port, the caller must free. + * If is_v6 is non-NULL, then it is set to true if the address is an IPv6 + * address (i.e., [address]), otherwise it is set to false. + * On failure NULL is returned with the error stored in *errp. + */ +const char *inet_parse_host_and_port(const char *str, int terminator, + char **hostp, char **portp, bool *is_v6, + Error **errp) { - const char *optstr, *h; + const char *terminator_ptr = strchr(str, terminator); + g_autofree char *buf = NULL; char host[65]; char port[33]; - int to; - int pos; - char *begin; - memset(addr, 0, sizeof(*addr)); + if (terminator_ptr == NULL) { + /* If the terminator isn't found then use the entire string. */ + terminator_ptr = str + strlen(str); + } + buf = g_strndup(str, terminator_ptr - str); - /* parse address */ - if (str[0] == ':') { + if (buf[0] == ':') { /* no host given */ host[0] = '\0'; - if (sscanf(str, ":%32[^,]%n", port, &pos) != 1) { - error_setg(errp, "error parsing port in address '%s'", str); - return -1; + if (sscanf(buf, ":%32s", port) != 1) { + error_setg(errp, "error parsing port in address '%s'", buf); + return NULL; } - } else if (str[0] == '[') { + } else if (buf[0] == '[') { /* IPv6 addr */ - if (sscanf(str, "[%64[^]]]:%32[^,]%n", host, port, &pos) != 2) { - error_setg(errp, "error parsing IPv6 address '%s'", str); - return -1; + if (sscanf(buf, "[%64[^]]]:%32s", host, port) != 2) { + error_setg(errp, "error parsing IPv6 address '%s'", buf); + return NULL; } } else { /* hostname or IPv4 addr */ - if (sscanf(str, "%64[^:]:%32[^,]%n", host, port, &pos) != 2) { - error_setg(errp, "error parsing address '%s'", str); - return -1; + if (sscanf(buf, "%64[^:]:%32s", host, port) != 2) { + error_setg(errp, "error parsing address '%s'", buf); + return NULL; } } - addr->host = g_strdup(host); - addr->port = g_strdup(port); + *hostp = g_strdup(host); + *portp = g_strdup(port); + if (is_v6 != NULL) { + *is_v6 = buf[0] == '['; + } + + return terminator_ptr; +} + +int inet_parse(InetSocketAddress *addr, const char *str, Error **errp) +{ + const char *optstr, *h; + int to; + int pos; + char *begin; + + memset(addr, 0, sizeof(*addr)); + + optstr = inet_parse_host_and_port(str, ',', &addr->host, &addr->port, + NULL, errp); + if (optstr == NULL) { + return -1; + } /* parse options */ - optstr = str + pos; h = strstr(optstr, ",to="); if (h) { h += 4; From patchwork Sat Feb 20 00:13:20 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Doug Evans X-Patchwork-Id: 12096447 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2103AC433E0 for ; Sat, 20 Feb 2021 00:19:47 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 6687C64EE0 for ; Sat, 20 Feb 2021 00:19:46 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 6687C64EE0 Authentication-Results: mail.kernel.org; dmarc=pass (p=none dis=none) header.from=nongnu.org Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:46384 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lDFzr-0000Oh-Jg for qemu-devel@archiver.kernel.org; Fri, 19 Feb 2021 19:19:43 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:37290) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <3LFQwYAMKCp0AGBDLLDIB.9LJNBJR-ABSBIKLKDKR.LOD@flex--dje.bounces.google.com>) id 1lDFtx-0000Xa-6x for qemu-devel@nongnu.org; Fri, 19 Feb 2021 19:13:37 -0500 Received: from mail-pl1-x64a.google.com ([2607:f8b0:4864:20::64a]:43090) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3LFQwYAMKCp0AGBDLLDIB.9LJNBJR-ABSBIKLKDKR.LOD@flex--dje.bounces.google.com>) id 1lDFtu-0005Rj-TO for qemu-devel@nongnu.org; Fri, 19 Feb 2021 19:13:36 -0500 Received: by mail-pl1-x64a.google.com with SMTP id 42so4625740plb.10 for ; Fri, 19 Feb 2021 16:13:33 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=iWai+KPSZ1OZ7yfodHOcvAbIn7VnVw8PFEon7KOtbLs=; b=hL7R97jHeq1eOo3yWX4KZfVb86gmwbDYYGXpGb/2kkLd7X7f/cNSuJYSLsZ4T4QANu c4VC4VCZXwq4tSkXQMJl6p+TjSuiy4yLDUURrUMEpzpKnd2VT0uySI3KuC5HWQ2IxbsP 1fmukBbQ/Y0mKU1Qaiwpd0iKB+pv8GW1zDTXQWTFBDH2KIdnB6WORKcSVUclL62ou1hI K7G9GfFypKxfclA0YjaU8dZyeW6+v+U/EuzOXVO17sSaMKnbhiscXMWwXFvgtbeWlagR 4CHnbbk2j3HPbXgBSQBCIXe8+WAthz0FOepCS3qne9fEJCyImxWXG+1P2z9q9N/oERO7 RWBw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=iWai+KPSZ1OZ7yfodHOcvAbIn7VnVw8PFEon7KOtbLs=; b=ZZI0z4yckkMSrvvcrfRXaRMtKU5T76QKbR8Zn71rG0nHV1g25kc/snQVMjoxElpI63 H0BHNxKF38FuLifQkyzMP93qNUAYxc9HoaKghrxKkTf/SXTIl5eA20Z0mvGLSMwdFpAW dBhSX6FdpDie1Lc7NSD4/hs5a2reKYxxKeSnxCz2FWcloKtoNZy2xVlcgfr9uO8uagrT kv2fm9tUnblv7Dpe8GoRJdlBvP7H5B6Fthb4gOeNkE7EBfqJBdKrvYeEGWwruyy4hsnA 8mFj7SF7Y5KPBB/kGk80FbhndFwGdhgexmJs20j/MS+beDW04jx+2UIdnw23vZRqKzu9 VM9w== X-Gm-Message-State: AOAM532xYiDMfNtMdxL3WGXy7+v7y2BcRl2qjHEkQLvRFuXdtYHkrDKd 57UJe2Dpka60V+Lkf/+82iDhnUmW/I/f828/YqNgXmhkBWFSPhxrenmk5KLeTB/9x8SDerTKoW7 wNKgWpcBvnIrjY612ZONCj4RhzChJXS+4CKHocp+lS9JreqTRlAVN X-Google-Smtp-Source: ABdhPJzNUX1J6EwqZLJlLvoLD0Y+Po9MNIzCynsPJ58AUp7RPz0Gd9FUZ/w2vYPHMX9qgw3uqYMRvas= X-Received: from ruffy.mtv.corp.google.com ([2620:0:1000:1412:7d06:9b98:ec95:3f70]) (user=dje job=sendgmr) by 2002:a17:90a:4dc1:: with SMTP id r1mr11668818pjl.12.1613780012934; Fri, 19 Feb 2021 16:13:32 -0800 (PST) Date: Fri, 19 Feb 2021 16:13:20 -0800 In-Reply-To: <20210220001322.1311139-1-dje@google.com> Message-Id: <20210220001322.1311139-4-dje@google.com> Mime-Version: 1.0 References: <20210220001322.1311139-1-dje@google.com> X-Mailer: git-send-email 2.30.0.617.g56c4b15f3c-goog Subject: [PATCH v5 3/5] inet_parse_host_and_addr: Recognize []:port (empty ipv6 address) To: qemu-devel@nongnu.org Cc: Samuel Thibault , " =?utf-8?q?Daniel_P_?= =?utf-8?q?=2E_Berrang=C3=A9?= " , Doug Evans Received-SPF: pass client-ip=2607:f8b0:4864:20::64a; envelope-from=3LFQwYAMKCp0AGBDLLDIB.9LJNBJR-ABSBIKLKDKR.LOD@flex--dje.bounces.google.com; helo=mail-pl1-x64a.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" Reply-to: Doug Evans X-Patchwork-Original-From: Doug Evans via From: Doug Evans Some callers need to distinguish empty ipv4 addresses from ipv6. Signed-off-by: Doug Evans --- Changes from v4: - new in this patchset revision util/qemu-sockets.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 3ca6a6fb3d..062f0eb074 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -620,7 +620,8 @@ static int inet_parse_flag(const char *flagname, const char *optstr, bool *val, * Terminator may be '\0'. * The syntax for IPv4 addresses is: address:port. "address" is optional, * and may be empty (i.e., str is ":port"). - * The syntax for IPv6 addresses is: [address]:port. Upon return the wrapping + * The syntax for IPv6 addresses is: [address]:port. "address" is optional, + * and may be empty (i.e., str is "[]:port"). Upon return the wrapping * [] brackets are removed. * Host names are also supported as hostname:port. It is up to the caller to * distinguish host names from numeric IPv4 addresses. @@ -654,7 +655,10 @@ const char *inet_parse_host_and_port(const char *str, int terminator, } } else if (buf[0] == '[') { /* IPv6 addr */ - if (sscanf(buf, "[%64[^]]]:%32s", host, port) != 2) { + /* Note: sscanf %[ doesn't recognize empty contents. */ + if (sscanf(buf, "[]:%32s", port) == 1) { + host[0] = '\0'; + } else if (sscanf(buf, "[%64[^]]]:%32s", host, port) != 2) { error_setg(errp, "error parsing IPv6 address '%s'", buf); return NULL; } From patchwork Sat Feb 20 00:13:21 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Doug Evans X-Patchwork-Id: 12096451 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 75D92C433E6 for ; Sat, 20 Feb 2021 00:20:22 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id F07EB64E76 for ; Sat, 20 Feb 2021 00:20:21 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org F07EB64E76 Authentication-Results: mail.kernel.org; dmarc=pass (p=none dis=none) header.from=nongnu.org Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:47902 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lDG0T-000126-0X for qemu-devel@archiver.kernel.org; Fri, 19 Feb 2021 19:20:21 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:37314) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <3L1QwYAMKCqADJEGOOGLE.COMQEMU-DEVELNONGNU.ORG@flex--dje.bounces.google.com>) id 1lDFu0-0000aL-JY for qemu-devel@nongnu.org; Fri, 19 Feb 2021 19:13:40 -0500 Received: from mail-pj1-x1049.google.com ([2607:f8b0:4864:20::1049]:59721) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3L1QwYAMKCqADJEGOOGLE.COMQEMU-DEVELNONGNU.ORG@flex--dje.bounces.google.com>) id 1lDFtx-0005Sk-8e for qemu-devel@nongnu.org; Fri, 19 Feb 2021 19:13:40 -0500 Received: by mail-pj1-x1049.google.com with SMTP id lk3so4151038pjb.9 for ; Fri, 19 Feb 2021 16:13:36 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=xrRSLLdswdaKTo/rEGM81W74DTcTo6DMcyuJBJoBECg=; b=pgKa+80dBf6va5xgHqRLOSi9VofbCh2mGRGxlRzF116h+bUCZTY5X66388fBcSYu/J l6G4TAKwNLb/CBZbqq3yQ2EiWDjdhUuxQRPHetifOo53+ZCUgbjefbBTVIoBrjP3ZbKX cEcpcWsPC14vMEz9iNfdERRj6kxvY6HBEpJunRXubAAKJyfB3vcUlf7ve4Dseq7AJSwh cc6Q0XjLmNxeKz4RMaLR7/eFZ1O3vwZqUt3xb0pHH4H/iQl5/yNx0eJ6LUFoDDagsXwd lefWmmisLF3BG+R+mptwOq8eUBQ64FWUh8qFj6nQHwvGT80BeNKCvjINdKHw5LMS6TSV n2MQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=xrRSLLdswdaKTo/rEGM81W74DTcTo6DMcyuJBJoBECg=; b=q7bUzVtdK3ZrUlNETz55tPqwGI/YA7WD/kMJPWDk4uYfQLTd873+GpBq9sTQmk2NXO EISBbKJ3kYop0A1UIXQJwslnIURiHqZ9s0PiTHPbvUFpOAxEaSOJpOYlNDx6up7N9JFi VUnqARRwgB/yEKvf4siXSkG5dwP3yHQOhXenOJRmHzjahVSLlT1uNEw0Pr0I1G2T6G4w tbkYNXT5B5crZ/bUTppomzwFwmqQFbO0bj0+gj1ditmYR1CrUvgQLyWftMHTupl0GJP9 leGPkanyWiA0HVmaMt47LtIr1Ltf9BvsJQ5AsVwfGbwTiQhzFwCdTNC4MJo1N06AcD6w yiuA== X-Gm-Message-State: AOAM532fJr43DKQTNbeGcqtC2fh3qpD5VC5IgUhg3D5gYgTpH8Kz7AR9 b3AovPmwesAzB+ocAxb5kK/bKnVaD+1OYXc8UJn4a2Cg8pLkM5l6i4VvpCWQYT1STjDJ+ex9OgX kS5wgK2QvG76lxN7HlZV3uNbGPfnIf+vYPpjXL7AlJ5TRpAHbMEf+ X-Google-Smtp-Source: ABdhPJyboi86awktnItL5dfH/BP12Ue5Mw0F2K5dYoab2O6tKkuJUQ8ULHCYTkm9sPqkhTMiIvJRc1g= X-Received: from ruffy.mtv.corp.google.com ([2620:0:1000:1412:7d06:9b98:ec95:3f70]) (user=dje job=sendgmr) by 2002:a17:90a:64cc:: with SMTP id i12mr5995067pjm.41.1613780015399; Fri, 19 Feb 2021 16:13:35 -0800 (PST) Date: Fri, 19 Feb 2021 16:13:21 -0800 In-Reply-To: <20210220001322.1311139-1-dje@google.com> Message-Id: <20210220001322.1311139-5-dje@google.com> Mime-Version: 1.0 References: <20210220001322.1311139-1-dje@google.com> X-Mailer: git-send-email 2.30.0.617.g56c4b15f3c-goog Subject: [PATCH v5 4/5] net/slirp.c: Refactor address parsing To: qemu-devel@nongnu.org Cc: Samuel Thibault , " =?utf-8?q?Daniel_P_?= =?utf-8?q?=2E_Berrang=C3=A9?= " , Doug Evans Received-SPF: pass client-ip=2607:f8b0:4864:20::1049; envelope-from=3L1QwYAMKCqADJEGOOGLE.COMQEMU-DEVELNONGNU.ORG@flex--dje.bounces.google.com; helo=mail-pj1-x1049.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" Reply-to: Doug Evans X-Patchwork-Original-From: Doug Evans via From: Doug Evans ... in preparation for adding ipv6 host forwarding support. New test: avocado run tests/acceptance/hostfwd.py Signed-off-by: Doug Evans --- Changes from v4: - was 3/4 in v4 - fix some formatting issues Changes from v3: - this patch renamed from 2/3 to 3/4 - call inet_parse_host_and_port from util/qemu-sockets.c - added tests/acceptance/hostfwd.py Changes from v2: - nothing of consequence Changes from v1: - this patch is new in v2 - address parsing refactor split out, ipv4 changes here - libslirp part is now upstream in libslirp repo net/slirp.c | 165 ++++++++++++++++++++++-------------- tests/acceptance/hostfwd.py | 94 ++++++++++++++++++++ 2 files changed, 196 insertions(+), 63 deletions(-) create mode 100644 tests/acceptance/hostfwd.py diff --git a/net/slirp.c b/net/slirp.c index be914c0be0..e0284492b9 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -631,15 +631,91 @@ static SlirpState *slirp_lookup(Monitor *mon, const char *id) } } +/* + * Parse a protocol name of the form "name". + * Valid protocols are "tcp" and "udp". An empty string means "tcp". + * Returns a pointer to the end of the parsed string on success, and stores + * the result in *is_udp. + * Otherwise returns NULL and stores the error in *errp. + */ +static const char *parse_protocol(const char *str, int sep, bool *is_udp, + Error **errp) +{ + char buf[10]; + const char *p = str; + + if (get_str_sep(buf, sizeof(buf), &p, sep) < 0) { + error_setg(errp, "Missing protocol name separator"); + return NULL; + } + + if (!strcmp(buf, "tcp") || buf[0] == '\0') { + *is_udp = false; + } else if (!strcmp(buf, "udp")) { + *is_udp = true; + } else { + error_setg(errp, "Bad protocol name"); + return NULL; + } + + return p; +} + +/* + * Parse an ip address/port of the form "address:port". + * An empty address means INADDR_ANY. + * Returns a pointer to after the terminator, unless it was '\0' in which case + * the result points to the '\0'. + * The parsed results are stored in *addr, *port. + * On error NULL is returned and stores the error in *errp. + */ +static const char *parse_ip_addr_and_port(const char *str, int terminator, + struct in_addr *addr, int *port, + Error **errp) +{ + g_autofree char *addr_str = NULL; + g_autofree char *port_str = NULL; + bool is_v6; + const char *p = inet_parse_host_and_port(str, terminator, &addr_str, + &port_str, &is_v6, errp); + + if (p == NULL) { + return NULL; + } + + /* Ignore is_v6 for the moment, if inet_aton fails let it. */ + if (addr_str[0] == '\0') { + addr->s_addr = INADDR_ANY; + } else if (!inet_aton(addr_str, addr)) { + error_setg(errp, "Bad address"); + return NULL; + } + + if (qemu_strtoi(port_str, NULL, 10, port) < 0 || + *port < 0 || *port > 65535) { + error_setg(errp, "Bad port"); + return NULL; + } + + /* + * At this point "p" points to the terminator or trailing NUL if the + * terminator is not present. + */ + if (*p) { + ++p; + } + return p; +} + void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict) { - struct in_addr host_addr = { .s_addr = INADDR_ANY }; + struct in_addr host_addr; int host_port; - char buf[256]; const char *src_str, *p; SlirpState *s; - int is_udp = 0; + bool is_udp; int err; + Error *error = NULL; const char *arg1 = qdict_get_str(qdict, "arg1"); const char *arg2 = qdict_get_try_str(qdict, "arg2"); @@ -654,30 +730,18 @@ void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict) return; } + g_assert(src_str != NULL); p = src_str; - if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) { - goto fail_syntax; - } - - if (!strcmp(buf, "tcp") || buf[0] == '\0') { - is_udp = 0; - } else if (!strcmp(buf, "udp")) { - is_udp = 1; - } else { - goto fail_syntax; - } - if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { - goto fail_syntax; - } - if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) { + p = parse_protocol(p, ':', &is_udp, &error); + if (p == NULL) { goto fail_syntax; } - if (qemu_strtoi(p, NULL, 10, &host_port)) { + if (parse_ip_addr_and_port(p, '\0', &host_addr, &host_port, + &error) == NULL) { goto fail_syntax; } - err = slirp_remove_hostfwd(s->slirp, is_udp, host_addr, host_port); monitor_printf(mon, "host forwarding rule for %s %s\n", src_str, @@ -685,65 +749,39 @@ void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict) return; fail_syntax: - monitor_printf(mon, "invalid format\n"); + monitor_printf(mon, "Invalid format: %s\n", error_get_pretty(error)); + error_free(error); } static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp) { - struct in_addr host_addr = { .s_addr = INADDR_ANY }; - struct in_addr guest_addr = { .s_addr = 0 }; + struct in_addr host_addr, guest_addr; int host_port, guest_port; const char *p; - char buf[256]; - int is_udp; - char *end; - const char *fail_reason = "Unknown reason"; + bool is_udp; + Error *error = NULL; + g_assert(redir_str != NULL); p = redir_str; - if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) { - fail_reason = "No : separators"; - goto fail_syntax; - } - if (!strcmp(buf, "tcp") || buf[0] == '\0') { - is_udp = 0; - } else if (!strcmp(buf, "udp")) { - is_udp = 1; - } else { - fail_reason = "Bad protocol name"; - goto fail_syntax; - } - if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { - fail_reason = "Missing : separator"; - goto fail_syntax; - } - if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) { - fail_reason = "Bad host address"; + p = parse_protocol(p, ':', &is_udp, &error); + if (p == NULL) { goto fail_syntax; } - if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) { - fail_reason = "Bad host port separator"; - goto fail_syntax; - } - host_port = strtol(buf, &end, 0); - if (*end != '\0' || host_port < 0 || host_port > 65535) { - fail_reason = "Bad host port"; + p = parse_ip_addr_and_port(p, '-', &host_addr, &host_port, &error); + if (p == NULL) { + error_prepend(&error, "For host address: "); goto fail_syntax; } - if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { - fail_reason = "Missing guest address"; + if (parse_ip_addr_and_port(p, '\0', &guest_addr, &guest_port, + &error) == NULL) { + error_prepend(&error, "For guest address: "); goto fail_syntax; } - if (buf[0] != '\0' && !inet_aton(buf, &guest_addr)) { - fail_reason = "Bad guest address"; - goto fail_syntax; - } - - guest_port = strtol(p, &end, 0); - if (*end != '\0' || guest_port < 1 || guest_port > 65535) { - fail_reason = "Bad guest port"; + if (guest_port == 0) { + error_setg(&error, "For guest address: Bad port"); goto fail_syntax; } @@ -757,7 +795,8 @@ static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp) fail_syntax: error_setg(errp, "Invalid host forwarding rule '%s' (%s)", redir_str, - fail_reason); + error_get_pretty(error)); + error_free(error); return -1; } diff --git a/tests/acceptance/hostfwd.py b/tests/acceptance/hostfwd.py new file mode 100644 index 0000000000..e5602a7708 --- /dev/null +++ b/tests/acceptance/hostfwd.py @@ -0,0 +1,94 @@ +# Hostfwd command tests +# +# Copyright 2021 Google LLC +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. + + +from avocado_qemu import Test + + +class Hostfwd(Test): + """ + :avocado: tags=hostfwd + """ + def hmc(self, cmd): + return self.vm.command('human-monitor-command', command_line=cmd) + + def test_qmp_hostfwd_ipv4(self): + self.vm.add_args('-nodefaults', + '-netdev', 'user,id=vnet', + '-device', 'virtio-net,netdev=vnet') + self.vm.launch() + self.assertEquals(self.hmc('hostfwd_add vnet tcp::65022-:22'), '') + self.assertEquals(self.hmc('hostfwd_remove vnet tcp::65022'), + 'host forwarding rule for tcp::65022 removed\r\n') + self.assertEquals(self.hmc('hostfwd_add tcp::65022-:22'), '') + self.assertEquals(self.hmc('hostfwd_remove tcp::65022'), + 'host forwarding rule for tcp::65022 removed\r\n') + self.assertEquals(self.hmc('hostfwd_add udp::65042-:42'), '') + self.assertEquals(self.hmc('hostfwd_remove udp::65042'), + 'host forwarding rule for udp::65042 removed\r\n') + + def test_qmp_hostfwd_ipv4_functional_errors(self): + """Verify handling of various kinds of errors given valid addresses.""" + self.vm.add_args('-nodefaults', + '-netdev', 'user,id=vnet', + '-device', 'virtio-net,netdev=vnet') + self.vm.launch() + self.assertEquals(self.hmc('hostfwd_remove ::65022'), + 'host forwarding rule for ::65022 not found\r\n') + self.assertEquals(self.hmc('hostfwd_add udp::65042-:42'), '') + self.assertEquals(self.hmc('hostfwd_add udp::65042-:42'), + "Could not set up host forwarding rule " + "'udp::65042-:42'\r\n") + self.assertEquals(self.hmc('hostfwd_remove ::65042'), + 'host forwarding rule for ::65042 not found\r\n') + self.assertEquals(self.hmc('hostfwd_remove udp::65042'), + 'host forwarding rule for udp::65042 removed\r\n') + self.assertEquals(self.hmc('hostfwd_remove udp::65042'), + 'host forwarding rule for udp::65042 not found\r\n') + + def test_qmp_hostfwd_ipv4_parsing_errors(self): + """Verify handling of various kinds of parsing errors.""" + self.vm.add_args('-nodefaults', + '-netdev', 'user,id=vnet', + '-device', 'virtio-net,netdev=vnet') + self.vm.launch() + self.assertEquals(self.hmc('hostfwd_remove abc::42'), + 'Invalid format: Bad protocol name\r\n') + self.assertEquals(self.hmc('hostfwd_add abc::65022-:22'), + "Invalid host forwarding rule 'abc::65022-:22' " + "(Bad protocol name)\r\n") + self.assertEquals(self.hmc('hostfwd_add :a.b.c.d:66-:66'), + "Invalid host forwarding rule ':a.b.c.d:66-:66' " + "(For host address: Bad address)\r\n") + self.assertEquals(self.hmc('hostfwd_add ::66-a.b.c.d:66'), + "Invalid host forwarding rule '::66-a.b.c.d:66' " + "(For guest address: Bad address)\r\n") + self.assertEquals(self.hmc('hostfwd_add ::66666-:66666'), + "Invalid host forwarding rule '::66666-:66666' " + "(For host address: Bad port)\r\n") + self.assertEquals(self.hmc('hostfwd_add ::-1-foo'), + "Invalid host forwarding rule '::-1-foo' (For " + "host address: error parsing port in address ':')\r\n") + self.assertEquals(self.hmc('hostfwd_add ::66-foo'), + "Invalid host forwarding rule '::66-foo' (For " + "guest address: error parsing address 'foo')\r\n") + self.assertEquals(self.hmc('hostfwd_add ::66-:66666'), + "Invalid host forwarding rule '::66-:66666' " + "(For guest address: Bad port)\r\n") + self.assertEquals(self.hmc('hostfwd_add ::66-:-1'), + "Invalid host forwarding rule '::66-:-1' " + "(For guest address: Bad port)\r\n") + self.assertEquals(self.hmc('hostfwd_add ::66-:0'), + "Invalid host forwarding rule '::66-:0' " + "(For guest address: Bad port)\r\n") From patchwork Sat Feb 20 00:13:22 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Doug Evans X-Patchwork-Id: 12096445 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D99D8C433E0 for ; Sat, 20 Feb 2021 00:17:58 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 7D96064ECA for ; Sat, 20 Feb 2021 00:17:58 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 7D96064ECA Authentication-Results: mail.kernel.org; dmarc=pass (p=none dis=none) header.from=nongnu.org Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:40502 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lDFy9-0006TI-JZ for qemu-devel@archiver.kernel.org; Fri, 19 Feb 2021 19:17:57 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:37340) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <3MVQwYAMKCqIFLGIQQING.EQOSGOW-FGXGNPQPIPW.QTI@flex--dje.bounces.google.com>) id 1lDFu2-0000gX-VQ for qemu-devel@nongnu.org; Fri, 19 Feb 2021 19:13:42 -0500 Received: from mail-pj1-x104a.google.com ([2607:f8b0:4864:20::104a]:50821) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3MVQwYAMKCqIFLGIQQING.EQOSGOW-FGXGNPQPIPW.QTI@flex--dje.bounces.google.com>) id 1lDFu0-0005Tl-9o for qemu-devel@nongnu.org; Fri, 19 Feb 2021 19:13:42 -0500 Received: by mail-pj1-x104a.google.com with SMTP id me5so4674336pjb.0 for ; Fri, 19 Feb 2021 16:13:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=FQeIi349Douf5mLeGHfD41Z7YRFz4ase8V++WPVO8f4=; b=iOyJu+h66Ijue6r4c91KQfu4pS8vNYX7EVWGTPYVbXFrXMaNHJhDrQpgG5dnhKHAJ0 1oNxzHyj+KT4mnb03LEjSDQTFO6C9Bf4NLh3mLS2T26Ejp3Eh/FCsBR3Jdg/0BF7R9ee xM/nxHeLKh7qiiBX6i0655o/i3FPOrsQ+MFjx2Q4hDPcMDEIOgJgypLwbc4estCCXOah DoTtJLny8Vt+Wgly+hJ/j5T3774e+BXB2thPpUbpvPPvLpZ0zofjJc4QbvPUpwczRn8t PybSBd+Njash0r+8CbdwQaPM843rXj9O1ngYVzF6cIlD2gjDyDzNcf1C3m/GEaUyP452 DqYA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=FQeIi349Douf5mLeGHfD41Z7YRFz4ase8V++WPVO8f4=; b=Al9xllo/b+rQUikLhG6izdWpLWA1BGfkvWzFQIw5nM+zGnShOS/s2Sb6I8E9qrB0SG YbM9/isgEI5Qbd9uyCKKRa6zctpZqZ/NEVV9QyP5mI6z8jXq0+1kaxIVLX/j8DkomEtw Jrjf4RVj7yP6hxMdKy8Zb72BWREupTkNY80HG3UsnByH+NsrsI8WVRknTwZos6COEl6M 61NCbGa5vV0M6KVg/ifSAYTNt6tIhLTPo0Hw5zUQ45X6tHusp08WkG0I2x0OzmMwrAhs iWJzCYt9oOUbcifnrxT8EVXtgCZAoAE1lWg7C7KlC5CgEGL4XHOXRmD1VRccFS9Ob55z T08A== X-Gm-Message-State: AOAM530eavbUWTPxZ3wBojL/24A0tZ3B2JwsUAw3Hyc0+opd708zW/eg Ol7+9NKDWXjPLWeXUAo6lAgfl09j0AK+r1YmjxxOF+yfu9URxnXJvvEdyi3awdZPAlpL6tdk8s2 1e6pR+z9nH3BxrsdE8/a1P/u/veaDNGmyGYdkd4KKrlfla+ses3tW X-Google-Smtp-Source: ABdhPJwdZU8sQN8ijI5Q/+hID+YJH6IeFjAggQph0JenM7GjIHtyuFP/U6alB6dvJWduof3UEkZSoo0= X-Received: from ruffy.mtv.corp.google.com ([2620:0:1000:1412:7d06:9b98:ec95:3f70]) (user=dje job=sendgmr) by 2002:a17:90b:4ac8:: with SMTP id mh8mr12006466pjb.38.1613780017796; Fri, 19 Feb 2021 16:13:37 -0800 (PST) Date: Fri, 19 Feb 2021 16:13:22 -0800 In-Reply-To: <20210220001322.1311139-1-dje@google.com> Message-Id: <20210220001322.1311139-6-dje@google.com> Mime-Version: 1.0 References: <20210220001322.1311139-1-dje@google.com> X-Mailer: git-send-email 2.30.0.617.g56c4b15f3c-goog Subject: [PATCH v5 5/5] net: Extend host forwarding to support IPv6 To: qemu-devel@nongnu.org Cc: Samuel Thibault , " =?utf-8?q?Daniel_P_?= =?utf-8?q?=2E_Berrang=C3=A9?= " , Doug Evans Received-SPF: pass client-ip=2607:f8b0:4864:20::104a; envelope-from=3MVQwYAMKCqIFLGIQQING.EQOSGOW-FGXGNPQPIPW.QTI@flex--dje.bounces.google.com; helo=mail-pj1-x104a.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" Reply-to: Doug Evans X-Patchwork-Original-From: Doug Evans via From: Doug Evans Net option "-hostfwd" now supports IPv6 addresses. Commands hostfwd_add, hostfwd_remove now support IPv6 addresses. Signed-off-by: Doug Evans --- Changes from v4: - was 4/4 in v4 - fix some formatting issues Differences from v3: - this patch renamed from 3/3 to 4/4 - ipv6 support added to existing hostfwd option, commands - instead of creating new ipv6 option, commands - added tests to tests/acceptance/hostfwd.py Differences from v2: - clarify spelling of ipv6 addresses in docs - tighten parsing of ipv6 addresses Differences from v1: - parsing refactor split out into separate patch (2/3) hmp-commands.hx | 15 +++++++ net/slirp.c | 77 +++++++++++++++++++++++++---------- tests/acceptance/hostfwd.py | 80 +++++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+), 22 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index d4001f9c5d..4de4e4979d 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1375,6 +1375,16 @@ ERST SRST ``hostfwd_add`` Redirect TCP or UDP connections from host to guest (requires -net user). + IPV6 addresses are wrapped in square brackes, IPV4 addresses are not. + + Examples: + hostfwd_add net0 tcp:127.0.0.1:10022-:22 + hostfwd_add net0 tcp:[::1]:10022-[fe80::1:2:3:4]:22 + + Note that Libslirp currently only provides a "stateless" DHCPv6 server, a + consequence of which is that it cannot do the "addr-any" translation to the + guest address that is done for IPv4. In other words, the following is + currently not supported: hostfwd_add net0 tcp:[::1]:10022-:22 ERST #ifdef CONFIG_SLIRP @@ -1390,6 +1400,11 @@ ERST SRST ``hostfwd_remove`` Remove host-to-guest TCP or UDP redirection. + IPV6 addresses are wrapped in square brackes, IPV4 addresses are not. + + Examples: + hostfwd_remove net0 tcp:127.0.0.1:10022 + hostfwd_remove net0 tcp:[::1]:10022 ERST { diff --git a/net/slirp.c b/net/slirp.c index e0284492b9..32df65c1f0 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -96,6 +96,11 @@ typedef struct SlirpState { GSList *fwd; } SlirpState; +union in4or6_addr { + struct in_addr addr4; + struct in6_addr addr6; +}; + static struct slirp_config_str *slirp_configs; static QTAILQ_HEAD(, SlirpState) slirp_stacks = QTAILQ_HEAD_INITIALIZER(slirp_stacks); @@ -663,32 +668,40 @@ static const char *parse_protocol(const char *str, int sep, bool *is_udp, /* * Parse an ip address/port of the form "address:port". - * An empty address means INADDR_ANY. + * IPv6 addresses are wrapped in [] brackets. + * An empty address means INADDR_ANY/in6addr_any. * Returns a pointer to after the terminator, unless it was '\0' in which case * the result points to the '\0'. - * The parsed results are stored in *addr, *port. + * The parsed results are stored in *addr, *port, *is_v6. * On error NULL is returned and stores the error in *errp. */ static const char *parse_ip_addr_and_port(const char *str, int terminator, - struct in_addr *addr, int *port, - Error **errp) + union in4or6_addr *addr, int *port, + bool *is_v6, Error **errp) { g_autofree char *addr_str = NULL; g_autofree char *port_str = NULL; - bool is_v6; const char *p = inet_parse_host_and_port(str, terminator, &addr_str, - &port_str, &is_v6, errp); + &port_str, is_v6, errp); if (p == NULL) { return NULL; } - /* Ignore is_v6 for the moment, if inet_aton fails let it. */ - if (addr_str[0] == '\0') { - addr->s_addr = INADDR_ANY; - } else if (!inet_aton(addr_str, addr)) { - error_setg(errp, "Bad address"); - return NULL; + if (*is_v6) { + if (addr_str[0] == '\0') { + addr->addr6 = in6addr_any; + } else if (!inet_pton(AF_INET6, addr_str, &addr->addr6)) { + error_setg(errp, "Bad address"); + return NULL; + } + } else { + if (addr_str[0] == '\0') { + addr->addr4.s_addr = INADDR_ANY; + } else if (!inet_pton(AF_INET, addr_str, &addr->addr4)) { + error_setg(errp, "Bad address"); + return NULL; + } } if (qemu_strtoi(port_str, NULL, 10, port) < 0 || @@ -709,11 +722,11 @@ static const char *parse_ip_addr_and_port(const char *str, int terminator, void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict) { - struct in_addr host_addr; + union in4or6_addr host_addr; int host_port; const char *src_str, *p; SlirpState *s; - bool is_udp; + bool is_udp, is_v6; int err; Error *error = NULL; const char *arg1 = qdict_get_str(qdict, "arg1"); @@ -738,11 +751,18 @@ void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict) goto fail_syntax; } - if (parse_ip_addr_and_port(p, '\0', &host_addr, &host_port, + if (parse_ip_addr_and_port(p, '\0', &host_addr, &host_port, &is_v6, &error) == NULL) { goto fail_syntax; } - err = slirp_remove_hostfwd(s->slirp, is_udp, host_addr, host_port); + + if (is_v6) { + err = slirp_remove_ipv6_hostfwd(s->slirp, is_udp, host_addr.addr6, + host_port); + } else { + err = slirp_remove_hostfwd(s->slirp, is_udp, host_addr.addr4, + host_port); + } monitor_printf(mon, "host forwarding rule for %s %s\n", src_str, err ? "not found" : "removed"); @@ -755,11 +775,12 @@ void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict) static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp) { - struct in_addr host_addr, guest_addr; + union in4or6_addr host_addr, guest_addr; int host_port, guest_port; const char *p; - bool is_udp; + bool is_udp, host_is_v6, guest_is_v6; Error *error = NULL; + int err; g_assert(redir_str != NULL); p = redir_str; @@ -769,24 +790,36 @@ static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp) goto fail_syntax; } - p = parse_ip_addr_and_port(p, '-', &host_addr, &host_port, &error); + p = parse_ip_addr_and_port(p, '-', &host_addr, &host_port, &host_is_v6, + &error); if (p == NULL) { error_prepend(&error, "For host address: "); goto fail_syntax; } - if (parse_ip_addr_and_port(p, '\0', &guest_addr, &guest_port, + if (parse_ip_addr_and_port(p, '\0', &guest_addr, &guest_port, &guest_is_v6, &error) == NULL) { error_prepend(&error, "For guest address: "); goto fail_syntax; } + if (host_is_v6 != guest_is_v6) { + /* TODO: Can libslirp support this? */ + error_setg(&error, "Both host,guest must be one of ipv4 or ipv6"); + goto fail_syntax; + } if (guest_port == 0) { error_setg(&error, "For guest address: Bad port"); goto fail_syntax; } - if (slirp_add_hostfwd(s->slirp, is_udp, host_addr, host_port, guest_addr, - guest_port) < 0) { + if (host_is_v6) { + err = slirp_add_ipv6_hostfwd(s->slirp, is_udp, host_addr.addr6, + host_port, guest_addr.addr6, guest_port); + } else { + err = slirp_add_hostfwd(s->slirp, is_udp, host_addr.addr4, host_port, + guest_addr.addr4, guest_port); + } + if (err < 0) { error_setg(errp, "Could not set up host forwarding rule '%s'", redir_str); return -1; diff --git a/tests/acceptance/hostfwd.py b/tests/acceptance/hostfwd.py index e5602a7708..d4c4b7b86d 100644 --- a/tests/acceptance/hostfwd.py +++ b/tests/acceptance/hostfwd.py @@ -92,3 +92,83 @@ def test_qmp_hostfwd_ipv4_parsing_errors(self): self.assertEquals(self.hmc('hostfwd_add ::66-:0'), "Invalid host forwarding rule '::66-:0' " "(For guest address: Bad port)\r\n") + + def test_qmp_hostfwd_ipv6(self): + self.vm.add_args('-nodefaults', + '-netdev', 'user,id=vnet', + '-device', 'virtio-net,netdev=vnet') + self.vm.launch() + self.assertEquals(self.hmc('hostfwd_add vnet tcp:[::1]:65022-[fe80::1]:22'), + '') + self.assertEquals(self.hmc('hostfwd_remove vnet tcp:[::1]:65022'), + 'host forwarding rule for tcp:[::1]:65022 removed\r\n') + self.assertEquals(self.hmc('hostfwd_add tcp:[]:65042-[fe80::1]:42'), + '') + self.assertEquals(self.hmc('hostfwd_remove tcp:[]:65042'), + 'host forwarding rule for tcp:[]:65042 removed\r\n') + self.assertEquals(self.hmc('hostfwd_add udp:[::1]:65042-[fe80::1]:42'), + '') + self.assertEquals(self.hmc('hostfwd_remove udp:[::1]:65042'), + 'host forwarding rule for udp:[::1]:65042 removed\r\n') + + def test_qmp_hostfwd_ipv6_functional_errors(self): + """Verify handling of various kinds of errors given valid addresses.""" + self.vm.add_args('-nodefaults', + '-netdev', 'user,id=vnet', + '-device', 'virtio-net,netdev=vnet') + self.vm.launch() + self.assertEquals(self.hmc('hostfwd_remove :[::1]:65022'), + 'host forwarding rule for :[::1]:65022 not found\r\n') + self.assertEquals(self.hmc('hostfwd_add udp:[::1]:65042-[fe80::1]:42'), + '') + self.assertEquals(self.hmc('hostfwd_add udp:[::1]:65042-[fe80::1]:42'), + "Could not set up host forwarding rule " + "'udp:[::1]:65042-[fe80::1]:42'\r\n") + self.assertEquals(self.hmc('hostfwd_remove :[::1]:65042'), + 'host forwarding rule for :[::1]:65042 not found\r\n') + self.assertEquals(self.hmc('hostfwd_remove udp:[::1]:65042'), + 'host forwarding rule for udp:[::1]:65042 removed\r\n') + self.assertEquals(self.hmc('hostfwd_remove udp:[::1]:65042'), + 'host forwarding rule for udp:[::1]:65042 not found\r\n') + + def test_qmp_hostfwd_ipv6_errors(self): + """Verify handling of various kinds of errors.""" + self.vm.add_args('-nodefaults', + '-netdev', 'user,id=vnet', + '-device', 'virtio-net,netdev=vnet') + self.vm.launch() + self.assertEquals(self.hmc('hostfwd_add :[::1-'), + "Invalid host forwarding rule ':[::1-' " + "(For host address: error parsing IPv6 address '[::1')\r\n") + self.assertEquals(self.hmc('hostfwd_add :[::1]:66-[fe80::1'), + "Invalid host forwarding rule ':[::1]:66-[fe80::1' " + "(For guest address: error parsing IPv6 address " + "'[fe80::1')\r\n") + self.assertEquals(self.hmc('hostfwd_add :[:::]:66-foo'), + "Invalid host forwarding rule ':[:::]:66-foo' " + "(For host address: Bad address)\r\n") + self.assertEquals(self.hmc('hostfwd_add :[::1]-foo'), + "Invalid host forwarding rule ':[::1]-foo' " + "(For host address: error parsing IPv6 address '[::1]')\r\n") + self.assertEquals(self.hmc('hostfwd_add :[::1]:66-[foo]'), + "Invalid host forwarding rule ':[::1]:66-[foo]' " + "(For guest address: error parsing IPv6 address '[foo]')\r\n") + self.assertEquals(self.hmc('hostfwd_add :[::1]:66666-foo'), + "Invalid host forwarding rule ':[::1]:66666-foo' " + "(For host address: Bad port)\r\n") + self.assertEquals(self.hmc('hostfwd_add :[::1]:66-[fe80::1]:-1'), + "Invalid host forwarding rule " + "':[::1]:66-[fe80::1]:-1' (For guest address: Bad port)\r\n") + self.assertEquals(self.hmc('hostfwd_add :[::1]:66-[fe80::1]:66666'), + "Invalid host forwarding rule " + "':[::1]:66-[fe80::1]:66666' (For guest address: Bad port)\r\n") + self.assertEquals(self.hmc('hostfwd_add :[::1]:66-[fe80::1]:0'), + "Invalid host forwarding rule " + "':[::1]:66-[fe80::1]:0' (For guest address: Bad port)\r\n") + self.assertEquals(self.hmc('hostfwd_add :[::1]:66-1.2.3.4:66'), + "Invalid host forwarding rule ':[::1]:66-1.2.3.4:66' " + "(Both host,guest must be one of ipv4 or ipv6)\r\n") + self.assertEquals(self.hmc('hostfwd_add :1.2.3.4:66-[fe80::1]:66'), + "Invalid host forwarding rule " + "':1.2.3.4:66-[fe80::1]:66' (Both host,guest must be " + "one of ipv4 or ipv6)\r\n")