From nobody Tue Jun 23 04:09:14 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 46587C433EF for ; Fri, 11 Mar 2022 03:00:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1345850AbiCKDBD (ORCPT ); Thu, 10 Mar 2022 22:01:03 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34354 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S243959AbiCKDA7 (ORCPT ); Thu, 10 Mar 2022 22:00:59 -0500 X-Greylist: delayed 550 seconds by postgrey-1.37 at lindbergh.monkeyblade.net; Thu, 10 Mar 2022 18:59:56 PST Received: from chinatelecom.cn (prt-mail.chinatelecom.cn [42.123.76.228]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 5C47B9398F; Thu, 10 Mar 2022 18:59:56 -0800 (PST) HMM_SOURCE_IP: 172.18.0.218:37692.1206753115 HMM_ATTACHE_NUM: 0000 HMM_SOURCE_TYPE: SMTP Received: from clientip-202.80.192.39 (unknown [172.18.0.218]) by chinatelecom.cn (HERMES) with SMTP id 0687D280128; Fri, 11 Mar 2022 10:50:52 +0800 (CST) X-189-SAVE-TO-SEND: +sunshouxin@chinatelecom.cn Received: from ([172.18.0.218]) by app0025 with ESMTP id e12254a66a0d46dabb1c2f778c4d84bf for j.vosburgh@gmail.com; Fri, 11 Mar 2022 10:50:57 CST X-Transaction-ID: e12254a66a0d46dabb1c2f778c4d84bf X-Real-From: sunshouxin@chinatelecom.cn X-Receive-IP: 172.18.0.218 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 Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, huyd12@chinatelecom.cn, sunshouxin@chinatelecom.cn Subject: [PATCH 1/3] net:ipv6:Add ndisc_bond_send_na to support sending na by slave directly Date: Thu, 10 Mar 2022 21:49:56 -0500 Message-Id: <20220311024958.7458-2-sunshouxin@chinatelecom.cn> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20220311024958.7458-1-sunshouxin@chinatelecom.cn> References: <20220311024958.7458-1-sunshouxin@chinatelecom.cn> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Add ndisc_bond_send_na to support sending na by slave directly and export it for bonding usage. Suggested-by: Hu Yadi Signed-off-by: Sun Shouxin Reported-by: kernel test robot --- include/net/ndisc.h | 6 +++++ net/ipv6/ndisc.c | 61 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/include/net/ndisc.h b/include/net/ndisc.h index da7eec8669ec..317bcb29c795 100644 --- a/include/net/ndisc.h +++ b/include/net/ndisc.h @@ -471,6 +471,12 @@ void ndisc_update(const struct net_device *dev, struct= neighbour *neigh, const u8 *lladdr, u8 new, u32 flags, u8 icmp6_type, struct ndisc_options *ndopts); =20 +void ndisc_bond_send_na(struct net_device *dev, const struct in6_addr *dad= dr, + const struct in6_addr *solicited_addr, bool router, + bool solicited, bool override, bool inc_opt, + unsigned short vlan_id, const void *mac_dst, + const void *mac_src); + /* * IGMP */ diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index fcb288b0ae13..c59a110e9b10 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -572,6 +572,67 @@ void ndisc_send_na(struct net_device *dev, const struc= t in6_addr *daddr, ndisc_send_skb(skb, daddr, src_addr); } =20 +void ndisc_bond_send_na(struct net_device *dev, const struct in6_addr *dad= dr, + const struct in6_addr *solicited_addr, + bool router, bool solicited, bool override, + bool inc_opt, unsigned short vlan_id, + const void *mac_dst, const void *mac_src) +{ + struct sk_buff *skb; + const struct in6_addr *src_addr; + struct nd_msg *msg; + struct net *net =3D dev_net(dev); + struct sock *sk =3D net->ipv6.ndisc_sk; + int optlen =3D 0; + int ret; + + src_addr =3D solicited_addr; + if (!dev->addr_len) + inc_opt =3D false; + if (inc_opt) + optlen +=3D ndisc_opt_addr_space(dev, + NDISC_NEIGHBOUR_ADVERTISEMENT); + + skb =3D ndisc_alloc_skb(dev, sizeof(*msg) + optlen); + if (!skb) + return; + + msg =3D skb_put(skb, sizeof(*msg)); + *msg =3D (struct nd_msg) { + .icmph =3D { + .icmp6_type =3D NDISC_NEIGHBOUR_ADVERTISEMENT, + .icmp6_router =3D router, + .icmp6_solicited =3D solicited, + .icmp6_override =3D override, + }, + .target =3D *solicited_addr, + }; + + if (inc_opt) + ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, + dev->dev_addr, + NDISC_NEIGHBOUR_ADVERTISEMENT); + + if (vlan_id) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), + vlan_id); + + msg->icmph.icmp6_cksum =3D csum_ipv6_magic(src_addr, daddr, skb->len, + IPPROTO_ICMPV6, + csum_partial(&msg->icmph, + skb->len, 0)); + + ip6_nd_hdr(skb, src_addr, daddr, inet6_sk(sk)->hop_limit, skb->len); + + skb->protocol =3D htons(ETH_P_IPV6); + skb->dev =3D dev; + if (dev_hard_header(skb, dev, ETH_P_IPV6, mac_dst, mac_src, skb->len) < 0) + return; + + ret =3D dev_queue_xmit(skb); +} +EXPORT_SYMBOL(ndisc_bond_send_na); + static void ndisc_send_unsol_na(struct net_device *dev) { struct inet6_dev *idev; --=20 2.27.0 From nobody Tue Jun 23 04:09:14 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6209AC433F5 for ; Fri, 11 Mar 2022 03:00:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1345875AbiCKDBM (ORCPT ); Thu, 10 Mar 2022 22:01:12 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34428 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1345837AbiCKDBA (ORCPT ); Thu, 10 Mar 2022 22:01:00 -0500 Received: from chinatelecom.cn (prt-mail.chinatelecom.cn [42.123.76.228]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 81D0D98586; Thu, 10 Mar 2022 18:59:58 -0800 (PST) HMM_SOURCE_IP: 172.18.0.218:37692.1206753115 HMM_ATTACHE_NUM: 0000 HMM_SOURCE_TYPE: SMTP Received: from clientip-202.80.192.39 (unknown [172.18.0.218]) by chinatelecom.cn (HERMES) with SMTP id 4D40B280146; Fri, 11 Mar 2022 10:51:04 +0800 (CST) X-189-SAVE-TO-SEND: +sunshouxin@chinatelecom.cn Received: from ([172.18.0.218]) by app0025 with ESMTP id 49ec3a755d724745a73cf7af43972e6f for j.vosburgh@gmail.com; Fri, 11 Mar 2022 10:51:10 CST X-Transaction-ID: 49ec3a755d724745a73cf7af43972e6f X-Real-From: sunshouxin@chinatelecom.cn X-Receive-IP: 172.18.0.218 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 Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, huyd12@chinatelecom.cn, sunshouxin@chinatelecom.cn Subject: [PATCH 2/3] net:ipv6:Export inet6_ifa_finish_destroy and ipv6_get_ifaddr Date: Thu, 10 Mar 2022 21:49:57 -0500 Message-Id: <20220311024958.7458-3-sunshouxin@chinatelecom.cn> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20220311024958.7458-1-sunshouxin@chinatelecom.cn> References: <20220311024958.7458-1-sunshouxin@chinatelecom.cn> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Export inet6_ifa_finish_destroy and ipv6_get_ifaddr for bonding usage. Signed-off-by: Sun Shouxin --- net/ipv6/addrconf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index b22504176588..12004b8a46b5 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -975,6 +975,7 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp) =20 kfree_rcu(ifp, rcu); } +EXPORT_SYMBOL(inet6_ifa_finish_destroy); =20 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 =20 return result; } +EXPORT_SYMBOL(ipv6_get_ifaddr); =20 /* Gets referenced address, destroys ifaddr */ =20 --=20 2.27.0 From nobody Tue Jun 23 04:09:14 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 094B2C433F5 for ; Fri, 11 Mar 2022 03:00:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1345882AbiCKDBQ (ORCPT ); Thu, 10 Mar 2022 22:01:16 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34468 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1345841AbiCKDBB (ORCPT ); Thu, 10 Mar 2022 22:01:01 -0500 Received: from chinatelecom.cn (prt-mail.chinatelecom.cn [42.123.76.228]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 6242795490; Thu, 10 Mar 2022 18:59:56 -0800 (PST) HMM_SOURCE_IP: 172.18.0.218:37692.1206753115 HMM_ATTACHE_NUM: 0000 HMM_SOURCE_TYPE: SMTP Received: from clientip-202.80.192.39 (unknown [172.18.0.218]) by chinatelecom.cn (HERMES) with SMTP id 289C128014C; Fri, 11 Mar 2022 10:51:14 +0800 (CST) X-189-SAVE-TO-SEND: +sunshouxin@chinatelecom.cn Received: from ([172.18.0.218]) by app0025 with ESMTP id a86d3c47e50744f28e213495cffc9689 for j.vosburgh@gmail.com; Fri, 11 Mar 2022 10:51:18 CST X-Transaction-ID: a86d3c47e50744f28e213495cffc9689 X-Real-From: sunshouxin@chinatelecom.cn X-Receive-IP: 172.18.0.218 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 Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, huyd12@chinatelecom.cn, sunshouxin@chinatelecom.cn Subject: [PATCH 3/3] net:bonding:Add support for IPV6 RLB to balance-alb mode Date: Thu, 10 Mar 2022 21:49:58 -0500 Message-Id: <20220311024958.7458-4-sunshouxin@chinatelecom.cn> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20220311024958.7458-1-sunshouxin@chinatelecom.cn> References: <20220311024958.7458-1-sunshouxin@chinatelecom.cn> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" This patch is implementing IPV6 RLB for balance-alb mode. Suggested-by: Hu Yadi Signed-off-by: Sun Shouxin Reported-by: kernel test robot --- drivers/net/bonding/bond_3ad.c | 2 +- drivers/net/bonding/bond_alb.c | 588 ++++++++++++++++++++++++++++- drivers/net/bonding/bond_debugfs.c | 14 + drivers/net/bonding/bond_main.c | 6 +- include/net/bond_3ad.h | 2 +- include/net/bond_alb.h | 7 + include/net/bonding.h | 6 +- 7 files changed, 611 insertions(+), 14 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; } =20 -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..b17e2c091ea6 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include =20 static const u8 mac_v6_allmcast[ETH_ALEN + 2] __long_aligned =3D { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 @@ -57,6 +59,13 @@ static void rlb_purge_src_ip(struct bonding *bond, struc= t 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 *bon= d); +static int rlb_recv(struct sk_buff *skb, struct bonding *bond, + struct slave *slave); =20 static inline u8 _simple_hash(const u8 *hash_start, int hash_size) { @@ -269,7 +278,7 @@ static void rlb_update_entry_from_arp(struct bonding *b= ond, struct arp_pkt *arp) spin_unlock_bh(&bond->mode_lock); } =20 -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 +424,31 @@ static void rlb_clear_slave(struct bonding *bond, stru= ct slave *slave) } } =20 + rx_hash_table =3D bond_info->rx6_hashtbl; + index =3D bond_info->rx6_hashtbl_used_head; + for (; index !=3D RLB_NULL_INDEX; index =3D next_index) { + next_index =3D rx_hash_table[index].used_next; + if (rx_hash_table[index].slave =3D=3D slave) { + struct slave *assigned_slave =3D rlb_next_rx_slave(bond); + + if (assigned_slave) { + u8 mac_dst[ETH_ALEN]; + + rx_hash_table[index].slave =3D 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 =3D 1; + bond_info->rx6_ntt =3D 1; + bond_info->rlb6_update_retry_counter =3D + RLB_UPDATE_RETRY; + } + } else { /* there is no active slave */ + rx_hash_table[index].slave =3D NULL; + } + } + } + spin_unlock_bh(&bond->mode_lock); =20 if (slave !=3D rtnl_dereference(bond->curr_active_slave)) @@ -704,7 +738,7 @@ static void rlb_rebalance(struct bonding *bond) struct alb_bond_info *bond_info =3D &(BOND_ALB_INFO(bond)); struct slave *assigned_slave; struct rlb_client_info *client_info; - int ntt; + int ntt, ntt_ip6; u32 hash_index; =20 spin_lock_bh(&bond->mode_lock); @@ -724,9 +758,27 @@ static void rlb_rebalance(struct bonding *bond) } } =20 + ntt_ip6 =3D 0; + hash_index =3D bond_info->rx6_hashtbl_used_head; + for (; hash_index !=3D RLB_NULL_INDEX; + hash_index =3D client_info->used_next) { + client_info =3D &bond_info->rx6_hashtbl[hash_index]; + assigned_slave =3D __rlb_next_rx_slave(bond); + if (assigned_slave && client_info->slave !=3D assigned_slave) { + client_info->slave =3D assigned_slave; + if (!is_zero_ether_addr(client_info->mac_dst)) { + client_info->ntt =3D 1; + ntt_ip6 =3D 1; + } + } + } + /* update the team's flag only after the whole iteration */ if (ntt) bond_info->rx_ntt =3D 1; + + if (ntt_ip6) + bond_info->rx6_ntt =3D 1; spin_unlock_bh(&bond->mode_lock); } =20 @@ -846,6 +898,7 @@ static int rlb_initialize(struct bonding *bond) { struct alb_bond_info *bond_info =3D &(BOND_ALB_INFO(bond)); struct rlb_client_info *new_hashtbl; + struct rlb_client_info *new6_hashtbl; int size =3D RLB_HASH_TABLE_SIZE * sizeof(struct rlb_client_info); int i; =20 @@ -853,19 +906,29 @@ static int rlb_initialize(struct bonding *bond) if (!new_hashtbl) return -1; =20 + new6_hashtbl =3D kmalloc(size, GFP_KERNEL); + if (!new6_hashtbl) { + kfree(new_hashtbl); + return -1; + } + spin_lock_bh(&bond->mode_lock); =20 bond_info->rx_hashtbl =3D new_hashtbl; + bond_info->rx6_hashtbl =3D new6_hashtbl; =20 bond_info->rx_hashtbl_used_head =3D RLB_NULL_INDEX; + bond_info->rx6_hashtbl_used_head =3D RLB_NULL_INDEX; =20 - for (i =3D 0; i < RLB_HASH_TABLE_SIZE; i++) + for (i =3D 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); + } =20 spin_unlock_bh(&bond->mode_lock); =20 /* register to receive ARPs */ - bond->recv_probe =3D rlb_arp_recv; + bond->recv_probe =3D rlb_recv; =20 return 0; } @@ -880,6 +943,10 @@ static void rlb_deinitialize(struct bonding *bond) bond_info->rx_hashtbl =3D NULL; bond_info->rx_hashtbl_used_head =3D RLB_NULL_INDEX; =20 + kfree(bond_info->rx6_hashtbl); + bond_info->rx6_hashtbl =3D NULL; + bond_info->rx6_hashtbl_used_head =3D RLB_NULL_INDEX; + spin_unlock_bh(&bond->mode_lock); } =20 @@ -901,9 +968,394 @@ static void rlb_clear_vlan(struct bonding *bond, unsi= gned short vlan_id) curr_index =3D next_index; } =20 + curr_index =3D bond_info->rx6_hashtbl_used_head; + while (curr_index !=3D RLB_NULL_INDEX) { + struct rlb_client_info *curr =3D &bond_info->rx6_hashtbl[curr_index]; + u32 next_index =3D bond_info->rx6_hashtbl[curr_index].used_next; + + if (curr->vlan_id =3D=3D vlan_id) + rlb6_delete_table_entry(bond, curr_index); + + curr_index =3D next_index; + } + spin_unlock_bh(&bond->mode_lock); } =20 +/*********************** ipv6 rlb specific functions *********************= ******/ +void rlb6_update_client(struct rlb_client_info *client_info) +{ + int i; + + if (!client_info->slave || !is_valid_ether_addr(client_info->mac_dst)) + return; + + for (i =3D 0; i < RLB_ARP_BURST_SIZE; i++) { + ndisc_bond_send_na(client_info->slave->dev, + &client_info->ip6_dst, + &client_info->ip6_src, + false, false, true, true, + client_info->vlan_id, + client_info->mac_dst, + client_info->slave->dev->dev_addr); + } +} + +static void rlb6_update_rx_clients(struct bonding *bond) +{ + struct alb_bond_info *bond_info =3D &(BOND_ALB_INFO(bond)); + struct rlb_client_info *client_info; + u32 hash_index; + + spin_lock_bh(&bond->mode_lock); + + hash_index =3D bond_info->rx6_hashtbl_used_head; + for (; hash_index !=3D RLB_NULL_INDEX; + hash_index =3D client_info->used_next) { + client_info =3D &bond_info->rx6_hashtbl[hash_index]; + if (client_info->ntt) { + rlb6_update_client(client_info); + if (bond_info->rlb6_update_retry_counter =3D=3D 0) + client_info->ntt =3D 0; + } + } + + bond_info->rlb6_update_delay_counter =3D 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 =3D &(BOND_ALB_INFO(bond)); + u32 next_index =3D bond_info->rx6_hashtbl[index].used_next; + u32 prev_index =3D bond_info->rx6_hashtbl[index].used_prev; + + if (index =3D=3D bond_info->rx6_hashtbl_used_head) + bond_info->rx6_hashtbl_used_head =3D next_index; + + if (next_index !=3D RLB_NULL_INDEX) + bond_info->rx6_hashtbl[next_index].used_prev =3D prev_index; + + if (prev_index !=3D RLB_NULL_INDEX) + bond_info->rx6_hashtbl[prev_index].used_next =3D next_index; +} + +static void rlb6_src_link(struct bonding *bond, u32 ip_src_hash, + u32 ip_dst_hash) +{ + struct alb_bond_info *bond_info =3D &(BOND_ALB_INFO(bond)); + u32 next; + + bond_info->rx6_hashtbl[ip_dst_hash].src_prev =3D ip_src_hash; + next =3D bond_info->rx6_hashtbl[ip_src_hash].src_first; + bond_info->rx6_hashtbl[ip_dst_hash].src_next =3D next; + if (next !=3D RLB_NULL_INDEX) + bond_info->rx6_hashtbl[next].src_prev =3D ip_dst_hash; + bond_info->rx6_hashtbl[ip_src_hash].src_first =3D ip_dst_hash; +} + +static void rlb6_src_unlink(struct bonding *bond, u32 index) +{ + struct alb_bond_info *bond_info =3D &(BOND_ALB_INFO(bond)); + u32 next_index =3D bond_info->rx6_hashtbl[index].src_next; + u32 prev_index =3D bond_info->rx6_hashtbl[index].src_prev; + + bond_info->rx6_hashtbl[index].src_next =3D RLB_NULL_INDEX; + bond_info->rx6_hashtbl[index].src_prev =3D RLB_NULL_INDEX; + + if (next_index !=3D RLB_NULL_INDEX) + bond_info->rx6_hashtbl[next_index].src_prev =3D prev_index; + + if (prev_index =3D=3D RLB_NULL_INDEX) + return; + + if (bond_info->rx6_hashtbl[prev_index].src_first =3D=3D index) + bond_info->rx6_hashtbl[prev_index].src_first =3D next_index; + else + bond_info->rx6_hashtbl[prev_index].src_next =3D next_index; +} + +static void rlb6_req_update_slave_clients(struct bonding *bond, + struct slave *slave) +{ + struct alb_bond_info *bond_info =3D &(BOND_ALB_INFO(bond)); + struct rlb_client_info *client_info; + u32 hash_index; + int ntt =3D 0; + + spin_lock_bh(&bond->mode_lock); + + hash_index =3D bond_info->rx6_hashtbl_used_head; + for (; hash_index !=3D RLB_NULL_INDEX; + hash_index =3D client_info->used_next) { + client_info =3D &bond_info->rx6_hashtbl[hash_index]; + if (client_info->slave =3D=3D slave && + is_valid_ether_addr(client_info->mac_dst)) { + client_info->ntt =3D 1; + ntt =3D 1; + } + } + + if (ntt) { + bond_info->rx6_ntt =3D 1; + bond_info->rlb6_update_retry_counter =3D + 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 =3D &(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 =3D 0; + + spin_lock(&bond->mode_lock); + + msg =3D (struct nd_msg *)skb_transport_header(skb); + eth_data =3D eth_hdr(skb); + curr_active_slave =3D rcu_dereference(bond->curr_active_slave); + + if (type =3D=3D NDISC_NEIGHBOUR_SOLICITATION) + dst_ip =3D (u8 *)msg->target.s6_addr; + else + dst_ip =3D (u8 *)ip6hdr->daddr.s6_addr; + + hash_index =3D _simple_hash(dst_ip, + sizeof(struct in6_addr)); + client_info =3D &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 =3D client_info->slave; + if (assigned_slave) { + spin_unlock(&bond->mode_lock); + return assigned_slave; + } + } else { + if (curr_active_slave && + curr_active_slave !=3D client_info->slave) { + client_info->slave =3D curr_active_slave; + rlb6_update_client(client_info); + } + } + } + + /* assign a new slave */ + assigned_slave =3D __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 =3D _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 =3D assigned_slave; + + if (is_valid_ether_addr(client_info->mac_dst)) { + client_info->ntt =3D 1; + bond->alb_info.rx6_ntt =3D 1; + } else { + client_info->ntt =3D 0; + } + + if (vlan_get_tag(skb, &client_info->vlan_id)) + client_info->vlan_id =3D 0; + + if (!client_info->assigned) { + u32 prev_tbl_head =3D bond_info->rx6_hashtbl_used_head; + + bond_info->rx6_hashtbl_used_head =3D hash_index; + client_info->used_next =3D prev_tbl_head; + if (prev_tbl_head !=3D RLB_NULL_INDEX) + bond_info->rx6_hashtbl[prev_tbl_head].used_prev =3D hash_index; + client_info->assigned =3D 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 =3D NULL; + struct ipv6hdr *ip6hdr; + struct icmp6hdr *hdr; + u8 *lladdr; + + if (!pskb_network_may_pull(skb, sizeof(*ip6hdr))) + return NULL; + + ip6hdr =3D ipv6_hdr(skb); + if (ip6hdr->nexthdr !=3D IPPROTO_ICMPV6) + return NULL; + + if (!pskb_network_may_pull(skb, sizeof(*ip6hdr) + sizeof(*hdr))) + return NULL; + + hdr =3D icmp6_hdr(skb); + + if (hdr->icmp6_type !=3D NDISC_NEIGHBOUR_ADVERTISEMENT && + hdr->icmp6_type !=3D NDISC_NEIGHBOUR_SOLICITATION) { + return NULL; + } + + lladdr =3D alb_get_lladdr(skb); + if (!lladdr) + return NULL; + + if (!bond_slave_has_mac_rx(bond, lladdr)) + return NULL; + + tx_slave =3D 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 =3D &(BOND_ALB_INFO(bond)); + struct rlb_client_info *client_info; + u32 hash_index; + + spin_lock_bh(&bond->mode_lock); + + hash_index =3D _simple_hash(ip6hdr->saddr.s6_addr, + sizeof(ip6hdr->saddr.s6_addr)); + client_info =3D &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)); + client_info->ntt =3D 1; + bond_info->rx6_ntt =3D 1; + } + spin_unlock_bh(&bond->mode_lock); +} + +static void rlb6_delete_table_entry(struct bonding *bond, u32 index) +{ + struct alb_bond_info *bond_info =3D &(BOND_ALB_INFO(bond)); + struct rlb_client_info *entry =3D &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 =3D &(BOND_ALB_INFO(bond)); + struct rlb_client_info *client_info; + u32 ip_src_hash =3D _simple_hash((u8 *)ip6hdr->saddr.s6_addr, + sizeof(ip6hdr->saddr.s6_addr)); + u32 index, next_index; + + spin_lock_bh(&bond->mode_lock); + + index =3D bond_info->rx6_hashtbl[ip_src_hash].src_first; + while (index !=3D RLB_NULL_INDEX) { + client_info =3D &bond_info->rx6_hashtbl[index]; + next_index =3D 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 =3D 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 =3D ipv6_hdr(skb); + + ifp =3D 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 =3D (struct nd_msg *)skb_transport_header(skb); + lladdr =3D alb_get_lladdr(skb); + if (!lladdr) + return RX_HANDLER_ANOTHER; + + rlb6_purge_src_ip(bond, ip6hdr, lladdr); + + if (msg->icmph.icmp6_type =3D=3D 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 =3D=3D 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 *********************/ =20 static void alb_send_lp_vid(struct slave *slave, const u8 mac_addr[], @@ -1068,6 +1520,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 =3D slave1; @@ -1080,6 +1533,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 =3D slave2; @@ -1291,6 +1745,111 @@ static bool alb_determine_nd(struct sk_buff *skb, s= truct bonding *bond) hdr->icmp6_type =3D=3D NDISC_NEIGHBOUR_SOLICITATION; } =20 +static bool alb_determine_ipv6_nd(struct sk_buff *skb, struct bonding *bon= d) +{ + if (skb->protocol =3D=3D htons(ETH_P_IPV6)) { + if (skb_vlan_tag_present(skb)) + skb->transport_header =3D 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 =3D (struct nd_msg *)skb_transport_header(skb); + struct nd_opt_hdr *nd_opt =3D (struct nd_opt_hdr *)msg->opt; + struct net_device *dev =3D skb->dev; + struct icmp6hdr *icmp6h =3D icmp6_hdr(skb); + struct ipv6hdr *ip6hdr =3D ipv6_hdr(skb); + u8 *lladdr =3D NULL; + u32 ndoptlen =3D 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 =3D ndisc_opt_addr_data(nd_opt, dev); + break; + + default: + lladdr =3D NULL; + break; + } + + l =3D nd_opt->nd_opt_len << 3; + + if (ndoptlen < l || l =3D=3D 0) + return; + + if (lladdr) { + memcpy(lladdr, data, dev->addr_len); + icmp6h->icmp6_cksum =3D 0; + + icmp6h->icmp6_cksum =3D csum_ipv6_magic(&ip6hdr->saddr, + &ip6hdr->daddr, + ntohs(ip6hdr->payload_len), + IPPROTO_ICMPV6, + csum_partial(icmp6h, + ntohs(ip6hdr->payload_len), + 0)); + return; + } + ndoptlen -=3D l; + nd_opt =3D ((void *)nd_opt) + l; + } +} + +static u8 *alb_get_lladdr(struct sk_buff *skb) +{ + struct nd_msg *msg =3D (struct nd_msg *)skb_transport_header(skb); + struct nd_opt_hdr *nd_opt =3D (struct nd_opt_hdr *)msg->opt; + struct net_device *dev =3D skb->dev; + u8 *lladdr =3D NULL; + u32 ndoptlen =3D 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 =3D ndisc_opt_addr_data(nd_opt, dev); + break; + + default: + break; + } + + l =3D nd_opt->nd_opt_len << 3; + + if (ndoptlen < l || l =3D=3D 0) + return NULL; + + if (lladdr) + return lladdr; + + ndoptlen -=3D l; + nd_opt =3D ((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 !=3D 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 ************************/ =20 int bond_alb_initialize(struct bonding *bond, int rlb_enabled) @@ -1457,12 +2016,17 @@ struct slave *bond_xmit_alb_slave_get(struct bondin= g *bond, break; } =20 - if (alb_determine_nd(skb, bond)) { + tx_slave =3D rlb_nd_xmit(skb, bond); + if (tx_slave) { + do_tx_balance =3D false; + break; + } + + if (!pskb_network_may_pull(skb, sizeof(*ip6hdr))) { do_tx_balance =3D false; break; } =20 - /* 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 +2176,17 @@ void bond_alb_monitor(struct work_struct *work) bond_info->rx_ntt =3D 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 =3D 0; + } + } } rcu_read_unlock(); re_arm: @@ -1812,6 +2387,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); } } =20 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); } =20 + seq_puts(m, "SourceIP DestinationIP = Destination MAC Src MAC DEV\n"); + + hash_index =3D bond_info->rx6_hashtbl_used_head; + for (; hash_index !=3D RLB_NULL_INDEX; + hash_index =3D client_info->used_next) { + client_info =3D &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); =20 return 0; diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_mai= n.c index 55e0ba2a163d..e487ac5bc533 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 s= k_buff **pskb) struct sk_buff *skb =3D *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 =3D RX_HANDLER_ANOTHER; =20 skb =3D skb_share_check(skb, GFP_ATOMIC); @@ -3228,7 +3228,7 @@ static int bond_na_rcv(const struct sk_buff *skb, str= uct bonding *bond, } #endif =20 -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/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, c= har 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 */ =20 @@ -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 d0dfe727e0b1..c71dc175178d 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; }; =20 -int bond_rcv_validate(const struct sk_buff *skb, struct bonding *bond, str= uct slave *slave); +int bond_rcv_validate(struct sk_buff *skb, struct bonding *bond, struct sl= ave *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); --=20 2.27.0