Merge branch 'hsr'
Sebastian Andrzej Siewior says: ==================== I started playing with HSR and run into a problem. Tested latest upstream -rc and noticed more problems. Now it appears to work. For testing I have a small three node setup with iperf and ping. While iperf doesn't complain ping reports missing packets and duplicates. ==================== Link: https://lore.kernel.org/r/20221129164815.128922-1-bigeasy@linutronix.de/ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
3f5a4aa1c3
|
@ -9,7 +9,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/jhash.h>
|
||||
#include "hsr_main.h"
|
||||
#include "hsr_framereg.h"
|
||||
|
||||
|
@ -21,7 +20,6 @@ hsr_node_table_show(struct seq_file *sfp, void *data)
|
|||
{
|
||||
struct hsr_priv *priv = (struct hsr_priv *)sfp->private;
|
||||
struct hsr_node *node;
|
||||
int i;
|
||||
|
||||
seq_printf(sfp, "Node Table entries for (%s) device\n",
|
||||
(priv->prot_version == PRP_V1 ? "PRP" : "HSR"));
|
||||
|
@ -33,28 +31,22 @@ hsr_node_table_show(struct seq_file *sfp, void *data)
|
|||
seq_puts(sfp, "DAN-H\n");
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(node, &priv->node_db, mac_list) {
|
||||
/* skip self node */
|
||||
if (hsr_addr_is_self(priv, node->macaddress_A))
|
||||
continue;
|
||||
seq_printf(sfp, "%pM ", &node->macaddress_A[0]);
|
||||
seq_printf(sfp, "%pM ", &node->macaddress_B[0]);
|
||||
seq_printf(sfp, "%10lx, ", node->time_in[HSR_PT_SLAVE_A]);
|
||||
seq_printf(sfp, "%10lx, ", node->time_in[HSR_PT_SLAVE_B]);
|
||||
seq_printf(sfp, "%14x, ", node->addr_B_port);
|
||||
|
||||
for (i = 0 ; i < priv->hash_buckets; i++) {
|
||||
hlist_for_each_entry_rcu(node, &priv->node_db[i], mac_list) {
|
||||
/* skip self node */
|
||||
if (hsr_addr_is_self(priv, node->macaddress_A))
|
||||
continue;
|
||||
seq_printf(sfp, "%pM ", &node->macaddress_A[0]);
|
||||
seq_printf(sfp, "%pM ", &node->macaddress_B[0]);
|
||||
seq_printf(sfp, "%10lx, ",
|
||||
node->time_in[HSR_PT_SLAVE_A]);
|
||||
seq_printf(sfp, "%10lx, ",
|
||||
node->time_in[HSR_PT_SLAVE_B]);
|
||||
seq_printf(sfp, "%14x, ", node->addr_B_port);
|
||||
|
||||
if (priv->prot_version == PRP_V1)
|
||||
seq_printf(sfp, "%5x, %5x, %5x\n",
|
||||
node->san_a, node->san_b,
|
||||
(node->san_a == 0 &&
|
||||
node->san_b == 0));
|
||||
else
|
||||
seq_printf(sfp, "%5x\n", 1);
|
||||
}
|
||||
if (priv->prot_version == PRP_V1)
|
||||
seq_printf(sfp, "%5x, %5x, %5x\n",
|
||||
node->san_a, node->san_b,
|
||||
(node->san_a == 0 && node->san_b == 0));
|
||||
else
|
||||
seq_printf(sfp, "%5x\n", 1);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
|
|
|
@ -219,7 +219,9 @@ static netdev_tx_t hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
skb->dev = master->dev;
|
||||
skb_reset_mac_header(skb);
|
||||
skb_reset_mac_len(skb);
|
||||
spin_lock_bh(&hsr->seqnr_lock);
|
||||
hsr_forward_skb(skb, master);
|
||||
spin_unlock_bh(&hsr->seqnr_lock);
|
||||
} else {
|
||||
dev_core_stats_tx_dropped_inc(dev);
|
||||
dev_kfree_skb_any(skb);
|
||||
|
@ -278,7 +280,6 @@ static void send_hsr_supervision_frame(struct hsr_port *master,
|
|||
__u8 type = HSR_TLV_LIFE_CHECK;
|
||||
struct hsr_sup_payload *hsr_sp;
|
||||
struct hsr_sup_tag *hsr_stag;
|
||||
unsigned long irqflags;
|
||||
struct sk_buff *skb;
|
||||
|
||||
*interval = msecs_to_jiffies(HSR_LIFE_CHECK_INTERVAL);
|
||||
|
@ -299,7 +300,7 @@ static void send_hsr_supervision_frame(struct hsr_port *master,
|
|||
set_hsr_stag_HSR_ver(hsr_stag, hsr->prot_version);
|
||||
|
||||
/* From HSRv1 on we have separate supervision sequence numbers. */
|
||||
spin_lock_irqsave(&master->hsr->seqnr_lock, irqflags);
|
||||
spin_lock_bh(&hsr->seqnr_lock);
|
||||
if (hsr->prot_version > 0) {
|
||||
hsr_stag->sequence_nr = htons(hsr->sup_sequence_nr);
|
||||
hsr->sup_sequence_nr++;
|
||||
|
@ -307,7 +308,6 @@ static void send_hsr_supervision_frame(struct hsr_port *master,
|
|||
hsr_stag->sequence_nr = htons(hsr->sequence_nr);
|
||||
hsr->sequence_nr++;
|
||||
}
|
||||
spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags);
|
||||
|
||||
hsr_stag->tlv.HSR_TLV_type = type;
|
||||
/* TODO: Why 12 in HSRv0? */
|
||||
|
@ -318,11 +318,13 @@ static void send_hsr_supervision_frame(struct hsr_port *master,
|
|||
hsr_sp = skb_put(skb, sizeof(struct hsr_sup_payload));
|
||||
ether_addr_copy(hsr_sp->macaddress_A, master->dev->dev_addr);
|
||||
|
||||
if (skb_put_padto(skb, ETH_ZLEN))
|
||||
if (skb_put_padto(skb, ETH_ZLEN)) {
|
||||
spin_unlock_bh(&hsr->seqnr_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
hsr_forward_skb(skb, master);
|
||||
|
||||
spin_unlock_bh(&hsr->seqnr_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -332,7 +334,6 @@ static void send_prp_supervision_frame(struct hsr_port *master,
|
|||
struct hsr_priv *hsr = master->hsr;
|
||||
struct hsr_sup_payload *hsr_sp;
|
||||
struct hsr_sup_tag *hsr_stag;
|
||||
unsigned long irqflags;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = hsr_init_skb(master);
|
||||
|
@ -347,7 +348,7 @@ static void send_prp_supervision_frame(struct hsr_port *master,
|
|||
set_hsr_stag_HSR_ver(hsr_stag, (hsr->prot_version ? 1 : 0));
|
||||
|
||||
/* From HSRv1 on we have separate supervision sequence numbers. */
|
||||
spin_lock_irqsave(&master->hsr->seqnr_lock, irqflags);
|
||||
spin_lock_bh(&hsr->seqnr_lock);
|
||||
hsr_stag->sequence_nr = htons(hsr->sup_sequence_nr);
|
||||
hsr->sup_sequence_nr++;
|
||||
hsr_stag->tlv.HSR_TLV_type = PRP_TLV_LIFE_CHECK_DD;
|
||||
|
@ -358,13 +359,12 @@ static void send_prp_supervision_frame(struct hsr_port *master,
|
|||
ether_addr_copy(hsr_sp->macaddress_A, master->dev->dev_addr);
|
||||
|
||||
if (skb_put_padto(skb, ETH_ZLEN)) {
|
||||
spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags);
|
||||
spin_unlock_bh(&hsr->seqnr_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags);
|
||||
|
||||
hsr_forward_skb(skb, master);
|
||||
spin_unlock_bh(&hsr->seqnr_lock);
|
||||
}
|
||||
|
||||
/* Announce (supervision frame) timer function
|
||||
|
@ -444,7 +444,7 @@ void hsr_dev_setup(struct net_device *dev)
|
|||
dev->header_ops = &hsr_header_ops;
|
||||
dev->netdev_ops = &hsr_device_ops;
|
||||
SET_NETDEV_DEVTYPE(dev, &hsr_type);
|
||||
dev->priv_flags |= IFF_NO_QUEUE;
|
||||
dev->priv_flags |= IFF_NO_QUEUE | IFF_DISABLE_NETPOLL;
|
||||
|
||||
dev->needs_free_netdev = true;
|
||||
|
||||
|
@ -485,16 +485,11 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
|
|||
{
|
||||
bool unregister = false;
|
||||
struct hsr_priv *hsr;
|
||||
int res, i;
|
||||
int res;
|
||||
|
||||
hsr = netdev_priv(hsr_dev);
|
||||
INIT_LIST_HEAD(&hsr->ports);
|
||||
INIT_HLIST_HEAD(&hsr->self_node_db);
|
||||
hsr->hash_buckets = HSR_HSIZE;
|
||||
get_random_bytes(&hsr->hash_seed, sizeof(hsr->hash_seed));
|
||||
for (i = 0; i < hsr->hash_buckets; i++)
|
||||
INIT_HLIST_HEAD(&hsr->node_db[i]);
|
||||
|
||||
INIT_LIST_HEAD(&hsr->node_db);
|
||||
spin_lock_init(&hsr->list_lock);
|
||||
|
||||
eth_hw_addr_set(hsr_dev, slave[0]->dev_addr);
|
||||
|
|
|
@ -500,7 +500,6 @@ static void handle_std_frame(struct sk_buff *skb,
|
|||
{
|
||||
struct hsr_port *port = frame->port_rcv;
|
||||
struct hsr_priv *hsr = port->hsr;
|
||||
unsigned long irqflags;
|
||||
|
||||
frame->skb_hsr = NULL;
|
||||
frame->skb_prp = NULL;
|
||||
|
@ -510,10 +509,9 @@ static void handle_std_frame(struct sk_buff *skb,
|
|||
frame->is_from_san = true;
|
||||
} else {
|
||||
/* Sequence nr for the master node */
|
||||
spin_lock_irqsave(&hsr->seqnr_lock, irqflags);
|
||||
lockdep_assert_held(&hsr->seqnr_lock);
|
||||
frame->sequence_nr = hsr->sequence_nr;
|
||||
hsr->sequence_nr++;
|
||||
spin_unlock_irqrestore(&hsr->seqnr_lock, irqflags);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -571,23 +569,20 @@ static int fill_frame_info(struct hsr_frame_info *frame,
|
|||
struct ethhdr *ethhdr;
|
||||
__be16 proto;
|
||||
int ret;
|
||||
u32 hash;
|
||||
|
||||
/* Check if skb contains ethhdr */
|
||||
if (skb->mac_len < sizeof(struct ethhdr))
|
||||
return -EINVAL;
|
||||
|
||||
memset(frame, 0, sizeof(*frame));
|
||||
|
||||
ethhdr = (struct ethhdr *)skb_mac_header(skb);
|
||||
hash = hsr_mac_hash(port->hsr, ethhdr->h_source);
|
||||
frame->is_supervision = is_supervision_frame(port->hsr, skb);
|
||||
frame->node_src = hsr_get_node(port, &hsr->node_db[hash], skb,
|
||||
frame->node_src = hsr_get_node(port, &hsr->node_db, skb,
|
||||
frame->is_supervision,
|
||||
port->type);
|
||||
if (!frame->node_src)
|
||||
return -1; /* Unknown node and !is_supervision, or no mem */
|
||||
|
||||
ethhdr = (struct ethhdr *)skb_mac_header(skb);
|
||||
frame->is_vlan = false;
|
||||
proto = ethhdr->h_proto;
|
||||
|
||||
|
@ -617,11 +612,13 @@ void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port)
|
|||
{
|
||||
struct hsr_frame_info frame;
|
||||
|
||||
rcu_read_lock();
|
||||
if (fill_frame_info(&frame, skb, port) < 0)
|
||||
goto out_drop;
|
||||
|
||||
hsr_register_frame_in(frame.node_src, port, frame.sequence_nr);
|
||||
hsr_forward_do(&frame);
|
||||
rcu_read_unlock();
|
||||
/* Gets called for ingress frames as well as egress from master port.
|
||||
* So check and increment stats for master port only here.
|
||||
*/
|
||||
|
@ -636,6 +633,7 @@ void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port)
|
|||
return;
|
||||
|
||||
out_drop:
|
||||
rcu_read_unlock();
|
||||
port->dev->stats.tx_dropped++;
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
|
|
@ -15,37 +15,10 @@
|
|||
#include <linux/etherdevice.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/jhash.h>
|
||||
#include "hsr_main.h"
|
||||
#include "hsr_framereg.h"
|
||||
#include "hsr_netlink.h"
|
||||
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
int lockdep_hsr_is_held(spinlock_t *lock)
|
||||
{
|
||||
return lockdep_is_held(lock);
|
||||
}
|
||||
#endif
|
||||
|
||||
u32 hsr_mac_hash(struct hsr_priv *hsr, const unsigned char *addr)
|
||||
{
|
||||
u32 hash = jhash(addr, ETH_ALEN, hsr->hash_seed);
|
||||
|
||||
return reciprocal_scale(hash, hsr->hash_buckets);
|
||||
}
|
||||
|
||||
struct hsr_node *hsr_node_get_first(struct hlist_head *head, spinlock_t *lock)
|
||||
{
|
||||
struct hlist_node *first;
|
||||
|
||||
first = rcu_dereference_bh_check(hlist_first_rcu(head),
|
||||
lockdep_hsr_is_held(lock));
|
||||
if (first)
|
||||
return hlist_entry(first, struct hsr_node, mac_list);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* seq_nr_after(a, b) - return true if a is after (higher in sequence than) b,
|
||||
* false otherwise.
|
||||
*/
|
||||
|
@ -65,30 +38,32 @@ static bool seq_nr_after(u16 a, u16 b)
|
|||
|
||||
bool hsr_addr_is_self(struct hsr_priv *hsr, unsigned char *addr)
|
||||
{
|
||||
struct hsr_node *node;
|
||||
struct hsr_self_node *sn;
|
||||
bool ret = false;
|
||||
|
||||
node = hsr_node_get_first(&hsr->self_node_db, &hsr->list_lock);
|
||||
if (!node) {
|
||||
rcu_read_lock();
|
||||
sn = rcu_dereference(hsr->self_node);
|
||||
if (!sn) {
|
||||
WARN_ONCE(1, "HSR: No self node\n");
|
||||
return false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ether_addr_equal(addr, node->macaddress_A))
|
||||
return true;
|
||||
if (ether_addr_equal(addr, node->macaddress_B))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
if (ether_addr_equal(addr, sn->macaddress_A) ||
|
||||
ether_addr_equal(addr, sn->macaddress_B))
|
||||
ret = true;
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Search for mac entry. Caller must hold rcu read lock.
|
||||
*/
|
||||
static struct hsr_node *find_node_by_addr_A(struct hlist_head *node_db,
|
||||
static struct hsr_node *find_node_by_addr_A(struct list_head *node_db,
|
||||
const unsigned char addr[ETH_ALEN])
|
||||
{
|
||||
struct hsr_node *node;
|
||||
|
||||
hlist_for_each_entry_rcu(node, node_db, mac_list) {
|
||||
list_for_each_entry_rcu(node, node_db, mac_list) {
|
||||
if (ether_addr_equal(node->macaddress_A, addr))
|
||||
return node;
|
||||
}
|
||||
|
@ -96,58 +71,51 @@ static struct hsr_node *find_node_by_addr_A(struct hlist_head *node_db,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* Helper for device init; the self_node_db is used in hsr_rcv() to recognize
|
||||
/* Helper for device init; the self_node is used in hsr_rcv() to recognize
|
||||
* frames from self that's been looped over the HSR ring.
|
||||
*/
|
||||
int hsr_create_self_node(struct hsr_priv *hsr,
|
||||
const unsigned char addr_a[ETH_ALEN],
|
||||
const unsigned char addr_b[ETH_ALEN])
|
||||
{
|
||||
struct hlist_head *self_node_db = &hsr->self_node_db;
|
||||
struct hsr_node *node, *oldnode;
|
||||
struct hsr_self_node *sn, *old;
|
||||
|
||||
node = kmalloc(sizeof(*node), GFP_KERNEL);
|
||||
if (!node)
|
||||
sn = kmalloc(sizeof(*sn), GFP_KERNEL);
|
||||
if (!sn)
|
||||
return -ENOMEM;
|
||||
|
||||
ether_addr_copy(node->macaddress_A, addr_a);
|
||||
ether_addr_copy(node->macaddress_B, addr_b);
|
||||
ether_addr_copy(sn->macaddress_A, addr_a);
|
||||
ether_addr_copy(sn->macaddress_B, addr_b);
|
||||
|
||||
spin_lock_bh(&hsr->list_lock);
|
||||
oldnode = hsr_node_get_first(self_node_db, &hsr->list_lock);
|
||||
if (oldnode) {
|
||||
hlist_replace_rcu(&oldnode->mac_list, &node->mac_list);
|
||||
spin_unlock_bh(&hsr->list_lock);
|
||||
kfree_rcu(oldnode, rcu_head);
|
||||
} else {
|
||||
hlist_add_tail_rcu(&node->mac_list, self_node_db);
|
||||
spin_unlock_bh(&hsr->list_lock);
|
||||
}
|
||||
old = rcu_replace_pointer(hsr->self_node, sn,
|
||||
lockdep_is_held(&hsr->list_lock));
|
||||
spin_unlock_bh(&hsr->list_lock);
|
||||
|
||||
if (old)
|
||||
kfree_rcu(old, rcu_head);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hsr_del_self_node(struct hsr_priv *hsr)
|
||||
{
|
||||
struct hlist_head *self_node_db = &hsr->self_node_db;
|
||||
struct hsr_node *node;
|
||||
struct hsr_self_node *old;
|
||||
|
||||
spin_lock_bh(&hsr->list_lock);
|
||||
node = hsr_node_get_first(self_node_db, &hsr->list_lock);
|
||||
if (node) {
|
||||
hlist_del_rcu(&node->mac_list);
|
||||
kfree_rcu(node, rcu_head);
|
||||
}
|
||||
old = rcu_replace_pointer(hsr->self_node, NULL,
|
||||
lockdep_is_held(&hsr->list_lock));
|
||||
spin_unlock_bh(&hsr->list_lock);
|
||||
if (old)
|
||||
kfree_rcu(old, rcu_head);
|
||||
}
|
||||
|
||||
void hsr_del_nodes(struct hlist_head *node_db)
|
||||
void hsr_del_nodes(struct list_head *node_db)
|
||||
{
|
||||
struct hsr_node *node;
|
||||
struct hlist_node *tmp;
|
||||
struct hsr_node *tmp;
|
||||
|
||||
hlist_for_each_entry_safe(node, tmp, node_db, mac_list)
|
||||
kfree_rcu(node, rcu_head);
|
||||
list_for_each_entry_safe(node, tmp, node_db, mac_list)
|
||||
kfree(node);
|
||||
}
|
||||
|
||||
void prp_handle_san_frame(bool san, enum hsr_port_type port,
|
||||
|
@ -168,7 +136,7 @@ void prp_handle_san_frame(bool san, enum hsr_port_type port,
|
|||
* originating from the newly added node.
|
||||
*/
|
||||
static struct hsr_node *hsr_add_node(struct hsr_priv *hsr,
|
||||
struct hlist_head *node_db,
|
||||
struct list_head *node_db,
|
||||
unsigned char addr[],
|
||||
u16 seq_out, bool san,
|
||||
enum hsr_port_type rx_port)
|
||||
|
@ -182,6 +150,7 @@ static struct hsr_node *hsr_add_node(struct hsr_priv *hsr,
|
|||
return NULL;
|
||||
|
||||
ether_addr_copy(new_node->macaddress_A, addr);
|
||||
spin_lock_init(&new_node->seq_out_lock);
|
||||
|
||||
/* We are only interested in time diffs here, so use current jiffies
|
||||
* as initialization. (0 could trigger an spurious ring error warning).
|
||||
|
@ -198,14 +167,14 @@ static struct hsr_node *hsr_add_node(struct hsr_priv *hsr,
|
|||
hsr->proto_ops->handle_san_frame(san, rx_port, new_node);
|
||||
|
||||
spin_lock_bh(&hsr->list_lock);
|
||||
hlist_for_each_entry_rcu(node, node_db, mac_list,
|
||||
lockdep_hsr_is_held(&hsr->list_lock)) {
|
||||
list_for_each_entry_rcu(node, node_db, mac_list,
|
||||
lockdep_is_held(&hsr->list_lock)) {
|
||||
if (ether_addr_equal(node->macaddress_A, addr))
|
||||
goto out;
|
||||
if (ether_addr_equal(node->macaddress_B, addr))
|
||||
goto out;
|
||||
}
|
||||
hlist_add_tail_rcu(&new_node->mac_list, node_db);
|
||||
list_add_tail_rcu(&new_node->mac_list, node_db);
|
||||
spin_unlock_bh(&hsr->list_lock);
|
||||
return new_node;
|
||||
out:
|
||||
|
@ -225,7 +194,7 @@ void prp_update_san_info(struct hsr_node *node, bool is_sup)
|
|||
|
||||
/* Get the hsr_node from which 'skb' was sent.
|
||||
*/
|
||||
struct hsr_node *hsr_get_node(struct hsr_port *port, struct hlist_head *node_db,
|
||||
struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db,
|
||||
struct sk_buff *skb, bool is_sup,
|
||||
enum hsr_port_type rx_port)
|
||||
{
|
||||
|
@ -241,7 +210,7 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct hlist_head *node_db,
|
|||
|
||||
ethhdr = (struct ethhdr *)skb_mac_header(skb);
|
||||
|
||||
hlist_for_each_entry_rcu(node, node_db, mac_list) {
|
||||
list_for_each_entry_rcu(node, node_db, mac_list) {
|
||||
if (ether_addr_equal(node->macaddress_A, ethhdr->h_source)) {
|
||||
if (hsr->proto_ops->update_san_info)
|
||||
hsr->proto_ops->update_san_info(node, is_sup);
|
||||
|
@ -291,12 +260,11 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
|
|||
struct hsr_sup_tlv *hsr_sup_tlv;
|
||||
struct hsr_node *node_real;
|
||||
struct sk_buff *skb = NULL;
|
||||
struct hlist_head *node_db;
|
||||
struct list_head *node_db;
|
||||
struct ethhdr *ethhdr;
|
||||
int i;
|
||||
unsigned int pull_size = 0;
|
||||
unsigned int total_pull_size = 0;
|
||||
u32 hash;
|
||||
|
||||
/* Here either frame->skb_hsr or frame->skb_prp should be
|
||||
* valid as supervision frame always will have protocol
|
||||
|
@ -334,13 +302,11 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
|
|||
hsr_sp = (struct hsr_sup_payload *)skb->data;
|
||||
|
||||
/* Merge node_curr (registered on macaddress_B) into node_real */
|
||||
node_db = port_rcv->hsr->node_db;
|
||||
hash = hsr_mac_hash(hsr, hsr_sp->macaddress_A);
|
||||
node_real = find_node_by_addr_A(&node_db[hash], hsr_sp->macaddress_A);
|
||||
node_db = &port_rcv->hsr->node_db;
|
||||
node_real = find_node_by_addr_A(node_db, hsr_sp->macaddress_A);
|
||||
if (!node_real)
|
||||
/* No frame received from AddrA of this node yet */
|
||||
node_real = hsr_add_node(hsr, &node_db[hash],
|
||||
hsr_sp->macaddress_A,
|
||||
node_real = hsr_add_node(hsr, node_db, hsr_sp->macaddress_A,
|
||||
HSR_SEQNR_START - 1, true,
|
||||
port_rcv->type);
|
||||
if (!node_real)
|
||||
|
@ -374,14 +340,14 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
|
|||
hsr_sp = (struct hsr_sup_payload *)skb->data;
|
||||
|
||||
/* Check if redbox mac and node mac are equal. */
|
||||
if (!ether_addr_equal(node_real->macaddress_A,
|
||||
hsr_sp->macaddress_A)) {
|
||||
if (!ether_addr_equal(node_real->macaddress_A, hsr_sp->macaddress_A)) {
|
||||
/* This is a redbox supervision frame for a VDAN! */
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
ether_addr_copy(node_real->macaddress_B, ethhdr->h_source);
|
||||
spin_lock_bh(&node_real->seq_out_lock);
|
||||
for (i = 0; i < HSR_PT_PORTS; i++) {
|
||||
if (!node_curr->time_in_stale[i] &&
|
||||
time_after(node_curr->time_in[i], node_real->time_in[i])) {
|
||||
|
@ -392,12 +358,16 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
|
|||
if (seq_nr_after(node_curr->seq_out[i], node_real->seq_out[i]))
|
||||
node_real->seq_out[i] = node_curr->seq_out[i];
|
||||
}
|
||||
spin_unlock_bh(&node_real->seq_out_lock);
|
||||
node_real->addr_B_port = port_rcv->type;
|
||||
|
||||
spin_lock_bh(&hsr->list_lock);
|
||||
hlist_del_rcu(&node_curr->mac_list);
|
||||
if (!node_curr->removed) {
|
||||
list_del_rcu(&node_curr->mac_list);
|
||||
node_curr->removed = true;
|
||||
kfree_rcu(node_curr, rcu_head);
|
||||
}
|
||||
spin_unlock_bh(&hsr->list_lock);
|
||||
kfree_rcu(node_curr, rcu_head);
|
||||
|
||||
done:
|
||||
/* Push back here */
|
||||
|
@ -433,7 +403,6 @@ void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb,
|
|||
struct hsr_port *port)
|
||||
{
|
||||
struct hsr_node *node_dst;
|
||||
u32 hash;
|
||||
|
||||
if (!skb_mac_header_was_set(skb)) {
|
||||
WARN_ONCE(1, "%s: Mac header not set\n", __func__);
|
||||
|
@ -443,8 +412,7 @@ void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb,
|
|||
if (!is_unicast_ether_addr(eth_hdr(skb)->h_dest))
|
||||
return;
|
||||
|
||||
hash = hsr_mac_hash(port->hsr, eth_hdr(skb)->h_dest);
|
||||
node_dst = find_node_by_addr_A(&port->hsr->node_db[hash],
|
||||
node_dst = find_node_by_addr_A(&port->hsr->node_db,
|
||||
eth_hdr(skb)->h_dest);
|
||||
if (!node_dst) {
|
||||
if (net_ratelimit())
|
||||
|
@ -484,13 +452,17 @@ void hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port,
|
|||
int hsr_register_frame_out(struct hsr_port *port, struct hsr_node *node,
|
||||
u16 sequence_nr)
|
||||
{
|
||||
spin_lock_bh(&node->seq_out_lock);
|
||||
if (seq_nr_before_or_eq(sequence_nr, node->seq_out[port->type]) &&
|
||||
time_is_after_jiffies(node->time_out[port->type] +
|
||||
msecs_to_jiffies(HSR_ENTRY_FORGET_TIME)))
|
||||
msecs_to_jiffies(HSR_ENTRY_FORGET_TIME))) {
|
||||
spin_unlock_bh(&node->seq_out_lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
node->time_out[port->type] = jiffies;
|
||||
node->seq_out[port->type] = sequence_nr;
|
||||
spin_unlock_bh(&node->seq_out_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -520,71 +492,60 @@ static struct hsr_port *get_late_port(struct hsr_priv *hsr,
|
|||
void hsr_prune_nodes(struct timer_list *t)
|
||||
{
|
||||
struct hsr_priv *hsr = from_timer(hsr, t, prune_timer);
|
||||
struct hlist_node *tmp;
|
||||
struct hsr_node *node;
|
||||
struct hsr_node *tmp;
|
||||
struct hsr_port *port;
|
||||
unsigned long timestamp;
|
||||
unsigned long time_a, time_b;
|
||||
int i;
|
||||
|
||||
spin_lock_bh(&hsr->list_lock);
|
||||
list_for_each_entry_safe(node, tmp, &hsr->node_db, mac_list) {
|
||||
/* Don't prune own node. Neither time_in[HSR_PT_SLAVE_A]
|
||||
* nor time_in[HSR_PT_SLAVE_B], will ever be updated for
|
||||
* the master port. Thus the master node will be repeatedly
|
||||
* pruned leading to packet loss.
|
||||
*/
|
||||
if (hsr_addr_is_self(hsr, node->macaddress_A))
|
||||
continue;
|
||||
|
||||
for (i = 0; i < hsr->hash_buckets; i++) {
|
||||
hlist_for_each_entry_safe(node, tmp, &hsr->node_db[i],
|
||||
mac_list) {
|
||||
/* Don't prune own node.
|
||||
* Neither time_in[HSR_PT_SLAVE_A]
|
||||
* nor time_in[HSR_PT_SLAVE_B], will ever be updated
|
||||
* for the master port. Thus the master node will be
|
||||
* repeatedly pruned leading to packet loss.
|
||||
*/
|
||||
if (hsr_addr_is_self(hsr, node->macaddress_A))
|
||||
continue;
|
||||
/* Shorthand */
|
||||
time_a = node->time_in[HSR_PT_SLAVE_A];
|
||||
time_b = node->time_in[HSR_PT_SLAVE_B];
|
||||
|
||||
/* Shorthand */
|
||||
time_a = node->time_in[HSR_PT_SLAVE_A];
|
||||
time_b = node->time_in[HSR_PT_SLAVE_B];
|
||||
/* Check for timestamps old enough to risk wrap-around */
|
||||
if (time_after(jiffies, time_a + MAX_JIFFY_OFFSET / 2))
|
||||
node->time_in_stale[HSR_PT_SLAVE_A] = true;
|
||||
if (time_after(jiffies, time_b + MAX_JIFFY_OFFSET / 2))
|
||||
node->time_in_stale[HSR_PT_SLAVE_B] = true;
|
||||
|
||||
/* Check for timestamps old enough to
|
||||
* risk wrap-around
|
||||
*/
|
||||
if (time_after(jiffies, time_a + MAX_JIFFY_OFFSET / 2))
|
||||
node->time_in_stale[HSR_PT_SLAVE_A] = true;
|
||||
if (time_after(jiffies, time_b + MAX_JIFFY_OFFSET / 2))
|
||||
node->time_in_stale[HSR_PT_SLAVE_B] = true;
|
||||
/* Get age of newest frame from node.
|
||||
* At least one time_in is OK here; nodes get pruned long
|
||||
* before both time_ins can get stale
|
||||
*/
|
||||
timestamp = time_a;
|
||||
if (node->time_in_stale[HSR_PT_SLAVE_A] ||
|
||||
(!node->time_in_stale[HSR_PT_SLAVE_B] &&
|
||||
time_after(time_b, time_a)))
|
||||
timestamp = time_b;
|
||||
|
||||
/* Get age of newest frame from node.
|
||||
* At least one time_in is OK here; nodes get pruned
|
||||
* long before both time_ins can get stale
|
||||
*/
|
||||
timestamp = time_a;
|
||||
if (node->time_in_stale[HSR_PT_SLAVE_A] ||
|
||||
(!node->time_in_stale[HSR_PT_SLAVE_B] &&
|
||||
time_after(time_b, time_a)))
|
||||
timestamp = time_b;
|
||||
/* Warn of ring error only as long as we get frames at all */
|
||||
if (time_is_after_jiffies(timestamp +
|
||||
msecs_to_jiffies(1.5 * MAX_SLAVE_DIFF))) {
|
||||
rcu_read_lock();
|
||||
port = get_late_port(hsr, node);
|
||||
if (port)
|
||||
hsr_nl_ringerror(hsr, node->macaddress_A, port);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
/* Warn of ring error only as long as we get
|
||||
* frames at all
|
||||
*/
|
||||
if (time_is_after_jiffies(timestamp +
|
||||
msecs_to_jiffies(1.5 * MAX_SLAVE_DIFF))) {
|
||||
rcu_read_lock();
|
||||
port = get_late_port(hsr, node);
|
||||
if (port)
|
||||
hsr_nl_ringerror(hsr,
|
||||
node->macaddress_A,
|
||||
port);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
/* Prune old entries */
|
||||
if (time_is_before_jiffies(timestamp +
|
||||
msecs_to_jiffies(HSR_NODE_FORGET_TIME))) {
|
||||
hsr_nl_nodedown(hsr, node->macaddress_A);
|
||||
hlist_del_rcu(&node->mac_list);
|
||||
/* Note that we need to free this
|
||||
* entry later:
|
||||
*/
|
||||
/* Prune old entries */
|
||||
if (time_is_before_jiffies(timestamp +
|
||||
msecs_to_jiffies(HSR_NODE_FORGET_TIME))) {
|
||||
hsr_nl_nodedown(hsr, node->macaddress_A);
|
||||
if (!node->removed) {
|
||||
list_del_rcu(&node->mac_list);
|
||||
node->removed = true;
|
||||
/* Note that we need to free this entry later: */
|
||||
kfree_rcu(node, rcu_head);
|
||||
}
|
||||
}
|
||||
|
@ -600,20 +561,17 @@ void *hsr_get_next_node(struct hsr_priv *hsr, void *_pos,
|
|||
unsigned char addr[ETH_ALEN])
|
||||
{
|
||||
struct hsr_node *node;
|
||||
u32 hash;
|
||||
|
||||
hash = hsr_mac_hash(hsr, addr);
|
||||
|
||||
if (!_pos) {
|
||||
node = hsr_node_get_first(&hsr->node_db[hash],
|
||||
&hsr->list_lock);
|
||||
node = list_first_or_null_rcu(&hsr->node_db,
|
||||
struct hsr_node, mac_list);
|
||||
if (node)
|
||||
ether_addr_copy(addr, node->macaddress_A);
|
||||
return node;
|
||||
}
|
||||
|
||||
node = _pos;
|
||||
hlist_for_each_entry_continue_rcu(node, mac_list) {
|
||||
list_for_each_entry_continue_rcu(node, &hsr->node_db, mac_list) {
|
||||
ether_addr_copy(addr, node->macaddress_A);
|
||||
return node;
|
||||
}
|
||||
|
@ -633,11 +591,8 @@ int hsr_get_node_data(struct hsr_priv *hsr,
|
|||
struct hsr_node *node;
|
||||
struct hsr_port *port;
|
||||
unsigned long tdiff;
|
||||
u32 hash;
|
||||
|
||||
hash = hsr_mac_hash(hsr, addr);
|
||||
|
||||
node = find_node_by_addr_A(&hsr->node_db[hash], addr);
|
||||
node = find_node_by_addr_A(&hsr->node_db, addr);
|
||||
if (!node)
|
||||
return -ENOENT;
|
||||
|
||||
|
|
|
@ -28,17 +28,9 @@ struct hsr_frame_info {
|
|||
bool is_from_san;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
int lockdep_hsr_is_held(spinlock_t *lock);
|
||||
#else
|
||||
#define lockdep_hsr_is_held(lock) 1
|
||||
#endif
|
||||
|
||||
u32 hsr_mac_hash(struct hsr_priv *hsr, const unsigned char *addr);
|
||||
struct hsr_node *hsr_node_get_first(struct hlist_head *head, spinlock_t *lock);
|
||||
void hsr_del_self_node(struct hsr_priv *hsr);
|
||||
void hsr_del_nodes(struct hlist_head *node_db);
|
||||
struct hsr_node *hsr_get_node(struct hsr_port *port, struct hlist_head *node_db,
|
||||
void hsr_del_nodes(struct list_head *node_db);
|
||||
struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db,
|
||||
struct sk_buff *skb, bool is_sup,
|
||||
enum hsr_port_type rx_port);
|
||||
void hsr_handle_sup_frame(struct hsr_frame_info *frame);
|
||||
|
@ -76,7 +68,9 @@ void prp_handle_san_frame(bool san, enum hsr_port_type port,
|
|||
void prp_update_san_info(struct hsr_node *node, bool is_sup);
|
||||
|
||||
struct hsr_node {
|
||||
struct hlist_node mac_list;
|
||||
struct list_head mac_list;
|
||||
/* Protect R/W access to seq_out */
|
||||
spinlock_t seq_out_lock;
|
||||
unsigned char macaddress_A[ETH_ALEN];
|
||||
unsigned char macaddress_B[ETH_ALEN];
|
||||
/* Local slave through which AddrB frames are received from this node */
|
||||
|
@ -88,6 +82,7 @@ struct hsr_node {
|
|||
bool san_a;
|
||||
bool san_b;
|
||||
u16 seq_out[HSR_PT_PORTS];
|
||||
bool removed;
|
||||
struct rcu_head rcu_head;
|
||||
};
|
||||
|
||||
|
|
|
@ -47,9 +47,6 @@
|
|||
|
||||
#define HSR_V1_SUP_LSDUSIZE 52
|
||||
|
||||
#define HSR_HSIZE_SHIFT 8
|
||||
#define HSR_HSIZE BIT(HSR_HSIZE_SHIFT)
|
||||
|
||||
/* The helper functions below assumes that 'path' occupies the 4 most
|
||||
* significant bits of the 16-bit field shared by 'path' and 'LSDU_size' (or
|
||||
* equivalently, the 4 most significant bits of HSR tag byte 14).
|
||||
|
@ -185,11 +182,17 @@ struct hsr_proto_ops {
|
|||
void (*update_san_info)(struct hsr_node *node, bool is_sup);
|
||||
};
|
||||
|
||||
struct hsr_self_node {
|
||||
unsigned char macaddress_A[ETH_ALEN];
|
||||
unsigned char macaddress_B[ETH_ALEN];
|
||||
struct rcu_head rcu_head;
|
||||
};
|
||||
|
||||
struct hsr_priv {
|
||||
struct rcu_head rcu_head;
|
||||
struct list_head ports;
|
||||
struct hlist_head node_db[HSR_HSIZE]; /* Known HSR nodes */
|
||||
struct hlist_head self_node_db; /* MACs of slaves */
|
||||
struct list_head node_db; /* Known HSR nodes */
|
||||
struct hsr_self_node __rcu *self_node; /* MACs of slaves */
|
||||
struct timer_list announce_timer; /* Supervision frame dispatch */
|
||||
struct timer_list prune_timer;
|
||||
int announce_count;
|
||||
|
@ -199,8 +202,6 @@ struct hsr_priv {
|
|||
spinlock_t seqnr_lock; /* locking for sequence_nr */
|
||||
spinlock_t list_lock; /* locking for node list */
|
||||
struct hsr_proto_ops *proto_ops;
|
||||
u32 hash_buckets;
|
||||
u32 hash_seed;
|
||||
#define PRP_LAN_ID 0x5 /* 0x1010 for A and 0x1011 for B. Bit 0 is set
|
||||
* based on SLAVE_A or SLAVE_B
|
||||
*/
|
||||
|
|
|
@ -105,7 +105,6 @@ static int hsr_newlink(struct net *src_net, struct net_device *dev,
|
|||
static void hsr_dellink(struct net_device *dev, struct list_head *head)
|
||||
{
|
||||
struct hsr_priv *hsr = netdev_priv(dev);
|
||||
int i;
|
||||
|
||||
del_timer_sync(&hsr->prune_timer);
|
||||
del_timer_sync(&hsr->announce_timer);
|
||||
|
@ -114,8 +113,7 @@ static void hsr_dellink(struct net_device *dev, struct list_head *head)
|
|||
hsr_del_ports(hsr);
|
||||
|
||||
hsr_del_self_node(hsr);
|
||||
for (i = 0; i < hsr->hash_buckets; i++)
|
||||
hsr_del_nodes(&hsr->node_db[i]);
|
||||
hsr_del_nodes(&hsr->node_db);
|
||||
|
||||
unregister_netdevice_queue(dev, head);
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ TARGETS += nci
|
|||
TARGETS += net
|
||||
TARGETS += net/af_unix
|
||||
TARGETS += net/forwarding
|
||||
TARGETS += net/hsr
|
||||
TARGETS += net/mptcp
|
||||
TARGETS += net/openvswitch
|
||||
TARGETS += netfilter
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
top_srcdir = ../../../../..
|
||||
|
||||
TEST_PROGS := hsr_ping.sh
|
||||
|
||||
include ../../lib.mk
|
|
@ -0,0 +1,4 @@
|
|||
CONFIG_IPV6=y
|
||||
CONFIG_NET_SCH_NETEM=m
|
||||
CONFIG_HSR=y
|
||||
CONFIG_VETH=y
|
|
@ -0,0 +1,256 @@
|
|||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
ret=0
|
||||
ksft_skip=4
|
||||
ipv6=true
|
||||
|
||||
optstring="h4"
|
||||
usage() {
|
||||
echo "Usage: $0 [OPTION]"
|
||||
echo -e "\t-4: IPv4 only: disable IPv6 tests (default: test both IPv4 and IPv6)"
|
||||
}
|
||||
|
||||
while getopts "$optstring" option;do
|
||||
case "$option" in
|
||||
"h")
|
||||
usage $0
|
||||
exit 0
|
||||
;;
|
||||
"4")
|
||||
ipv6=false
|
||||
;;
|
||||
"?")
|
||||
usage $0
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
sec=$(date +%s)
|
||||
rndh=$(printf %x $sec)-$(mktemp -u XXXXXX)
|
||||
ns1="ns1-$rndh"
|
||||
ns2="ns2-$rndh"
|
||||
ns3="ns3-$rndh"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
local netns
|
||||
for netns in "$ns1" "$ns2" "$ns3" ;do
|
||||
ip netns del $netns
|
||||
done
|
||||
}
|
||||
|
||||
ip -Version > /dev/null 2>&1
|
||||
if [ $? -ne 0 ];then
|
||||
echo "SKIP: Could not run test without ip tool"
|
||||
exit $ksft_skip
|
||||
fi
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
for i in "$ns1" "$ns2" "$ns3" ;do
|
||||
ip netns add $i || exit $ksft_skip
|
||||
ip -net $i link set lo up
|
||||
done
|
||||
|
||||
echo "INFO: preparing interfaces."
|
||||
# Three HSR nodes. Each node has one link to each of its neighbour, two links in total.
|
||||
#
|
||||
# ns1eth1 ----- ns2eth1
|
||||
# hsr1 hsr2
|
||||
# ns1eth2 ns2eth2
|
||||
# | |
|
||||
# ns3eth1 ns3eth2
|
||||
# \ /
|
||||
# hsr3
|
||||
#
|
||||
# Interfaces
|
||||
ip link add ns1eth1 netns "$ns1" type veth peer name ns2eth1 netns "$ns2"
|
||||
ip link add ns1eth2 netns "$ns1" type veth peer name ns3eth1 netns "$ns3"
|
||||
ip link add ns3eth2 netns "$ns3" type veth peer name ns2eth2 netns "$ns2"
|
||||
|
||||
# HSRv0.
|
||||
ip -net "$ns1" link add name hsr1 type hsr slave1 ns1eth1 slave2 ns1eth2 supervision 45 version 0 proto 0
|
||||
ip -net "$ns2" link add name hsr2 type hsr slave1 ns2eth1 slave2 ns2eth2 supervision 45 version 0 proto 0
|
||||
ip -net "$ns3" link add name hsr3 type hsr slave1 ns3eth1 slave2 ns3eth2 supervision 45 version 0 proto 0
|
||||
|
||||
# IP for HSR
|
||||
ip -net "$ns1" addr add 100.64.0.1/24 dev hsr1
|
||||
ip -net "$ns1" addr add dead:beef:1::1/64 dev hsr1 nodad
|
||||
ip -net "$ns2" addr add 100.64.0.2/24 dev hsr2
|
||||
ip -net "$ns2" addr add dead:beef:1::2/64 dev hsr2 nodad
|
||||
ip -net "$ns3" addr add 100.64.0.3/24 dev hsr3
|
||||
ip -net "$ns3" addr add dead:beef:1::3/64 dev hsr3 nodad
|
||||
|
||||
# All Links up
|
||||
ip -net "$ns1" link set ns1eth1 up
|
||||
ip -net "$ns1" link set ns1eth2 up
|
||||
ip -net "$ns1" link set hsr1 up
|
||||
|
||||
ip -net "$ns2" link set ns2eth1 up
|
||||
ip -net "$ns2" link set ns2eth2 up
|
||||
ip -net "$ns2" link set hsr2 up
|
||||
|
||||
ip -net "$ns3" link set ns3eth1 up
|
||||
ip -net "$ns3" link set ns3eth2 up
|
||||
ip -net "$ns3" link set hsr3 up
|
||||
|
||||
# $1: IP address
|
||||
is_v6()
|
||||
{
|
||||
[ -z "${1##*:*}" ]
|
||||
}
|
||||
|
||||
do_ping()
|
||||
{
|
||||
local netns="$1"
|
||||
local connect_addr="$2"
|
||||
local ping_args="-q -c 2"
|
||||
|
||||
if is_v6 "${connect_addr}"; then
|
||||
$ipv6 || return 0
|
||||
ping_args="${ping_args} -6"
|
||||
fi
|
||||
|
||||
ip netns exec ${netns} ping ${ping_args} $connect_addr >/dev/null
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "$netns -> $connect_addr connectivity [ FAIL ]" 1>&2
|
||||
ret=1
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
do_ping_long()
|
||||
{
|
||||
local netns="$1"
|
||||
local connect_addr="$2"
|
||||
local ping_args="-q -c 10"
|
||||
|
||||
if is_v6 "${connect_addr}"; then
|
||||
$ipv6 || return 0
|
||||
ping_args="${ping_args} -6"
|
||||
fi
|
||||
|
||||
OUT="$(LANG=C ip netns exec ${netns} ping ${ping_args} $connect_addr | grep received)"
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "$netns -> $connect_addr ping [ FAIL ]" 1>&2
|
||||
ret=1
|
||||
return 1
|
||||
fi
|
||||
|
||||
VAL="$(echo $OUT | cut -d' ' -f1-8)"
|
||||
if [ "$VAL" != "10 packets transmitted, 10 received, 0% packet loss," ]
|
||||
then
|
||||
echo "$netns -> $connect_addr ping TEST [ FAIL ]"
|
||||
echo "Expect to send and receive 10 packets and no duplicates."
|
||||
echo "Full message: ${OUT}."
|
||||
ret=1
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
stop_if_error()
|
||||
{
|
||||
local msg="$1"
|
||||
|
||||
if [ ${ret} -ne 0 ]; then
|
||||
echo "FAIL: ${msg}" 1>&2
|
||||
exit ${ret}
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
echo "INFO: Initial validation ping."
|
||||
# Each node has to be able each one.
|
||||
do_ping "$ns1" 100.64.0.2
|
||||
do_ping "$ns2" 100.64.0.1
|
||||
do_ping "$ns3" 100.64.0.1
|
||||
stop_if_error "Initial validation failed."
|
||||
|
||||
do_ping "$ns1" 100.64.0.3
|
||||
do_ping "$ns2" 100.64.0.3
|
||||
do_ping "$ns3" 100.64.0.2
|
||||
|
||||
do_ping "$ns1" dead:beef:1::2
|
||||
do_ping "$ns1" dead:beef:1::3
|
||||
do_ping "$ns2" dead:beef:1::1
|
||||
do_ping "$ns2" dead:beef:1::2
|
||||
do_ping "$ns3" dead:beef:1::1
|
||||
do_ping "$ns3" dead:beef:1::2
|
||||
|
||||
stop_if_error "Initial validation failed."
|
||||
|
||||
# Wait until supervisor all supervision frames have been processed and the node
|
||||
# entries have been merged. Otherwise duplicate frames will be observed which is
|
||||
# valid at this stage.
|
||||
WAIT=5
|
||||
while [ ${WAIT} -gt 0 ]
|
||||
do
|
||||
grep 00:00:00:00:00:00 /sys/kernel/debug/hsr/hsr*/node_table
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
let WAIT = WAIT - 1
|
||||
done
|
||||
|
||||
# Just a safety delay in case the above check didn't handle it.
|
||||
sleep 1
|
||||
|
||||
echo "INFO: Longer ping test."
|
||||
do_ping_long "$ns1" 100.64.0.2
|
||||
do_ping_long "$ns1" dead:beef:1::2
|
||||
do_ping_long "$ns1" 100.64.0.3
|
||||
do_ping_long "$ns1" dead:beef:1::3
|
||||
|
||||
stop_if_error "Longer ping test failed."
|
||||
|
||||
do_ping_long "$ns2" 100.64.0.1
|
||||
do_ping_long "$ns2" dead:beef:1::1
|
||||
do_ping_long "$ns2" 100.64.0.3
|
||||
do_ping_long "$ns2" dead:beef:1::2
|
||||
stop_if_error "Longer ping test failed."
|
||||
|
||||
do_ping_long "$ns3" 100.64.0.1
|
||||
do_ping_long "$ns3" dead:beef:1::1
|
||||
do_ping_long "$ns3" 100.64.0.2
|
||||
do_ping_long "$ns3" dead:beef:1::2
|
||||
stop_if_error "Longer ping test failed."
|
||||
|
||||
echo "INFO: Cutting one link."
|
||||
do_ping_long "$ns1" 100.64.0.3 &
|
||||
|
||||
sleep 3
|
||||
ip -net "$ns3" link set ns3eth1 down
|
||||
wait
|
||||
|
||||
ip -net "$ns3" link set ns3eth1 up
|
||||
|
||||
stop_if_error "Failed with one link down."
|
||||
|
||||
echo "INFO: Delay the link and drop a few packages."
|
||||
tc -net "$ns3" qdisc add dev ns3eth1 root netem delay 50ms
|
||||
tc -net "$ns2" qdisc add dev ns2eth1 root netem delay 5ms loss 25%
|
||||
|
||||
do_ping_long "$ns1" 100.64.0.2
|
||||
do_ping_long "$ns1" 100.64.0.3
|
||||
|
||||
stop_if_error "Failed with delay and packetloss."
|
||||
|
||||
do_ping_long "$ns2" 100.64.0.1
|
||||
do_ping_long "$ns2" 100.64.0.3
|
||||
|
||||
stop_if_error "Failed with delay and packetloss."
|
||||
|
||||
do_ping_long "$ns3" 100.64.0.1
|
||||
do_ping_long "$ns3" 100.64.0.2
|
||||
stop_if_error "Failed with delay and packetloss."
|
||||
|
||||
echo "INFO: All good."
|
||||
exit $ret
|
Loading…
Reference in New Issue