diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst index a66054d0763a69d9e7cfae8e6242ac6d254e9169..12fa38639102995106f7c05429978a468d8d0864 100644 --- a/Documentation/networking/ip-sysctl.rst +++ b/Documentation/networking/ip-sysctl.rst @@ -3206,3 +3206,14 @@ max_dgram_qlen - INTEGER Default: 10 + +``/proc/sys/net/bonding/*`` +======================== + +broadcast_arp_or_nd - INTEGER + Control broadcasting ARP or ND messages to all slaves + + 0: Not broadcasting + 1: Broadcasting + + Default: 0 diff --git a/drivers/net/bonding/Makefile b/drivers/net/bonding/Makefile index 30e8ae3da2da7c4fd6a008ff2a36480a3851b3f6..9dd55d91185a6f5979d2c99fb438c194300e5b75 100644 --- a/drivers/net/bonding/Makefile +++ b/drivers/net/bonding/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_BONDING) += bonding.o -bonding-objs := bond_main.o bond_3ad.o bond_alb.o bond_sysfs.o bond_sysfs_slave.o bond_debugfs.o bond_netlink.o bond_options.o +bonding-objs := bond_main.o bond_3ad.o bond_alb.o bond_sysfs.o bond_sysfs_slave.o bond_debugfs.o bond_netlink.o bond_options.o bond_sysctl.o proc-$(CONFIG_PROC_FS) += bond_procfs.o bonding-objs += $(proc-y) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 51d47eda1c873debda6da094377bcb3367a78f6e..842a138a574db685716997bea6d9dcd6ebd9f12d 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -5127,6 +5127,39 @@ int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave) return ret; } +/* Check whether the skb is arp or nd msg */ +static inline bool skb_is_arp_or_nd(struct sk_buff *skb) +{ + switch (ntohs(skb->protocol)) { + case ETH_P_ARP: + return true; + case ETH_P_IPV6: + if (pskb_may_pull(skb, sizeof(struct ipv6hdr) + + sizeof(struct nd_msg))) { + struct ipv6hdr *hdr = ipv6_hdr(skb); + u8 nexthdr = hdr->nexthdr; + struct icmp6hdr *icmp6; + + if (nexthdr == IPPROTO_ICMPV6) { + icmp6 = icmp6_hdr(skb); + + if ((icmp6->icmp6_type == + NDISC_NEIGHBOUR_SOLICITATION || + icmp6->icmp6_type == + NDISC_NEIGHBOUR_ADVERTISEMENT) && + icmp6->icmp6_code == 0) { + return true; + } + } + } + } + + return false; +} + +static netdev_tx_t bond_xmit_broadcast(struct sk_buff *skb, + struct net_device *bond_dev); + static struct slave *bond_xmit_3ad_xor_slave_get(struct bonding *bond, struct sk_buff *skb, struct bond_up_slave *slaves) @@ -5171,6 +5204,10 @@ static netdev_tx_t bond_3ad_xor_xmit(struct sk_buff *skb, struct bond_up_slave *slaves; struct slave *slave; + /* Broadcast to all slaves. */ + if (sysctl_bond_broadcast_arp_or_nd && skb_is_arp_or_nd(skb)) + return bond_xmit_broadcast(skb, dev); + slaves = rcu_dereference(bond->usable_slaves); slave = bond_xmit_3ad_xor_slave_get(bond, skb, slaves); if (likely(slave)) @@ -6488,6 +6525,7 @@ static int __init bonding_init(void) goto err_link; bond_create_debugfs(); + bond_create_sysctl(); for (i = 0; i < max_bonds; i++) { res = bond_create(&init_net, NULL); @@ -6504,6 +6542,7 @@ static int __init bonding_init(void) return res; err: bond_destroy_debugfs(); + bond_destroy_sysctl(); bond_netlink_fini(); err_link: unregister_pernet_subsys(&bond_net_ops); @@ -6516,6 +6555,7 @@ static void __exit bonding_exit(void) unregister_netdevice_notifier(&bond_netdev_notifier); bond_destroy_debugfs(); + bond_destroy_sysctl(); bond_netlink_fini(); unregister_pernet_subsys(&bond_net_ops); diff --git a/drivers/net/bonding/bond_sysctl.c b/drivers/net/bonding/bond_sysctl.c new file mode 100644 index 0000000000000000000000000000000000000000..17404d37a2fd9dca0999c360817bce908aef382c --- /dev/null +++ b/drivers/net/bonding/bond_sysctl.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include + +int sysctl_bond_broadcast_arp_or_nd __read_mostly; +EXPORT_SYMBOL(sysctl_bond_broadcast_arp_or_nd); + +struct ctl_table_header *bond_broadcast_arp_or_nd_table_header; + +static struct ctl_table bond_broadcast_arp_or_nd_table[] = { + { + .procname = "broadcast_arp_or_nd", + .data = &sysctl_bond_broadcast_arp_or_nd, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + {} +}; + +void bond_create_sysctl(void) +{ + bond_broadcast_arp_or_nd_table_header = + register_net_sysctl(&init_net, "net/bonding", + bond_broadcast_arp_or_nd_table); +} + +void bond_destroy_sysctl(void) +{ + unregister_net_sysctl_table(bond_broadcast_arp_or_nd_table_header); +} diff --git a/include/net/bonding.h b/include/net/bonding.h index 5b8b1b644a2dbf542695c874e27e0b2b3222cef6..0f065f150b81c0a1d8bdb04af708b6c42d3f6953 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -115,6 +115,8 @@ static inline int is_netpoll_tx_blocked(struct net_device *dev) #define is_netpoll_tx_blocked(dev) (0) #endif +extern int sysctl_bond_broadcast_arp_or_nd; + struct bond_params { int mode; int xmit_policy; @@ -684,6 +686,8 @@ struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev, int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave); void bond_slave_arr_work_rearm(struct bonding *bond, unsigned long delay); void bond_work_init_all(struct bonding *bond); +void bond_create_sysctl(void); +void bond_destroy_sysctl(void); #ifdef CONFIG_PROC_FS void bond_create_proc_entry(struct bonding *bond);