net: support arp/nd broadcast
This feature will enable 802.3ad bonding broadcase ipv4 arp & ipv6 nd packet. Signed-off-by: Hongbo Li <herberthbli@tencent.com>
This commit is contained in:
parent
56e8a03a04
commit
65dd4c8dcb
|
@ -116,6 +116,7 @@ static void ad_marker_response_received(struct bond_marker *marker,
|
|||
struct port *port);
|
||||
static void ad_update_actor_keys(struct port *port, bool reset);
|
||||
|
||||
int bond_8023ad_up;
|
||||
|
||||
/* ================= api to bonding and kernel code ================== */
|
||||
|
||||
|
@ -1223,6 +1224,13 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port)
|
|||
__record_pdu(lacpdu, port);
|
||||
port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(port->actor_oper_port_state & LACP_STATE_LACP_TIMEOUT));
|
||||
port->actor_oper_port_state &= ~LACP_STATE_EXPIRED;
|
||||
|
||||
if ((last_state != AD_RX_CURRENT) &&
|
||||
port->slave &&
|
||||
port->slave->bond &&
|
||||
(port->slave->bond->params.broadcast_arp ||
|
||||
port->slave->bond->params.broadcast_nd))
|
||||
port->slave->bond->send_peer_notif++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -2375,6 +2383,22 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
|
|||
port->sm_vars &= ~AD_PORT_BEGIN;
|
||||
}
|
||||
|
||||
if (bond_8023ad_up > 0 &&
|
||||
(jiffies >= bond->params.last_na +
|
||||
msecs_to_jiffies(5 * 1000))) {
|
||||
bond->send_peer_notif++;
|
||||
bond->params.last_na = jiffies;
|
||||
bond_8023ad_up--;
|
||||
}
|
||||
|
||||
if (bond->params.periodic_na &&
|
||||
(bond->params.periodic_na_interval >= 1) &&
|
||||
(jiffies >= bond->params.last_na +
|
||||
msecs_to_jiffies(bond->params.periodic_na_interval * 1000))) {
|
||||
bond->send_peer_notif++;
|
||||
bond->params.last_na = jiffies;
|
||||
}
|
||||
|
||||
re_arm:
|
||||
bond_for_each_slave_rcu(bond, slave, iter) {
|
||||
if (slave->should_notify) {
|
||||
|
|
|
@ -81,6 +81,9 @@
|
|||
#include <net/netns/generic.h>
|
||||
#include <net/pkt_sched.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/icmpv6.h>
|
||||
#include <net/ndisc.h>
|
||||
#include <net/flow_dissector.h>
|
||||
#include <net/xfrm.h>
|
||||
#include <net/bonding.h>
|
||||
|
@ -1126,7 +1129,12 @@ static bool bond_should_notify_peers(struct bonding *bond)
|
|||
struct slave *slave;
|
||||
|
||||
rcu_read_lock();
|
||||
slave = rcu_dereference(bond->curr_active_slave);
|
||||
if ((bond->params.broadcast_arp || bond->params.broadcast_nd) &&
|
||||
bond->params.mode == BOND_MODE_8023AD)
|
||||
slave = bond_first_slave_rcu(bond);
|
||||
else
|
||||
slave = rcu_dereference(bond->curr_active_slave);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
if (!slave || !bond->send_peer_notif ||
|
||||
|
@ -2751,6 +2759,8 @@ static void bond_miimon_commit(struct bonding *bond)
|
|||
slave->duplex ? "full" : "half");
|
||||
|
||||
bond_miimon_link_change(bond, slave, BOND_LINK_UP);
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD)
|
||||
bond_8023ad_up = 2;
|
||||
|
||||
active = rtnl_dereference(bond->curr_active_slave);
|
||||
if (!active || slave == primary || slave->prio > active->prio)
|
||||
|
@ -2774,6 +2784,11 @@ static void bond_miimon_commit(struct bonding *bond)
|
|||
|
||||
bond_miimon_link_change(bond, slave, BOND_LINK_DOWN);
|
||||
|
||||
if ((bond->params.broadcast_arp ||
|
||||
bond->params.broadcast_nd) &&
|
||||
bond->params.mode == BOND_MODE_8023AD)
|
||||
bond->send_peer_notif++;
|
||||
|
||||
if (slave == rcu_access_pointer(bond->curr_active_slave))
|
||||
do_failover = true;
|
||||
|
||||
|
@ -5168,6 +5183,8 @@ static struct slave *bond_xdp_xmit_3ad_xor_slave_get(struct bonding *bond,
|
|||
return slaves->arr[hash % count];
|
||||
}
|
||||
|
||||
static netdev_tx_t bond_xmit_broadcast(struct sk_buff *skb,
|
||||
struct net_device *bond_dev);
|
||||
/* Use this Xmit function for 3AD as well as XOR modes. The current
|
||||
* usable slave array is formed in the control path. The xmit function
|
||||
* just calculates hash and sends the packet out.
|
||||
|
@ -5179,6 +5196,23 @@ static netdev_tx_t bond_3ad_xor_xmit(struct sk_buff *skb,
|
|||
struct bond_up_slave *slaves;
|
||||
struct slave *slave;
|
||||
|
||||
/* Broadcast arp to all slaves. */
|
||||
if (bond->params.broadcast_arp && ntohs(skb->protocol) == ETH_P_ARP)
|
||||
return bond_xmit_broadcast(skb, dev);
|
||||
if (bond->params.broadcast_nd && (ntohs(skb->protocol) == ETH_P_IPV6) &&
|
||||
pskb_may_pull(skb,
|
||||
sizeof(struct ipv6hdr) + sizeof(struct icmp6hdr))) {
|
||||
if (ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) {
|
||||
struct icmp6hdr *icmph = icmp6_hdr(skb);
|
||||
|
||||
if ((icmph->icmp6_type ==
|
||||
NDISC_NEIGHBOUR_SOLICITATION) ||
|
||||
(icmph->icmp6_type ==
|
||||
NDISC_NEIGHBOUR_ADVERTISEMENT))
|
||||
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))
|
||||
|
|
|
@ -210,14 +210,19 @@ static void bond_info_show_slave(struct seq_file *seq,
|
|||
seq_printf(seq, "Permanent HW addr: %*phC\n",
|
||||
slave->dev->addr_len, slave->perm_hwaddr);
|
||||
seq_printf(seq, "Slave queue ID: %d\n", slave->queue_id);
|
||||
seq_printf(seq, "Slave active: %d\n", !slave->backup);
|
||||
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
const struct port *port = &SLAVE_AD_INFO(slave)->port;
|
||||
const struct aggregator *agg = port->aggregator;
|
||||
|
||||
seq_printf(seq, "Slave sm_vars: 0x%x\n",
|
||||
port->sm_vars);
|
||||
if (agg) {
|
||||
seq_printf(seq, "Aggregator ID: %d\n",
|
||||
agg->aggregator_identifier);
|
||||
seq_printf(seq, "Aggregator active: %d\n",
|
||||
agg->is_active);
|
||||
seq_printf(seq, "Actor Churn State: %s\n",
|
||||
bond_3ad_churn_desc(port->sm_churn_actor_state));
|
||||
seq_printf(seq, "Partner Churn State: %s\n",
|
||||
|
|
|
@ -753,6 +753,163 @@ static ssize_t bonding_show_ad_user_port_key(struct device *d,
|
|||
static DEVICE_ATTR(ad_user_port_key, 0644,
|
||||
bonding_show_ad_user_port_key, bonding_sysfs_store_option);
|
||||
|
||||
static ssize_t bonding_show_broadcast_arp(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
|
||||
return sprintf(buf, "%d\n", bond->params.broadcast_arp);
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_broadcast_arp(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int new_value, ret = count;
|
||||
struct bonding *bond = to_bond(d);
|
||||
|
||||
if (sscanf(buf, "%d", &new_value) != 1) {
|
||||
pr_err("%s: no broadcast arp value specified.\n",
|
||||
bond->dev->name);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (new_value != 0 && new_value != 1) {
|
||||
pr_err("%s: Invalid broadcast arp value %d not in range 0-1; rejected.\n",
|
||||
bond->dev->name, new_value);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pr_info("%s: Setting broadcast arp to %d.\n",
|
||||
bond->dev->name, new_value);
|
||||
bond->params.broadcast_arp = new_value;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(broadcast_arp, S_IRUGO | S_IWUSR,
|
||||
bonding_show_broadcast_arp, bonding_store_broadcast_arp);
|
||||
|
||||
static ssize_t bonding_show_broadcast_nd(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
|
||||
return sprintf(buf, "%d\n", bond->params.broadcast_nd);
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_broadcast_nd(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int new_value, ret = count;
|
||||
struct bonding *bond = to_bond(d);
|
||||
|
||||
if (sscanf(buf, "%d", &new_value) != 1) {
|
||||
pr_err("%s: no broadcast_nd value specified.\n",
|
||||
bond->dev->name);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (new_value != 0 && new_value != 1) {
|
||||
pr_err("%s: Invalid broadcast_nd value %d not in range 0-1; rejected.\n",
|
||||
bond->dev->name, new_value);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
pr_info("%s: Setting broadcast_nd to %d.\n",
|
||||
bond->dev->name, new_value);
|
||||
bond->params.broadcast_nd = new_value;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(broadcast_nd, S_IRUGO | S_IWUSR,
|
||||
bonding_show_broadcast_nd,
|
||||
bonding_store_broadcast_nd);
|
||||
|
||||
static ssize_t bonding_show_periodic_na(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
|
||||
return sprintf(buf, "%d\n", bond->params.periodic_na);
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_periodic_na(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int new_value, ret = count;
|
||||
struct bonding *bond = to_bond(d);
|
||||
|
||||
if (sscanf(buf, "%d", &new_value) != 1) {
|
||||
pr_err("%s: no periodic_na value specified.\n",
|
||||
bond->dev->name);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (new_value != 0 && new_value != 1) {
|
||||
pr_err("%s: Invalid periodic_na value %d not in range 0-1; rejected.\n",
|
||||
bond->dev->name, new_value);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
pr_info("%s: Setting periodic_na to %d.\n",
|
||||
bond->dev->name, new_value);
|
||||
bond->params.periodic_na = new_value;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(periodic_na, S_IRUGO | S_IWUSR,
|
||||
bonding_show_periodic_na,
|
||||
bonding_store_periodic_na);
|
||||
|
||||
static ssize_t bonding_show_periodic_na_interval(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
|
||||
return sprintf(buf, "%d\n", bond->params.periodic_na_interval);
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_periodic_na_interval(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int new_value, ret = count;
|
||||
struct bonding *bond = to_bond(d);
|
||||
|
||||
if (sscanf(buf, "%u", &new_value) != 1) {
|
||||
pr_err("%s: no periodic_na_interval value specified.\n",
|
||||
bond->dev->name);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pr_info("%s: Setting periodic_na_interval to %u.\n",
|
||||
bond->dev->name, new_value);
|
||||
bond->params.periodic_na_interval = new_value;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(periodic_na_interval, S_IRUGO | S_IWUSR,
|
||||
bonding_show_periodic_na_interval,
|
||||
bonding_store_periodic_na_interval);
|
||||
|
||||
static struct attribute *per_bond_attrs[] = {
|
||||
&dev_attr_slaves.attr,
|
||||
&dev_attr_mode.attr,
|
||||
|
@ -792,6 +949,10 @@ static struct attribute *per_bond_attrs[] = {
|
|||
&dev_attr_ad_actor_system.attr,
|
||||
&dev_attr_ad_user_port_key.attr,
|
||||
&dev_attr_arp_missed_max.attr,
|
||||
&dev_attr_broadcast_arp.attr,
|
||||
&dev_attr_broadcast_nd.attr,
|
||||
&dev_attr_periodic_na.attr,
|
||||
&dev_attr_periodic_na_interval.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
|
@ -262,6 +262,8 @@ struct ad_system {
|
|||
#define BOND_AD_INFO(bond) ((bond)->ad_info)
|
||||
#define SLAVE_AD_INFO(slave) ((slave)->ad_info)
|
||||
|
||||
extern int bond_8023ad_up;
|
||||
|
||||
struct ad_bond_info {
|
||||
struct ad_system system; /* 802.3ad system structure */
|
||||
struct bond_3ad_stats stats;
|
||||
|
|
|
@ -148,6 +148,11 @@ struct bond_params {
|
|||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
struct in6_addr ns_targets[BOND_MAX_NS_TARGETS];
|
||||
#endif
|
||||
int broadcast_arp;
|
||||
int broadcast_nd;
|
||||
unsigned long last_na;
|
||||
int periodic_na;
|
||||
u32 periodic_na_interval;
|
||||
|
||||
/* 2 bytes of padding : see ether_addr_equal_64bits() */
|
||||
u8 ad_actor_system[ETH_ALEN + 2];
|
||||
|
|
Loading…
Reference in New Issue