Received: by 10.223.185.116 with SMTP id b49csp5449242wrg; Wed, 7 Mar 2018 11:58:37 -0800 (PST) X-Google-Smtp-Source: AG47ELsidR2imm81HlXTIycCbfW0HIOtDt6zI2LbyRvQ7rdndgGlG1WnZi1b0YoX3mYJcJAzktbN X-Received: by 10.101.97.26 with SMTP id z26mr19228975pgu.44.1520452717713; Wed, 07 Mar 2018 11:58:37 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1520452717; cv=none; d=google.com; s=arc-20160816; b=akaYtDydkhM4wmh1Dnn/RA0Ka++S9kbYM2j/U27ZAAfOPrbCt65zbS2BWlHAtZFrDm 3zqX7Ppo1OxpqgylM/bWI3AY0FYnKewLiCdTjbCPaYj2Eaa3sunDDMiGpB86JOKWETxT hvD4M0VGGpQNh7bgGQa7BzKityC5b5mn5UtmC+DQo/HdNajp9uKlm9eT3+u6jXNtRBlO mppGjYRsiVpjDWd+f8eLUtlRodNvW2/GFQkxBgpXcbWHYkCOx7sQMajuUfHFOxKLUcY3 Y9YQ5B7jZ9KtV9vEWhEap6NTclMwksqG5rbgJX2cmvnK0AXZZzOK8HTJfFs1R03qAk4y HmHQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:user-agent:references :in-reply-to:message-id:date:subject:cc:to:from :arc-authentication-results; bh=hFpebPwa0+/uWr+SJDd13IYDRARjjAePuXfDFzV0Z70=; b=kzpAtcn41KmXPRxMkJepiAM1GZz0OXjeuUVxG+ybSD8G6JcVa9VW4w/JgqV4FJEDA1 Ot5bRhaTKFvRSH6FgkZL/QPVSehdQQAMT+ZyR/H2kyItejPzlKF3eDvM0PvJgfw+BRNJ 5imDHgQ1oqyiF5QVWOZXPbUi+clloZbJqn1ZYDM3ynrLkeKp/oK1IjpgEOpLZ7HKt4gd DgJs9K3b/89nkQTBkdLjL9AcGgcYNuxcUxzndBNwU5tOFyVI5IOAzuuO+rnfMa5N2+Xj P53QWsxv6GMG15e2DKMErn77derstY4HT6XIMtNEjuidm0znrhSSZFsaXPHQVNks8BVu I4Mw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id z124si11778618pgb.677.2018.03.07.11.58.23; Wed, 07 Mar 2018 11:58:37 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S965711AbeCGTrR (ORCPT + 99 others); Wed, 7 Mar 2018 14:47:17 -0500 Received: from mail.linuxfoundation.org ([140.211.169.12]:44936 "EHLO mail.linuxfoundation.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S965684AbeCGTrP (ORCPT ); Wed, 7 Mar 2018 14:47:15 -0500 Received: from localhost (unknown [185.236.200.248]) by mail.linuxfoundation.org (Postfix) with ESMTPSA id 3C0DB10DA; Wed, 7 Mar 2018 19:47:15 +0000 (UTC) From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Julian Wiedmann , "David S. Miller" Subject: [PATCH 4.14 081/110] s390/qeth: fix IP address lookup for L3 devices Date: Wed, 7 Mar 2018 11:39:04 -0800 Message-Id: <20180307191050.173434066@linuxfoundation.org> X-Mailer: git-send-email 2.16.2 In-Reply-To: <20180307191039.748351103@linuxfoundation.org> References: <20180307191039.748351103@linuxfoundation.org> User-Agent: quilt/0.65 X-stable: review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 4.14-stable review patch. If anyone has any objections, please let me know. ------------------ From: Julian Wiedmann [ Upstream commit c5c48c58b259bb8f0482398370ee539d7a12df3e ] Current code ("qeth_l3_ip_from_hash()") matches a queried address object against objects in the IP table by IP address, Mask/Prefix Length and MAC address ("qeth_l3_ipaddrs_is_equal()"). But what callers actually require is either a) "is this IP address registered" (ie. match by IP address only), before adding a new address. b) or "is this address object registered" (ie. match all relevant attributes), before deleting an address. Right now 1. the ADD path is too strict in its lookup, and eg. doesn't detect conflicts between an existing NORMAL address and a new VIPA address (because the NORMAL address will have mask != 0, while VIPA has a mask == 0), 2. the DELETE path is not strict enough, and eg. allows del_rxip() to delete a VIPA address as long as the IP address matches. Fix all this by adding helpers (_addr_match_ip() and _addr_match_all()) that do the appropriate checking. Note that the ADD path for NORMAL addresses is special, as qeth keeps track of how many times such an address is in use (and there is no immediate way of returning errors to the caller). So when a requested NORMAL address _fully_ matches an existing one, it's not considered a conflict and we merely increment the refcount. Fixes: 5f78e29ceebf ("qeth: optimize IP handling in rx_mode callback") Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/s390/net/qeth_l3.h | 34 ++++++++++++++ drivers/s390/net/qeth_l3_main.c | 91 ++++++++++++++++++---------------------- 2 files changed, 74 insertions(+), 51 deletions(-) --- a/drivers/s390/net/qeth_l3.h +++ b/drivers/s390/net/qeth_l3.h @@ -40,8 +40,40 @@ struct qeth_ipaddr { unsigned int pfxlen; } a6; } u; - }; + +static inline bool qeth_l3_addr_match_ip(struct qeth_ipaddr *a1, + struct qeth_ipaddr *a2) +{ + if (a1->proto != a2->proto) + return false; + if (a1->proto == QETH_PROT_IPV6) + return ipv6_addr_equal(&a1->u.a6.addr, &a2->u.a6.addr); + return a1->u.a4.addr == a2->u.a4.addr; +} + +static inline bool qeth_l3_addr_match_all(struct qeth_ipaddr *a1, + struct qeth_ipaddr *a2) +{ + /* Assumes that the pair was obtained via qeth_l3_addr_find_by_ip(), + * so 'proto' and 'addr' match for sure. + * + * For ucast: + * - 'mac' is always 0. + * - 'mask'/'pfxlen' for RXIP/VIPA is always 0. For NORMAL, matching + * values are required to avoid mixups in takeover eligibility. + * + * For mcast, + * - 'mac' is mapped from the IP, and thus always matches. + * - 'mask'/'pfxlen' is always 0. + */ + if (a1->type != a2->type) + return false; + if (a1->proto == QETH_PROT_IPV6) + return a1->u.a6.pfxlen == a2->u.a6.pfxlen; + return a1->u.a4.mask == a2->u.a4.mask; +} + static inline u64 qeth_l3_ipaddr_hash(struct qeth_ipaddr *addr) { u64 ret = 0; --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -149,6 +149,24 @@ int qeth_l3_string_to_ipaddr(const char return -EINVAL; } +static struct qeth_ipaddr *qeth_l3_find_addr_by_ip(struct qeth_card *card, + struct qeth_ipaddr *query) +{ + u64 key = qeth_l3_ipaddr_hash(query); + struct qeth_ipaddr *addr; + + if (query->is_multicast) { + hash_for_each_possible(card->ip_mc_htable, addr, hnode, key) + if (qeth_l3_addr_match_ip(addr, query)) + return addr; + } else { + hash_for_each_possible(card->ip_htable, addr, hnode, key) + if (qeth_l3_addr_match_ip(addr, query)) + return addr; + } + return NULL; +} + static void qeth_l3_convert_addr_to_bits(u8 *addr, u8 *bits, int len) { int i, j; @@ -202,34 +220,6 @@ static bool qeth_l3_is_addr_covered_by_i return rc; } -inline int -qeth_l3_ipaddrs_is_equal(struct qeth_ipaddr *addr1, struct qeth_ipaddr *addr2) -{ - return addr1->proto == addr2->proto && - !memcmp(&addr1->u, &addr2->u, sizeof(addr1->u)) && - !memcmp(&addr1->mac, &addr2->mac, sizeof(addr1->mac)); -} - -static struct qeth_ipaddr * -qeth_l3_ip_from_hash(struct qeth_card *card, struct qeth_ipaddr *tmp_addr) -{ - struct qeth_ipaddr *addr; - - if (tmp_addr->is_multicast) { - hash_for_each_possible(card->ip_mc_htable, addr, - hnode, qeth_l3_ipaddr_hash(tmp_addr)) - if (qeth_l3_ipaddrs_is_equal(tmp_addr, addr)) - return addr; - } else { - hash_for_each_possible(card->ip_htable, addr, - hnode, qeth_l3_ipaddr_hash(tmp_addr)) - if (qeth_l3_ipaddrs_is_equal(tmp_addr, addr)) - return addr; - } - - return NULL; -} - int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr) { int rc = 0; @@ -244,8 +234,8 @@ int qeth_l3_delete_ip(struct qeth_card * QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8); } - addr = qeth_l3_ip_from_hash(card, tmp_addr); - if (!addr) + addr = qeth_l3_find_addr_by_ip(card, tmp_addr); + if (!addr || !qeth_l3_addr_match_all(addr, tmp_addr)) return -ENOENT; addr->ref_counter--; @@ -267,6 +257,7 @@ int qeth_l3_add_ip(struct qeth_card *car { int rc = 0; struct qeth_ipaddr *addr; + char buf[40]; QETH_CARD_TEXT(card, 4, "addip"); @@ -277,8 +268,20 @@ int qeth_l3_add_ip(struct qeth_card *car QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8); } - addr = qeth_l3_ip_from_hash(card, tmp_addr); - if (!addr) { + addr = qeth_l3_find_addr_by_ip(card, tmp_addr); + if (addr) { + if (tmp_addr->type != QETH_IP_TYPE_NORMAL) + return -EADDRINUSE; + if (qeth_l3_addr_match_all(addr, tmp_addr)) { + addr->ref_counter++; + return 0; + } + qeth_l3_ipaddr_to_string(tmp_addr->proto, (u8 *)&tmp_addr->u, + buf); + dev_warn(&card->gdev->dev, + "Registering IP address %s failed\n", buf); + return -EADDRINUSE; + } else { addr = qeth_l3_get_addr_buffer(tmp_addr->proto); if (!addr) return -ENOMEM; @@ -326,11 +329,7 @@ int qeth_l3_add_ip(struct qeth_card *car hash_del(&addr->hnode); kfree(addr); } - } else { - if (addr->type == QETH_IP_TYPE_NORMAL) - addr->ref_counter++; } - return rc; } @@ -714,12 +713,7 @@ int qeth_l3_add_vipa(struct qeth_card *c return -ENOMEM; spin_lock_bh(&card->ip_lock); - - if (qeth_l3_ip_from_hash(card, ipaddr)) - rc = -EEXIST; - else - qeth_l3_add_ip(card, ipaddr); - + rc = qeth_l3_add_ip(card, ipaddr); spin_unlock_bh(&card->ip_lock); kfree(ipaddr); @@ -782,12 +776,7 @@ int qeth_l3_add_rxip(struct qeth_card *c return -ENOMEM; spin_lock_bh(&card->ip_lock); - - if (qeth_l3_ip_from_hash(card, ipaddr)) - rc = -EEXIST; - else - qeth_l3_add_ip(card, ipaddr); - + rc = qeth_l3_add_ip(card, ipaddr); spin_unlock_bh(&card->ip_lock); kfree(ipaddr); @@ -1395,8 +1384,9 @@ qeth_l3_add_mc_to_hash(struct qeth_card memcpy(tmp->mac, buf, sizeof(tmp->mac)); tmp->is_multicast = 1; - ipm = qeth_l3_ip_from_hash(card, tmp); + ipm = qeth_l3_find_addr_by_ip(card, tmp); if (ipm) { + /* for mcast, by-IP match means full match */ ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING; } else { ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV4); @@ -1479,8 +1469,9 @@ qeth_l3_add_mc6_to_hash(struct qeth_card sizeof(struct in6_addr)); tmp->is_multicast = 1; - ipm = qeth_l3_ip_from_hash(card, tmp); + ipm = qeth_l3_find_addr_by_ip(card, tmp); if (ipm) { + /* for mcast, by-IP match means full match */ ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING; continue; }