Received: by 2002:a05:6a10:413:0:0:0:0 with SMTP id 19csp1016679pxp; Thu, 17 Mar 2022 00:30:26 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwLFCOj6RZsGXsN5YncAT1/OejP+TuP2Ug+4FbpDcb02QAalxynR7VeVl5/JicvCFh4l1ZD X-Received: by 2002:a17:90b:789:b0:1bc:293c:1445 with SMTP id l9-20020a17090b078900b001bc293c1445mr14237151pjz.111.1647502226372; Thu, 17 Mar 2022 00:30:26 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1647502226; cv=none; d=google.com; s=arc-20160816; b=J8OJDCEipBEuei7ubLeb4o7dld+yk1oKQgCQ10En9TWfuT4UiTXcnVdWk6uZsXc9CC twJoHn0BgiraMvNaNtbwXkwBpQt6qca9+VommVmdYAa1RPyjwMhHORHT0496OnXJd/81 tBNXzZdwEdtHA5x/xSeCG0cn80cBeej7TrBjVYa/zkoSASoQ3jt8eYYoCTQTJ6tBo4mR xgKNVsNuR//Zwi0xTr3CtPw4FiR9w9skflGmDBtshKrQq7bhVggvESwL/7ErdltnkgRM kfZYbK8C341yIFy43UmBOwAwAhwVkzsawDJQfesT/Q0IkagefmZPRN8FzBQuWj+IytJa 48iQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from:sender:hmm_source_type :hmm_attache_num:hmm_source_ip; bh=uloCWQkPXkYPBSf2KNkNAic7yQOKCVb41+NwieVRfuo=; b=FejeQGwAbjajVLIEwzzMnY08YNI/KWzbJXYqf5ZpRPV67QRe4isODYaT/bPGQdNUHO fAmgBzI5V3W6DUeiAj/CTD6ezwAIlfl1abvTVtv9oE1gPKeOGsp9L3mIQfF768/TMjAy b6nqq7N7xDRr4wx2xkvGrwWAiPirkxRaJt9FQnxEX7LTCcRcwlBeYHWk60goS3ctfKpO bql6SU1Ha8P2D2xReMkkfpZW+/QaBsJDHdscz8wk/FWLcklYfawhBkTCvg5vpk78rDWw cK8UZLi435imbVJHX0+wj8RfRvSfVDnC4Y6aUtffpgSDa2JD1di9kM14uc2W+z+ncRQp WqLw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id d33-20020a630e21000000b003816043f0c1si1270120pgl.694.2022.03.17.00.30.13; Thu, 17 Mar 2022 00:30:26 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229888AbiCQGYr (ORCPT + 99 others); Thu, 17 Mar 2022 02:24:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41276 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230024AbiCQGYk (ORCPT ); Thu, 17 Mar 2022 02:24:40 -0400 Received: from chinatelecom.cn (prt-mail.chinatelecom.cn [42.123.76.219]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 4564D158DBF; Wed, 16 Mar 2022 23:17:02 -0700 (PDT) HMM_SOURCE_IP: 172.18.0.48:53606.1514678939 HMM_ATTACHE_NUM: 0000 HMM_SOURCE_TYPE: SMTP Received: from clientip-202.80.192.39 (unknown [172.18.0.48]) by chinatelecom.cn (HERMES) with SMTP id BDBD6280083; Thu, 17 Mar 2022 14:16:48 +0800 (CST) X-189-SAVE-TO-SEND: +sunshouxin@chinatelecom.cn Received: from ([172.18.0.48]) by app0024 with ESMTP id 49958f897f46428990b288c91cecf15b for j.vosburgh@gmail.com; Thu, 17 Mar 2022 14:16:53 CST X-Transaction-ID: 49958f897f46428990b288c91cecf15b X-Real-From: sunshouxin@chinatelecom.cn X-Receive-IP: 172.18.0.48 X-MEDUSA-Status: 0 Sender: sunshouxin@chinatelecom.cn From: Sun Shouxin To: j.vosburgh@gmail.com, vfalico@gmail.com, andy@greyhouse.net, davem@davemloft.net, kuba@kernel.org, yoshfuji@linux-ipv6.org, dsahern@kernel.org, oliver@neukum.org Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, huyd12@chinatelecom.cn, sunshouxin@chinatelecom.cn Subject: [PATCH v4] net:bonding:Add support for IPV6 RLB to balance-alb mode Date: Thu, 17 Mar 2022 02:15:21 -0400 Message-Id: <20220317061521.23985-1-sunshouxin@chinatelecom.cn> X-Mailer: git-send-email 2.27.0 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,SPF_HELO_PASS, SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This patch is implementing IPV6 RLB for balance-alb mode. Suggested-by: Hu Yadi Signed-off-by: Sun Shouxin --- changelog: v1-->v2: -Remove ndisc_bond_send_na and refactor ndisc_send_na. -In rlb_nd_xmit, if the lladdr is not local, return curr_active_slave. -Don't send neighbor advertisement message when receiving neighbor advertisement message in rlb6_update_entry_from_na. v2-->v3: -Don't export ndisc_send_na. -Use ipv6_stub->ndisc_send_na to replace ndisc_send_na in rlb6_update_client. v3-->v4: -Submit all code at a whole patch. --- drivers/net/bonding/bond_3ad.c | 2 +- drivers/net/bonding/bond_alb.c | 592 ++++++++++++++++++++++++++++- drivers/net/bonding/bond_debugfs.c | 14 + drivers/net/bonding/bond_main.c | 6 +- drivers/net/usb/cdc_mbim.c | 2 +- include/net/bond_3ad.h | 2 +- include/net/bond_alb.h | 7 + include/net/bonding.h | 6 +- include/net/ipv6_stubs.h | 3 +- include/net/ndisc.h | 9 +- net/ipv6/addrconf.c | 4 +- net/ipv6/ndisc.c | 64 +++- 12 files changed, 675 insertions(+), 36 deletions(-) diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index a86b1f71762e..3cba269f12e2 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -2682,7 +2682,7 @@ int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info) return ret; } -int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond, +int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct bonding *bond, struct slave *slave) { struct lacpdu *lacpdu, _lacpdu; diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 303c8d32d451..06a4557e00e3 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -23,6 +23,9 @@ #include #include #include +#include +#include +#include static const u8 mac_v6_allmcast[ETH_ALEN + 2] __long_aligned = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 @@ -57,6 +60,13 @@ static void rlb_purge_src_ip(struct bonding *bond, struct arp_pkt *arp); static void rlb_src_unlink(struct bonding *bond, u32 index); static void rlb_src_link(struct bonding *bond, u32 ip_src_hash, u32 ip_dst_hash); +static void rlb6_delete_table_entry(struct bonding *bond, u32 index); +static u8 *alb_get_lladdr(struct sk_buff *skb); +static void alb_set_nd_option(struct sk_buff *skb, struct bonding *bond, + struct slave *tx_slave); +static bool alb_determine_ipv6_nd(struct sk_buff *skb, struct bonding *bond); +static int rlb_recv(struct sk_buff *skb, struct bonding *bond, + struct slave *slave); static inline u8 _simple_hash(const u8 *hash_start, int hash_size) { @@ -269,7 +279,7 @@ static void rlb_update_entry_from_arp(struct bonding *bond, struct arp_pkt *arp) spin_unlock_bh(&bond->mode_lock); } -static int rlb_arp_recv(const struct sk_buff *skb, struct bonding *bond, +static int rlb_arp_recv(struct sk_buff *skb, struct bonding *bond, struct slave *slave) { struct arp_pkt *arp, _arp; @@ -415,6 +425,31 @@ static void rlb_clear_slave(struct bonding *bond, struct slave *slave) } } + rx_hash_table = bond_info->rx6_hashtbl; + index = bond_info->rx6_hashtbl_used_head; + for (; index != RLB_NULL_INDEX; index = next_index) { + next_index = rx_hash_table[index].used_next; + if (rx_hash_table[index].slave == slave) { + struct slave *assigned_slave = rlb_next_rx_slave(bond); + + if (assigned_slave) { + u8 mac_dst[ETH_ALEN]; + + rx_hash_table[index].slave = assigned_slave; + memcpy(mac_dst, rx_hash_table[index].mac_dst, + sizeof(mac_dst)); + if (is_valid_ether_addr(mac_dst)) { + bond_info->rx6_hashtbl[index].ntt = 1; + bond_info->rx6_ntt = 1; + bond_info->rlb6_update_retry_counter = + RLB_UPDATE_RETRY; + } + } else { /* there is no active slave */ + rx_hash_table[index].slave = NULL; + } + } + } + spin_unlock_bh(&bond->mode_lock); if (slave != rtnl_dereference(bond->curr_active_slave)) @@ -704,7 +739,7 @@ static void rlb_rebalance(struct bonding *bond) struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); struct slave *assigned_slave; struct rlb_client_info *client_info; - int ntt; + int ntt, ntt_ip6; u32 hash_index; spin_lock_bh(&bond->mode_lock); @@ -724,9 +759,27 @@ static void rlb_rebalance(struct bonding *bond) } } + ntt_ip6 = 0; + hash_index = bond_info->rx6_hashtbl_used_head; + for (; hash_index != RLB_NULL_INDEX; + hash_index = client_info->used_next) { + client_info = &bond_info->rx6_hashtbl[hash_index]; + assigned_slave = __rlb_next_rx_slave(bond); + if (assigned_slave && client_info->slave != assigned_slave) { + client_info->slave = assigned_slave; + if (!is_zero_ether_addr(client_info->mac_dst)) { + client_info->ntt = 1; + ntt_ip6 = 1; + } + } + } + /* update the team's flag only after the whole iteration */ if (ntt) bond_info->rx_ntt = 1; + + if (ntt_ip6) + bond_info->rx6_ntt = 1; spin_unlock_bh(&bond->mode_lock); } @@ -846,6 +899,7 @@ static int rlb_initialize(struct bonding *bond) { struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); struct rlb_client_info *new_hashtbl; + struct rlb_client_info *new6_hashtbl; int size = RLB_HASH_TABLE_SIZE * sizeof(struct rlb_client_info); int i; @@ -853,19 +907,29 @@ static int rlb_initialize(struct bonding *bond) if (!new_hashtbl) return -1; + new6_hashtbl = kmalloc(size, GFP_KERNEL); + if (!new6_hashtbl) { + kfree(new_hashtbl); + return -1; + } + spin_lock_bh(&bond->mode_lock); bond_info->rx_hashtbl = new_hashtbl; + bond_info->rx6_hashtbl = new6_hashtbl; bond_info->rx_hashtbl_used_head = RLB_NULL_INDEX; + bond_info->rx6_hashtbl_used_head = RLB_NULL_INDEX; - for (i = 0; i < RLB_HASH_TABLE_SIZE; i++) + for (i = 0; i < RLB_HASH_TABLE_SIZE; i++) { rlb_init_table_entry(bond_info->rx_hashtbl + i); + rlb_init_table_entry(bond_info->rx6_hashtbl + i); + } spin_unlock_bh(&bond->mode_lock); /* register to receive ARPs */ - bond->recv_probe = rlb_arp_recv; + bond->recv_probe = rlb_recv; return 0; } @@ -880,6 +944,10 @@ static void rlb_deinitialize(struct bonding *bond) bond_info->rx_hashtbl = NULL; bond_info->rx_hashtbl_used_head = RLB_NULL_INDEX; + kfree(bond_info->rx6_hashtbl); + bond_info->rx6_hashtbl = NULL; + bond_info->rx6_hashtbl_used_head = RLB_NULL_INDEX; + spin_unlock_bh(&bond->mode_lock); } @@ -901,9 +969,397 @@ static void rlb_clear_vlan(struct bonding *bond, unsigned short vlan_id) curr_index = next_index; } + curr_index = bond_info->rx6_hashtbl_used_head; + while (curr_index != RLB_NULL_INDEX) { + struct rlb_client_info *curr = &bond_info->rx6_hashtbl[curr_index]; + u32 next_index = bond_info->rx6_hashtbl[curr_index].used_next; + + if (curr->vlan_id == vlan_id) + rlb6_delete_table_entry(bond, curr_index); + + curr_index = next_index; + } + + spin_unlock_bh(&bond->mode_lock); +} + +/*********************** ipv6 rlb specific functions ***************************/ +static void rlb6_update_client(struct rlb_client_info *client_info) +{ + struct nd_sendinfo sendinfo; + int i; + + if (!client_info->slave || !is_valid_ether_addr(client_info->mac_dst)) + return; + + sendinfo.vlanid = client_info->vlan_id; + sendinfo.mac_dst = client_info->mac_dst; + sendinfo.mac_src = client_info->slave->dev->dev_addr; + + for (i = 0; i < RLB_ARP_BURST_SIZE; i++) { + ipv6_stub->ndisc_send_na(client_info->slave->dev, + &client_info->ip6_dst, + &client_info->ip6_src, + false, false, true, true, + &sendinfo); + } +} + +static void rlb6_update_rx_clients(struct bonding *bond) +{ + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + struct rlb_client_info *client_info; + u32 hash_index; + + spin_lock_bh(&bond->mode_lock); + + hash_index = bond_info->rx6_hashtbl_used_head; + for (; hash_index != RLB_NULL_INDEX; + hash_index = client_info->used_next) { + client_info = &bond_info->rx6_hashtbl[hash_index]; + if (client_info->ntt) { + rlb6_update_client(client_info); + if (bond_info->rlb6_update_retry_counter == 0) + client_info->ntt = 0; + } + } + + bond_info->rlb6_update_delay_counter = RLB_UPDATE_DELAY; + + spin_unlock_bh(&bond->mode_lock); +} + +static void rlb6_delete_table_entry_dst(struct bonding *bond, u32 index) +{ + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + u32 next_index = bond_info->rx6_hashtbl[index].used_next; + u32 prev_index = bond_info->rx6_hashtbl[index].used_prev; + + if (index == bond_info->rx6_hashtbl_used_head) + bond_info->rx6_hashtbl_used_head = next_index; + + if (next_index != RLB_NULL_INDEX) + bond_info->rx6_hashtbl[next_index].used_prev = prev_index; + + if (prev_index != RLB_NULL_INDEX) + bond_info->rx6_hashtbl[prev_index].used_next = next_index; +} + +static void rlb6_src_link(struct bonding *bond, u32 ip_src_hash, + u32 ip_dst_hash) +{ + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + u32 next; + + bond_info->rx6_hashtbl[ip_dst_hash].src_prev = ip_src_hash; + next = bond_info->rx6_hashtbl[ip_src_hash].src_first; + bond_info->rx6_hashtbl[ip_dst_hash].src_next = next; + if (next != RLB_NULL_INDEX) + bond_info->rx6_hashtbl[next].src_prev = ip_dst_hash; + bond_info->rx6_hashtbl[ip_src_hash].src_first = ip_dst_hash; +} + +static void rlb6_src_unlink(struct bonding *bond, u32 index) +{ + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + u32 next_index = bond_info->rx6_hashtbl[index].src_next; + u32 prev_index = bond_info->rx6_hashtbl[index].src_prev; + + bond_info->rx6_hashtbl[index].src_next = RLB_NULL_INDEX; + bond_info->rx6_hashtbl[index].src_prev = RLB_NULL_INDEX; + + if (next_index != RLB_NULL_INDEX) + bond_info->rx6_hashtbl[next_index].src_prev = prev_index; + + if (prev_index == RLB_NULL_INDEX) + return; + + if (bond_info->rx6_hashtbl[prev_index].src_first == index) + bond_info->rx6_hashtbl[prev_index].src_first = next_index; + else + bond_info->rx6_hashtbl[prev_index].src_next = next_index; +} + +static void rlb6_req_update_slave_clients(struct bonding *bond, + struct slave *slave) +{ + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + struct rlb_client_info *client_info; + u32 hash_index; + int ntt = 0; + + spin_lock_bh(&bond->mode_lock); + + hash_index = bond_info->rx6_hashtbl_used_head; + for (; hash_index != RLB_NULL_INDEX; + hash_index = client_info->used_next) { + client_info = &bond_info->rx6_hashtbl[hash_index]; + if (client_info->slave == slave && + is_valid_ether_addr(client_info->mac_dst)) { + client_info->ntt = 1; + ntt = 1; + } + } + + if (ntt) { + bond_info->rx6_ntt = 1; + bond_info->rlb6_update_retry_counter = + RLB_UPDATE_RETRY; + } + spin_unlock_bh(&bond->mode_lock); +} + +static struct slave *rlb6_nd_choose_channel(struct sk_buff *skb, + struct bonding *bond, + struct ipv6hdr *ip6hdr, + u8 type) +{ + struct nd_msg *msg; + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + struct slave *assigned_slave, *curr_active_slave; + struct rlb_client_info *client_info; + struct ethhdr *eth_data; + u8 *dst_ip; + u32 hash_index = 0; + + spin_lock(&bond->mode_lock); + + msg = (struct nd_msg *)skb_transport_header(skb); + eth_data = eth_hdr(skb); + curr_active_slave = rcu_dereference(bond->curr_active_slave); + + if (type == NDISC_NEIGHBOUR_SOLICITATION) + dst_ip = (u8 *)msg->target.s6_addr; + else + dst_ip = (u8 *)ip6hdr->daddr.s6_addr; + + hash_index = _simple_hash(dst_ip, + sizeof(struct in6_addr)); + client_info = &bond_info->rx6_hashtbl[hash_index]; + + if (client_info->assigned) { + if (!memcmp(client_info->ip6_dst.s6_addr, dst_ip, + sizeof(struct in6_addr)) && + !memcmp(client_info->ip6_src.s6_addr, + ip6hdr->saddr.s6_addr, + sizeof(ip6hdr->saddr.s6_addr))) { + ether_addr_copy(client_info->mac_src, + eth_data->h_source); + + assigned_slave = client_info->slave; + if (assigned_slave) { + spin_unlock(&bond->mode_lock); + return assigned_slave; + } + } else { + if (curr_active_slave && + curr_active_slave != client_info->slave) { + client_info->slave = curr_active_slave; + rlb6_update_client(client_info); + } + } + } + + /* assign a new slave */ + assigned_slave = __rlb_next_rx_slave(bond); + + if (assigned_slave) { + if (!(client_info->assigned && + !memcmp(client_info->ip6_src.s6_addr, + ip6hdr->saddr.s6_addr, sizeof(ip6hdr->saddr.s6_addr)))) { + u32 hash_src = _simple_hash((u8 *)ip6hdr->saddr.s6_addr, + sizeof(ip6hdr->saddr.s6_addr)); + + rlb6_src_unlink(bond, hash_index); + rlb6_src_link(bond, hash_src, hash_index); + } + + memcpy(client_info->ip6_src.s6_addr, ip6hdr->saddr.s6_addr, + sizeof(ip6hdr->saddr.s6_addr)); + memcpy(client_info->ip6_dst.s6_addr, dst_ip, + sizeof(struct in6_addr)); + + ether_addr_copy(client_info->mac_dst, eth_data->h_dest); + ether_addr_copy(client_info->mac_src, eth_data->h_source); + + client_info->slave = assigned_slave; + + if (is_valid_ether_addr(client_info->mac_dst)) { + client_info->ntt = 1; + bond->alb_info.rx6_ntt = 1; + } else { + client_info->ntt = 0; + } + + if (vlan_get_tag(skb, &client_info->vlan_id)) + client_info->vlan_id = 0; + + if (!client_info->assigned) { + u32 prev_tbl_head = bond_info->rx6_hashtbl_used_head; + + bond_info->rx6_hashtbl_used_head = hash_index; + client_info->used_next = prev_tbl_head; + if (prev_tbl_head != RLB_NULL_INDEX) + bond_info->rx6_hashtbl[prev_tbl_head].used_prev = hash_index; + client_info->assigned = 1; + } + } + + spin_unlock(&bond->mode_lock); + + return assigned_slave; +} + +static struct slave *rlb_nd_xmit(struct sk_buff *skb, struct bonding *bond) +{ + struct slave *tx_slave = NULL; + struct ipv6hdr *ip6hdr; + struct icmp6hdr *hdr; + u8 *lladdr; + + if (!pskb_network_may_pull(skb, sizeof(*ip6hdr))) + return NULL; + + ip6hdr = ipv6_hdr(skb); + if (ip6hdr->nexthdr != IPPROTO_ICMPV6) + return NULL; + + if (!pskb_network_may_pull(skb, sizeof(*ip6hdr) + sizeof(*hdr))) + return NULL; + + hdr = icmp6_hdr(skb); + + if (hdr->icmp6_type != NDISC_NEIGHBOUR_ADVERTISEMENT && + hdr->icmp6_type != NDISC_NEIGHBOUR_SOLICITATION) { + return NULL; + } + + lladdr = alb_get_lladdr(skb); + if (!lladdr) + return NULL; + + if (!bond_slave_has_mac_rx(bond, lladdr)) { + tx_slave = rcu_dereference(bond->curr_active_slave); + return tx_slave; + } + + tx_slave = rlb6_nd_choose_channel(skb, bond, ip6hdr, hdr->icmp6_type); + if (!tx_slave) + return NULL; + + alb_set_nd_option(skb, bond, tx_slave); + + return tx_slave; +} + +static void rlb6_update_entry_from_na(struct bonding *bond, + struct ipv6hdr *ip6hdr, u8 *lladdr) +{ + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + struct rlb_client_info *client_info; + u32 hash_index; + + spin_lock_bh(&bond->mode_lock); + + hash_index = _simple_hash(ip6hdr->saddr.s6_addr, + sizeof(ip6hdr->saddr.s6_addr)); + client_info = &bond_info->rx6_hashtbl[hash_index]; + + if (client_info->assigned && + !memcmp(ip6hdr->saddr.s6_addr, client_info->ip6_dst.s6_addr, + sizeof(ip6hdr->saddr.s6_addr)) && !memcmp(ip6hdr->daddr.s6_addr, + client_info->ip6_src.s6_addr, sizeof(ip6hdr->daddr.s6_addr)) && + !ether_addr_equal_64bits(client_info->mac_dst, lladdr)) { + memcpy(client_info->mac_dst, lladdr, + sizeof(client_info->mac_dst)); + } spin_unlock_bh(&bond->mode_lock); } +static void rlb6_delete_table_entry(struct bonding *bond, u32 index) +{ + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + struct rlb_client_info *entry = &bond_info->rx_hashtbl[index]; + + rlb6_delete_table_entry_dst(bond, index); + rlb_init_table_entry_dst(entry); + rlb6_src_unlink(bond, index); +} + +static void rlb6_purge_src_ip(struct bonding *bond, struct ipv6hdr *ip6hdr, + u8 *lladdr) +{ + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + struct rlb_client_info *client_info; + u32 ip_src_hash = _simple_hash((u8 *)ip6hdr->saddr.s6_addr, + sizeof(ip6hdr->saddr.s6_addr)); + u32 index, next_index; + + spin_lock_bh(&bond->mode_lock); + + index = bond_info->rx6_hashtbl[ip_src_hash].src_first; + while (index != RLB_NULL_INDEX) { + client_info = &bond_info->rx6_hashtbl[index]; + next_index = client_info->src_next; + + if (!memcmp(client_info->ip6_src.s6_addr, + ip6hdr->saddr.s6_addr, + sizeof(ip6hdr->saddr.s6_addr)) && + !ether_addr_equal_64bits(lladdr, + client_info->mac_src)) + rlb6_delete_table_entry(bond, index); + index = next_index; + } + + spin_unlock_bh(&bond->mode_lock); +} + +static int rlb_nd_recv(struct sk_buff *skb, struct bonding *bond) +{ + struct ipv6hdr *ip6hdr; + struct nd_msg *msg; + struct inet6_ifaddr *ifp; + u8 *lladdr; + + if (!pskb_network_may_pull(skb, sizeof(*ip6hdr))) + return RX_HANDLER_ANOTHER; + + ip6hdr = ipv6_hdr(skb); + + ifp = ipv6_get_ifaddr(dev_net(skb->dev), &ip6hdr->saddr, NULL, 0); + if (ifp) { + in6_ifa_put(ifp); + return RX_HANDLER_ANOTHER; + } + + if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr) + + sizeof(struct nd_msg))) + return RX_HANDLER_ANOTHER; + + msg = (struct nd_msg *)skb_transport_header(skb); + lladdr = alb_get_lladdr(skb); + if (!lladdr) + return RX_HANDLER_ANOTHER; + + rlb6_purge_src_ip(bond, ip6hdr, lladdr); + + if (msg->icmph.icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT) + rlb6_update_entry_from_na(bond, ip6hdr, lladdr); + + return RX_HANDLER_ANOTHER; +} + +static int rlb_recv(struct sk_buff *skb, struct bonding *bond, + struct slave *slave) +{ + if (skb->protocol == cpu_to_be16(ETH_P_ARP)) + return rlb_arp_recv(skb, bond, slave); + else if (alb_determine_ipv6_nd(skb, bond)) + return rlb_nd_recv(skb, bond); + + return RX_HANDLER_ANOTHER; +} + /*********************** tlb/rlb shared functions *********************/ static void alb_send_lp_vid(struct slave *slave, const u8 mac_addr[], @@ -1068,6 +1524,7 @@ static void alb_fasten_mac_swap(struct bonding *bond, struct slave *slave1, * has changed */ rlb_req_update_slave_clients(bond, slave1); + rlb6_req_update_slave_clients(bond, slave1); } } else { disabled_slave = slave1; @@ -1080,6 +1537,7 @@ static void alb_fasten_mac_swap(struct bonding *bond, struct slave *slave1, * has changed */ rlb_req_update_slave_clients(bond, slave2); + rlb6_req_update_slave_clients(bond, slave2); } } else { disabled_slave = slave2; @@ -1291,6 +1749,111 @@ static bool alb_determine_nd(struct sk_buff *skb, struct bonding *bond) hdr->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION; } +static bool alb_determine_ipv6_nd(struct sk_buff *skb, struct bonding *bond) +{ + if (skb->protocol == htons(ETH_P_IPV6)) { + if (skb_vlan_tag_present(skb)) + skb->transport_header = skb->network_header + sizeof(struct ipv6hdr); + return alb_determine_nd(skb, bond); + } + + return false; +} + +static void alb_change_nd_option(struct sk_buff *skb, const void *data) +{ + struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb); + struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)msg->opt; + struct net_device *dev = skb->dev; + struct icmp6hdr *icmp6h = icmp6_hdr(skb); + struct ipv6hdr *ip6hdr = ipv6_hdr(skb); + u8 *lladdr = NULL; + u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) + + offsetof(struct nd_msg, opt)); + + while (ndoptlen) { + int l; + + switch (nd_opt->nd_opt_type) { + case ND_OPT_SOURCE_LL_ADDR: + case ND_OPT_TARGET_LL_ADDR: + lladdr = ndisc_opt_addr_data(nd_opt, dev); + break; + + default: + lladdr = NULL; + break; + } + + l = nd_opt->nd_opt_len << 3; + + if (ndoptlen < l || l == 0) + return; + + if (lladdr) { + memcpy(lladdr, data, dev->addr_len); + icmp6h->icmp6_cksum = 0; + + icmp6h->icmp6_cksum = csum_ipv6_magic(&ip6hdr->saddr, + &ip6hdr->daddr, + ntohs(ip6hdr->payload_len), + IPPROTO_ICMPV6, + csum_partial(icmp6h, + ntohs(ip6hdr->payload_len), + 0)); + return; + } + ndoptlen -= l; + nd_opt = ((void *)nd_opt) + l; + } +} + +static u8 *alb_get_lladdr(struct sk_buff *skb) +{ + struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb); + struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)msg->opt; + struct net_device *dev = skb->dev; + u8 *lladdr = NULL; + u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) + + offsetof(struct nd_msg, opt)); + + while (ndoptlen) { + int l; + + switch (nd_opt->nd_opt_type) { + case ND_OPT_SOURCE_LL_ADDR: + case ND_OPT_TARGET_LL_ADDR: + lladdr = ndisc_opt_addr_data(nd_opt, dev); + break; + + default: + break; + } + + l = nd_opt->nd_opt_len << 3; + + if (ndoptlen < l || l == 0) + return NULL; + + if (lladdr) + return lladdr; + + ndoptlen -= l; + nd_opt = ((void *)nd_opt) + l; + } + + return lladdr; +} + +static void alb_set_nd_option(struct sk_buff *skb, struct bonding *bond, + struct slave *tx_slave) +{ + if (tx_slave != rcu_access_pointer(bond->curr_active_slave)) { + if (alb_determine_nd(skb, bond)) + alb_change_nd_option(skb, tx_slave->dev->dev_addr); + } +} + /************************ exported alb functions ************************/ int bond_alb_initialize(struct bonding *bond, int rlb_enabled) @@ -1457,12 +2020,17 @@ struct slave *bond_xmit_alb_slave_get(struct bonding *bond, break; } - if (alb_determine_nd(skb, bond)) { + tx_slave = rlb_nd_xmit(skb, bond); + if (tx_slave) { + do_tx_balance = false; + break; + } + + if (!pskb_network_may_pull(skb, sizeof(*ip6hdr))) { do_tx_balance = false; break; } - /* The IPv6 header is pulled by alb_determine_nd */ /* Additionally, DAD probes should not be tx-balanced as that * will lead to false positives for duplicate addresses and * prevent address configuration from working. @@ -1612,6 +2180,17 @@ void bond_alb_monitor(struct work_struct *work) bond_info->rx_ntt = 0; } } + if (bond_info->rx6_ntt) { + if (bond_info->rlb6_update_delay_counter) { + --bond_info->rlb6_update_delay_counter; + } else { + rlb6_update_rx_clients(bond); + if (bond_info->rlb6_update_retry_counter) + --bond_info->rlb6_update_retry_counter; + else + bond_info->rx6_ntt = 0; + } + } } rcu_read_unlock(); re_arm: @@ -1812,6 +2391,7 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr) if (bond->alb_info.rlb_enabled) { /* inform clients mac address has changed */ rlb_req_update_slave_clients(bond, curr_active); + rlb6_req_update_slave_clients(bond, curr_active); } } diff --git a/drivers/net/bonding/bond_debugfs.c b/drivers/net/bonding/bond_debugfs.c index 4f9b4a18c74c..90e88ff9b2bf 100644 --- a/drivers/net/bonding/bond_debugfs.c +++ b/drivers/net/bonding/bond_debugfs.c @@ -41,6 +41,20 @@ static int bond_debug_rlb_hash_show(struct seq_file *m, void *v) client_info->slave->dev->name); } + seq_puts(m, "SourceIP DestinationIP Destination MAC Src MAC DEV\n"); + + hash_index = bond_info->rx6_hashtbl_used_head; + for (; hash_index != RLB_NULL_INDEX; + hash_index = client_info->used_next) { + client_info = &bond_info->rx6_hashtbl[hash_index]; + seq_printf(m, "%-40pI6 %-40pI6 %-17pM %-17pM %s\n", + &client_info->ip6_src, + &client_info->ip6_dst, + &client_info->mac_dst, + &client_info->mac_src, + client_info->slave->dev->name); + } + spin_unlock_bh(&bond->mode_lock); return 0; diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 15eddca7b4b6..b6252b181940 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1510,8 +1510,8 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb) struct sk_buff *skb = *pskb; struct slave *slave; struct bonding *bond; - int (*recv_probe)(const struct sk_buff *, struct bonding *, - struct slave *); + int (*recv_probe)(struct sk_buff *skb, struct bonding *bond, + struct slave *slave); int ret = RX_HANDLER_ANOTHER; skb = skb_share_check(skb, GFP_ATOMIC); @@ -3228,7 +3228,7 @@ static int bond_na_rcv(const struct sk_buff *skb, struct bonding *bond, } #endif -int bond_rcv_validate(const struct sk_buff *skb, struct bonding *bond, +int bond_rcv_validate(struct sk_buff *skb, struct bonding *bond, struct slave *slave) { #if IS_ENABLED(CONFIG_IPV6) diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index c89639381eca..70f4327dbd2a 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -347,7 +347,7 @@ static void do_neigh_solicit(struct usbnet *dev, u8 *buf, u16 tci) is_router /* router */, true /* solicited */, false /* override */, - true /* inc_opt */); + true /* inc_opt */, NULL); out: dev_put(netdev); } diff --git a/include/net/bond_3ad.h b/include/net/bond_3ad.h index 184105d68294..51886d9c928d 100644 --- a/include/net/bond_3ad.h +++ b/include/net/bond_3ad.h @@ -300,7 +300,7 @@ void bond_3ad_handle_link_change(struct slave *slave, char link); int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info); int __bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info); -int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond, +int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct bonding *bond, struct slave *slave); int bond_3ad_set_carrier(struct bonding *bond); void bond_3ad_update_lacp_active(struct bonding *bond); diff --git a/include/net/bond_alb.h b/include/net/bond_alb.h index 191c36afa1f4..b1a572eead31 100644 --- a/include/net/bond_alb.h +++ b/include/net/bond_alb.h @@ -94,6 +94,8 @@ struct tlb_client_info { struct rlb_client_info { __be32 ip_src; /* the server IP address */ __be32 ip_dst; /* the client IP address */ + struct in6_addr ip6_src; + struct in6_addr ip6_dst; u8 mac_src[ETH_ALEN]; /* the server MAC address */ u8 mac_dst[ETH_ALEN]; /* the client MAC address */ @@ -131,10 +133,13 @@ struct alb_bond_info { /* -------- rlb parameters -------- */ int rlb_enabled; struct rlb_client_info *rx_hashtbl; /* Receive hash table */ + struct rlb_client_info *rx6_hashtbl; /* Receive hash table */ u32 rx_hashtbl_used_head; + u32 rx6_hashtbl_used_head; u8 rx_ntt; /* flag - need to transmit * to all rx clients */ + u8 rx6_ntt; struct slave *rx_slave;/* last slave to xmit from */ u8 primary_is_promisc; /* boolean */ u32 rlb_promisc_timeout_counter;/* counts primary @@ -144,6 +149,8 @@ struct alb_bond_info { u32 rlb_update_retry_counter;/* counter of retries * of client update */ + u32 rlb6_update_delay_counter; + u32 rlb6_update_retry_counter; u8 rlb_rebalance; /* flag - indicates that the * rx traffic should be * rebalanced diff --git a/include/net/bonding.h b/include/net/bonding.h index b14f4c0b4e9e..552bce0168d1 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -220,8 +220,8 @@ struct bonding { struct bond_up_slave __rcu *all_slaves; bool force_primary; s32 slave_cnt; /* never change this value outside the attach/detach wrappers */ - int (*recv_probe)(const struct sk_buff *, struct bonding *, - struct slave *); + int (*recv_probe)(struct sk_buff *skb, struct bonding *bond, + struct slave *slave); /* mode_lock is used for mode-specific locking needs, currently used by: * 3ad mode (4) - protect against running bond_3ad_unbind_slave() and * bond_3ad_state_machine_handler() concurrently and also @@ -639,7 +639,7 @@ struct bond_net { struct class_attribute class_attr_bonding_masters; }; -int bond_rcv_validate(const struct sk_buff *skb, struct bonding *bond, struct slave *slave); +int bond_rcv_validate(struct sk_buff *skb, struct bonding *bond, struct slave *slave); netdev_tx_t bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev); int bond_create(struct net *net, const char *name); int bond_create_sysfs(struct bond_net *net); diff --git a/include/net/ipv6_stubs.h b/include/net/ipv6_stubs.h index 45e0339be6fa..2b64ea6590b6 100644 --- a/include/net/ipv6_stubs.h +++ b/include/net/ipv6_stubs.h @@ -56,7 +56,8 @@ struct ipv6_stub { void (*udpv6_encap_enable)(void); void (*ndisc_send_na)(struct net_device *dev, const struct in6_addr *daddr, const struct in6_addr *solicited_addr, - bool router, bool solicited, bool override, bool inc_opt); + bool router, bool solicited, bool override, + bool inc_opt, void *data); #if IS_ENABLED(CONFIG_XFRM) void (*xfrm6_local_rxpmtu)(struct sk_buff *skb, u32 mtu); int (*xfrm6_udp_encap_rcv)(struct sock *sk, struct sk_buff *skb); diff --git a/include/net/ndisc.h b/include/net/ndisc.h index da7eec8669ec..e71702a44a3d 100644 --- a/include/net/ndisc.h +++ b/include/net/ndisc.h @@ -107,6 +107,12 @@ struct nd_opt_hdr { __u8 nd_opt_len; } __packed; +struct nd_sendinfo { + __u16 vlanid; + void *mac_dst; + const void *mac_src; +}; + /* ND options */ struct ndisc_options { struct nd_opt_hdr *nd_opt_array[__ND_OPT_ARRAY_MAX]; @@ -460,7 +466,8 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, const struct in6_addr *daddr); void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr, const struct in6_addr *solicited_addr, - bool router, bool solicited, bool override, bool inc_opt); + bool router, bool solicited, bool override, bool inc_opt, + void *data); void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index b22504176588..6825d70c34fb 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -975,6 +975,7 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp) kfree_rcu(ifp, rcu); } +EXPORT_SYMBOL(inet6_ifa_finish_destroy); static void ipv6_link_dev_addr(struct inet6_dev *idev, struct inet6_ifaddr *ifp) @@ -2037,6 +2038,7 @@ struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *add return result; } +EXPORT_SYMBOL(ipv6_get_ifaddr); /* Gets referenced address, destroys ifaddr */ @@ -4217,7 +4219,7 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp, bool bump_id, ndisc_send_na(dev, &in6addr_linklocal_allnodes, &ifp->addr, /*router=*/ !!ifp->idev->cnf.forwarding, /*solicited=*/ false, /*override=*/ true, - /*inc_opt=*/ true); + /*inc_opt=*/ true, NULL); } if (send_rs) { diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index fcb288b0ae13..47875aab86e5 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -518,29 +518,37 @@ EXPORT_SYMBOL(ndisc_send_skb); void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr, const struct in6_addr *solicited_addr, - bool router, bool solicited, bool override, bool inc_opt) + bool router, bool solicited, bool override, bool inc_opt, + void *data) { struct sk_buff *skb; struct in6_addr tmpaddr; struct inet6_ifaddr *ifp; const struct in6_addr *src_addr; struct nd_msg *msg; + struct nd_sendinfo *sendinfo = data; + struct net *net = dev_net(dev); + struct sock *sk = net->ipv6.ndisc_sk; int optlen = 0; - /* for anycast or proxy, solicited_addr != src_addr */ - ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1); - if (ifp) { - src_addr = solicited_addr; - if (ifp->flags & IFA_F_OPTIMISTIC) - override = false; - inc_opt |= ifp->idev->cnf.force_tllao; - in6_ifa_put(ifp); + if (!sendinfo) { + /* for anycast or proxy, solicited_addr != src_addr */ + ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1); + if (ifp) { + src_addr = solicited_addr; + if (ifp->flags & IFA_F_OPTIMISTIC) + override = false; + inc_opt |= ifp->idev->cnf.force_tllao; + in6_ifa_put(ifp); + } else { + if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr, + inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs, + &tmpaddr)) + return; + src_addr = &tmpaddr; + } } else { - if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr, - inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs, - &tmpaddr)) - return; - src_addr = &tmpaddr; + src_addr = solicited_addr; } if (!dev->addr_len) @@ -568,8 +576,28 @@ void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr, ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, dev->dev_addr, NDISC_NEIGHBOUR_ADVERTISEMENT); + if (!sendinfo) { + ndisc_send_skb(skb, daddr, src_addr); + } else { + if (sendinfo->vlanid) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), + sendinfo->vlanid); + + msg->icmph.icmp6_cksum = csum_ipv6_magic(src_addr, daddr, skb->len, + IPPROTO_ICMPV6, + csum_partial(&msg->icmph, + skb->len, 0)); - ndisc_send_skb(skb, daddr, src_addr); + ip6_nd_hdr(skb, src_addr, daddr, inet6_sk(sk)->hop_limit, skb->len); + + skb->protocol = htons(ETH_P_IPV6); + skb->dev = dev; + if (dev_hard_header(skb, dev, ETH_P_IPV6, sendinfo->mac_dst, + sendinfo->mac_src, skb->len) < 0) + return; + + dev_queue_xmit(skb); + } } static void ndisc_send_unsol_na(struct net_device *dev) @@ -591,7 +619,7 @@ static void ndisc_send_unsol_na(struct net_device *dev) ndisc_send_na(dev, &in6addr_linklocal_allnodes, &ifa->addr, /*router=*/ !!idev->cnf.forwarding, /*solicited=*/ false, /*override=*/ true, - /*inc_opt=*/ true); + /*inc_opt=*/ true, NULL); } read_unlock_bh(&idev->lock); @@ -932,7 +960,7 @@ static void ndisc_recv_ns(struct sk_buff *skb) if (dad) { ndisc_send_na(dev, &in6addr_linklocal_allnodes, &msg->target, - !!is_router, false, (ifp != NULL), true); + !!is_router, false, ifp, true, NULL); goto out; } @@ -954,7 +982,7 @@ static void ndisc_recv_ns(struct sk_buff *skb) NDISC_NEIGHBOUR_SOLICITATION, &ndopts); if (neigh || !dev->header_ops) { ndisc_send_na(dev, saddr, &msg->target, !!is_router, - true, (ifp != NULL && inc), inc); + true, (ifp && inc), inc, NULL); if (neigh) neigh_release(neigh); } base-commit: c84d86a0295c24487db5b7db1a61d9c0eddfbb66 -- 2.27.0