162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright 2011-2014 Autronica Fire and Security AS
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Author(s):
562306a36Sopenharmony_ci *	2011-2014 Arvid Brodin, arvid.brodin@alten.se
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * The HSR spec says never to forward the same frame twice on the same
862306a36Sopenharmony_ci * interface. A frame is identified by its source MAC address and its HSR
962306a36Sopenharmony_ci * sequence number. This code keeps track of senders and their sequence numbers
1062306a36Sopenharmony_ci * to allow filtering of duplicate frames, and to detect HSR ring errors.
1162306a36Sopenharmony_ci * Same code handles filtering of duplicates for PRP as well.
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/if_ether.h>
1562306a36Sopenharmony_ci#include <linux/etherdevice.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <linux/rculist.h>
1862306a36Sopenharmony_ci#include "hsr_main.h"
1962306a36Sopenharmony_ci#include "hsr_framereg.h"
2062306a36Sopenharmony_ci#include "hsr_netlink.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/* seq_nr_after(a, b) - return true if a is after (higher in sequence than) b,
2362306a36Sopenharmony_ci * false otherwise.
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_cistatic bool seq_nr_after(u16 a, u16 b)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	/* Remove inconsistency where
2862306a36Sopenharmony_ci	 * seq_nr_after(a, b) == seq_nr_before(a, b)
2962306a36Sopenharmony_ci	 */
3062306a36Sopenharmony_ci	if ((int)b - a == 32768)
3162306a36Sopenharmony_ci		return false;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	return (((s16)(b - a)) < 0);
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define seq_nr_before(a, b)		seq_nr_after((b), (a))
3762306a36Sopenharmony_ci#define seq_nr_before_or_eq(a, b)	(!seq_nr_after((a), (b)))
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cibool hsr_addr_is_self(struct hsr_priv *hsr, unsigned char *addr)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct hsr_self_node *sn;
4262306a36Sopenharmony_ci	bool ret = false;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	rcu_read_lock();
4562306a36Sopenharmony_ci	sn = rcu_dereference(hsr->self_node);
4662306a36Sopenharmony_ci	if (!sn) {
4762306a36Sopenharmony_ci		WARN_ONCE(1, "HSR: No self node\n");
4862306a36Sopenharmony_ci		goto out;
4962306a36Sopenharmony_ci	}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (ether_addr_equal(addr, sn->macaddress_A) ||
5262306a36Sopenharmony_ci	    ether_addr_equal(addr, sn->macaddress_B))
5362306a36Sopenharmony_ci		ret = true;
5462306a36Sopenharmony_ciout:
5562306a36Sopenharmony_ci	rcu_read_unlock();
5662306a36Sopenharmony_ci	return ret;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/* Search for mac entry. Caller must hold rcu read lock.
6062306a36Sopenharmony_ci */
6162306a36Sopenharmony_cistatic struct hsr_node *find_node_by_addr_A(struct list_head *node_db,
6262306a36Sopenharmony_ci					    const unsigned char addr[ETH_ALEN])
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	struct hsr_node *node;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	list_for_each_entry_rcu(node, node_db, mac_list) {
6762306a36Sopenharmony_ci		if (ether_addr_equal(node->macaddress_A, addr))
6862306a36Sopenharmony_ci			return node;
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	return NULL;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/* Helper for device init; the self_node is used in hsr_rcv() to recognize
7562306a36Sopenharmony_ci * frames from self that's been looped over the HSR ring.
7662306a36Sopenharmony_ci */
7762306a36Sopenharmony_ciint hsr_create_self_node(struct hsr_priv *hsr,
7862306a36Sopenharmony_ci			 const unsigned char addr_a[ETH_ALEN],
7962306a36Sopenharmony_ci			 const unsigned char addr_b[ETH_ALEN])
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct hsr_self_node *sn, *old;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	sn = kmalloc(sizeof(*sn), GFP_KERNEL);
8462306a36Sopenharmony_ci	if (!sn)
8562306a36Sopenharmony_ci		return -ENOMEM;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	ether_addr_copy(sn->macaddress_A, addr_a);
8862306a36Sopenharmony_ci	ether_addr_copy(sn->macaddress_B, addr_b);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	spin_lock_bh(&hsr->list_lock);
9162306a36Sopenharmony_ci	old = rcu_replace_pointer(hsr->self_node, sn,
9262306a36Sopenharmony_ci				  lockdep_is_held(&hsr->list_lock));
9362306a36Sopenharmony_ci	spin_unlock_bh(&hsr->list_lock);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (old)
9662306a36Sopenharmony_ci		kfree_rcu(old, rcu_head);
9762306a36Sopenharmony_ci	return 0;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_civoid hsr_del_self_node(struct hsr_priv *hsr)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct hsr_self_node *old;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	spin_lock_bh(&hsr->list_lock);
10562306a36Sopenharmony_ci	old = rcu_replace_pointer(hsr->self_node, NULL,
10662306a36Sopenharmony_ci				  lockdep_is_held(&hsr->list_lock));
10762306a36Sopenharmony_ci	spin_unlock_bh(&hsr->list_lock);
10862306a36Sopenharmony_ci	if (old)
10962306a36Sopenharmony_ci		kfree_rcu(old, rcu_head);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_civoid hsr_del_nodes(struct list_head *node_db)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	struct hsr_node *node;
11562306a36Sopenharmony_ci	struct hsr_node *tmp;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	list_for_each_entry_safe(node, tmp, node_db, mac_list)
11862306a36Sopenharmony_ci		kfree(node);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_civoid prp_handle_san_frame(bool san, enum hsr_port_type port,
12262306a36Sopenharmony_ci			  struct hsr_node *node)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	/* Mark if the SAN node is over LAN_A or LAN_B */
12562306a36Sopenharmony_ci	if (port == HSR_PT_SLAVE_A) {
12662306a36Sopenharmony_ci		node->san_a = true;
12762306a36Sopenharmony_ci		return;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (port == HSR_PT_SLAVE_B)
13162306a36Sopenharmony_ci		node->san_b = true;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/* Allocate an hsr_node and add it to node_db. 'addr' is the node's address_A;
13562306a36Sopenharmony_ci * seq_out is used to initialize filtering of outgoing duplicate frames
13662306a36Sopenharmony_ci * originating from the newly added node.
13762306a36Sopenharmony_ci */
13862306a36Sopenharmony_cistatic struct hsr_node *hsr_add_node(struct hsr_priv *hsr,
13962306a36Sopenharmony_ci				     struct list_head *node_db,
14062306a36Sopenharmony_ci				     unsigned char addr[],
14162306a36Sopenharmony_ci				     u16 seq_out, bool san,
14262306a36Sopenharmony_ci				     enum hsr_port_type rx_port)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct hsr_node *new_node, *node;
14562306a36Sopenharmony_ci	unsigned long now;
14662306a36Sopenharmony_ci	int i;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	new_node = kzalloc(sizeof(*new_node), GFP_ATOMIC);
14962306a36Sopenharmony_ci	if (!new_node)
15062306a36Sopenharmony_ci		return NULL;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	ether_addr_copy(new_node->macaddress_A, addr);
15362306a36Sopenharmony_ci	spin_lock_init(&new_node->seq_out_lock);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/* We are only interested in time diffs here, so use current jiffies
15662306a36Sopenharmony_ci	 * as initialization. (0 could trigger an spurious ring error warning).
15762306a36Sopenharmony_ci	 */
15862306a36Sopenharmony_ci	now = jiffies;
15962306a36Sopenharmony_ci	for (i = 0; i < HSR_PT_PORTS; i++) {
16062306a36Sopenharmony_ci		new_node->time_in[i] = now;
16162306a36Sopenharmony_ci		new_node->time_out[i] = now;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci	for (i = 0; i < HSR_PT_PORTS; i++)
16462306a36Sopenharmony_ci		new_node->seq_out[i] = seq_out;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	if (san && hsr->proto_ops->handle_san_frame)
16762306a36Sopenharmony_ci		hsr->proto_ops->handle_san_frame(san, rx_port, new_node);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	spin_lock_bh(&hsr->list_lock);
17062306a36Sopenharmony_ci	list_for_each_entry_rcu(node, node_db, mac_list,
17162306a36Sopenharmony_ci				lockdep_is_held(&hsr->list_lock)) {
17262306a36Sopenharmony_ci		if (ether_addr_equal(node->macaddress_A, addr))
17362306a36Sopenharmony_ci			goto out;
17462306a36Sopenharmony_ci		if (ether_addr_equal(node->macaddress_B, addr))
17562306a36Sopenharmony_ci			goto out;
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci	list_add_tail_rcu(&new_node->mac_list, node_db);
17862306a36Sopenharmony_ci	spin_unlock_bh(&hsr->list_lock);
17962306a36Sopenharmony_ci	return new_node;
18062306a36Sopenharmony_ciout:
18162306a36Sopenharmony_ci	spin_unlock_bh(&hsr->list_lock);
18262306a36Sopenharmony_ci	kfree(new_node);
18362306a36Sopenharmony_ci	return node;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_civoid prp_update_san_info(struct hsr_node *node, bool is_sup)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	if (!is_sup)
18962306a36Sopenharmony_ci		return;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	node->san_a = false;
19262306a36Sopenharmony_ci	node->san_b = false;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci/* Get the hsr_node from which 'skb' was sent.
19662306a36Sopenharmony_ci */
19762306a36Sopenharmony_cistruct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db,
19862306a36Sopenharmony_ci			      struct sk_buff *skb, bool is_sup,
19962306a36Sopenharmony_ci			      enum hsr_port_type rx_port)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct hsr_priv *hsr = port->hsr;
20262306a36Sopenharmony_ci	struct hsr_node *node;
20362306a36Sopenharmony_ci	struct ethhdr *ethhdr;
20462306a36Sopenharmony_ci	struct prp_rct *rct;
20562306a36Sopenharmony_ci	bool san = false;
20662306a36Sopenharmony_ci	u16 seq_out;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (!skb_mac_header_was_set(skb))
20962306a36Sopenharmony_ci		return NULL;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	ethhdr = (struct ethhdr *)skb_mac_header(skb);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	list_for_each_entry_rcu(node, node_db, mac_list) {
21462306a36Sopenharmony_ci		if (ether_addr_equal(node->macaddress_A, ethhdr->h_source)) {
21562306a36Sopenharmony_ci			if (hsr->proto_ops->update_san_info)
21662306a36Sopenharmony_ci				hsr->proto_ops->update_san_info(node, is_sup);
21762306a36Sopenharmony_ci			return node;
21862306a36Sopenharmony_ci		}
21962306a36Sopenharmony_ci		if (ether_addr_equal(node->macaddress_B, ethhdr->h_source)) {
22062306a36Sopenharmony_ci			if (hsr->proto_ops->update_san_info)
22162306a36Sopenharmony_ci				hsr->proto_ops->update_san_info(node, is_sup);
22262306a36Sopenharmony_ci			return node;
22362306a36Sopenharmony_ci		}
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/* Everyone may create a node entry, connected node to a HSR/PRP
22762306a36Sopenharmony_ci	 * device.
22862306a36Sopenharmony_ci	 */
22962306a36Sopenharmony_ci	if (ethhdr->h_proto == htons(ETH_P_PRP) ||
23062306a36Sopenharmony_ci	    ethhdr->h_proto == htons(ETH_P_HSR)) {
23162306a36Sopenharmony_ci		/* Check if skb contains hsr_ethhdr */
23262306a36Sopenharmony_ci		if (skb->mac_len < sizeof(struct hsr_ethhdr))
23362306a36Sopenharmony_ci			return NULL;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci		/* Use the existing sequence_nr from the tag as starting point
23662306a36Sopenharmony_ci		 * for filtering duplicate frames.
23762306a36Sopenharmony_ci		 */
23862306a36Sopenharmony_ci		seq_out = hsr_get_skb_sequence_nr(skb) - 1;
23962306a36Sopenharmony_ci	} else {
24062306a36Sopenharmony_ci		rct = skb_get_PRP_rct(skb);
24162306a36Sopenharmony_ci		if (rct && prp_check_lsdu_size(skb, rct, is_sup)) {
24262306a36Sopenharmony_ci			seq_out = prp_get_skb_sequence_nr(rct);
24362306a36Sopenharmony_ci		} else {
24462306a36Sopenharmony_ci			if (rx_port != HSR_PT_MASTER)
24562306a36Sopenharmony_ci				san = true;
24662306a36Sopenharmony_ci			seq_out = HSR_SEQNR_START;
24762306a36Sopenharmony_ci		}
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	return hsr_add_node(hsr, node_db, ethhdr->h_source, seq_out,
25162306a36Sopenharmony_ci			    san, rx_port);
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci/* Use the Supervision frame's info about an eventual macaddress_B for merging
25562306a36Sopenharmony_ci * nodes that has previously had their macaddress_B registered as a separate
25662306a36Sopenharmony_ci * node.
25762306a36Sopenharmony_ci */
25862306a36Sopenharmony_civoid hsr_handle_sup_frame(struct hsr_frame_info *frame)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct hsr_node *node_curr = frame->node_src;
26162306a36Sopenharmony_ci	struct hsr_port *port_rcv = frame->port_rcv;
26262306a36Sopenharmony_ci	struct hsr_priv *hsr = port_rcv->hsr;
26362306a36Sopenharmony_ci	struct hsr_sup_payload *hsr_sp;
26462306a36Sopenharmony_ci	struct hsr_sup_tlv *hsr_sup_tlv;
26562306a36Sopenharmony_ci	struct hsr_node *node_real;
26662306a36Sopenharmony_ci	struct sk_buff *skb = NULL;
26762306a36Sopenharmony_ci	struct list_head *node_db;
26862306a36Sopenharmony_ci	struct ethhdr *ethhdr;
26962306a36Sopenharmony_ci	int i;
27062306a36Sopenharmony_ci	unsigned int pull_size = 0;
27162306a36Sopenharmony_ci	unsigned int total_pull_size = 0;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/* Here either frame->skb_hsr or frame->skb_prp should be
27462306a36Sopenharmony_ci	 * valid as supervision frame always will have protocol
27562306a36Sopenharmony_ci	 * header info.
27662306a36Sopenharmony_ci	 */
27762306a36Sopenharmony_ci	if (frame->skb_hsr)
27862306a36Sopenharmony_ci		skb = frame->skb_hsr;
27962306a36Sopenharmony_ci	else if (frame->skb_prp)
28062306a36Sopenharmony_ci		skb = frame->skb_prp;
28162306a36Sopenharmony_ci	else if (frame->skb_std)
28262306a36Sopenharmony_ci		skb = frame->skb_std;
28362306a36Sopenharmony_ci	if (!skb)
28462306a36Sopenharmony_ci		return;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/* Leave the ethernet header. */
28762306a36Sopenharmony_ci	pull_size = sizeof(struct ethhdr);
28862306a36Sopenharmony_ci	skb_pull(skb, pull_size);
28962306a36Sopenharmony_ci	total_pull_size += pull_size;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	ethhdr = (struct ethhdr *)skb_mac_header(skb);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/* And leave the HSR tag. */
29462306a36Sopenharmony_ci	if (ethhdr->h_proto == htons(ETH_P_HSR)) {
29562306a36Sopenharmony_ci		pull_size = sizeof(struct hsr_tag);
29662306a36Sopenharmony_ci		skb_pull(skb, pull_size);
29762306a36Sopenharmony_ci		total_pull_size += pull_size;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	/* And leave the HSR sup tag. */
30162306a36Sopenharmony_ci	pull_size = sizeof(struct hsr_sup_tag);
30262306a36Sopenharmony_ci	skb_pull(skb, pull_size);
30362306a36Sopenharmony_ci	total_pull_size += pull_size;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	/* get HSR sup payload */
30662306a36Sopenharmony_ci	hsr_sp = (struct hsr_sup_payload *)skb->data;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	/* Merge node_curr (registered on macaddress_B) into node_real */
30962306a36Sopenharmony_ci	node_db = &port_rcv->hsr->node_db;
31062306a36Sopenharmony_ci	node_real = find_node_by_addr_A(node_db, hsr_sp->macaddress_A);
31162306a36Sopenharmony_ci	if (!node_real)
31262306a36Sopenharmony_ci		/* No frame received from AddrA of this node yet */
31362306a36Sopenharmony_ci		node_real = hsr_add_node(hsr, node_db, hsr_sp->macaddress_A,
31462306a36Sopenharmony_ci					 HSR_SEQNR_START - 1, true,
31562306a36Sopenharmony_ci					 port_rcv->type);
31662306a36Sopenharmony_ci	if (!node_real)
31762306a36Sopenharmony_ci		goto done; /* No mem */
31862306a36Sopenharmony_ci	if (node_real == node_curr)
31962306a36Sopenharmony_ci		/* Node has already been merged */
32062306a36Sopenharmony_ci		goto done;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	/* Leave the first HSR sup payload. */
32362306a36Sopenharmony_ci	pull_size = sizeof(struct hsr_sup_payload);
32462306a36Sopenharmony_ci	skb_pull(skb, pull_size);
32562306a36Sopenharmony_ci	total_pull_size += pull_size;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	/* Get second supervision tlv */
32862306a36Sopenharmony_ci	hsr_sup_tlv = (struct hsr_sup_tlv *)skb->data;
32962306a36Sopenharmony_ci	/* And check if it is a redbox mac TLV */
33062306a36Sopenharmony_ci	if (hsr_sup_tlv->HSR_TLV_type == PRP_TLV_REDBOX_MAC) {
33162306a36Sopenharmony_ci		/* We could stop here after pushing hsr_sup_payload,
33262306a36Sopenharmony_ci		 * or proceed and allow macaddress_B and for redboxes.
33362306a36Sopenharmony_ci		 */
33462306a36Sopenharmony_ci		/* Sanity check length */
33562306a36Sopenharmony_ci		if (hsr_sup_tlv->HSR_TLV_length != 6)
33662306a36Sopenharmony_ci			goto done;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci		/* Leave the second HSR sup tlv. */
33962306a36Sopenharmony_ci		pull_size = sizeof(struct hsr_sup_tlv);
34062306a36Sopenharmony_ci		skb_pull(skb, pull_size);
34162306a36Sopenharmony_ci		total_pull_size += pull_size;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci		/* Get redbox mac address. */
34462306a36Sopenharmony_ci		hsr_sp = (struct hsr_sup_payload *)skb->data;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci		/* Check if redbox mac and node mac are equal. */
34762306a36Sopenharmony_ci		if (!ether_addr_equal(node_real->macaddress_A, hsr_sp->macaddress_A)) {
34862306a36Sopenharmony_ci			/* This is a redbox supervision frame for a VDAN! */
34962306a36Sopenharmony_ci			goto done;
35062306a36Sopenharmony_ci		}
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	ether_addr_copy(node_real->macaddress_B, ethhdr->h_source);
35462306a36Sopenharmony_ci	spin_lock_bh(&node_real->seq_out_lock);
35562306a36Sopenharmony_ci	for (i = 0; i < HSR_PT_PORTS; i++) {
35662306a36Sopenharmony_ci		if (!node_curr->time_in_stale[i] &&
35762306a36Sopenharmony_ci		    time_after(node_curr->time_in[i], node_real->time_in[i])) {
35862306a36Sopenharmony_ci			node_real->time_in[i] = node_curr->time_in[i];
35962306a36Sopenharmony_ci			node_real->time_in_stale[i] =
36062306a36Sopenharmony_ci						node_curr->time_in_stale[i];
36162306a36Sopenharmony_ci		}
36262306a36Sopenharmony_ci		if (seq_nr_after(node_curr->seq_out[i], node_real->seq_out[i]))
36362306a36Sopenharmony_ci			node_real->seq_out[i] = node_curr->seq_out[i];
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci	spin_unlock_bh(&node_real->seq_out_lock);
36662306a36Sopenharmony_ci	node_real->addr_B_port = port_rcv->type;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	spin_lock_bh(&hsr->list_lock);
36962306a36Sopenharmony_ci	if (!node_curr->removed) {
37062306a36Sopenharmony_ci		list_del_rcu(&node_curr->mac_list);
37162306a36Sopenharmony_ci		node_curr->removed = true;
37262306a36Sopenharmony_ci		kfree_rcu(node_curr, rcu_head);
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci	spin_unlock_bh(&hsr->list_lock);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cidone:
37762306a36Sopenharmony_ci	/* Push back here */
37862306a36Sopenharmony_ci	skb_push(skb, total_pull_size);
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci/* 'skb' is a frame meant for this host, that is to be passed to upper layers.
38262306a36Sopenharmony_ci *
38362306a36Sopenharmony_ci * If the frame was sent by a node's B interface, replace the source
38462306a36Sopenharmony_ci * address with that node's "official" address (macaddress_A) so that upper
38562306a36Sopenharmony_ci * layers recognize where it came from.
38662306a36Sopenharmony_ci */
38762306a36Sopenharmony_civoid hsr_addr_subst_source(struct hsr_node *node, struct sk_buff *skb)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	if (!skb_mac_header_was_set(skb)) {
39062306a36Sopenharmony_ci		WARN_ONCE(1, "%s: Mac header not set\n", __func__);
39162306a36Sopenharmony_ci		return;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	memcpy(&eth_hdr(skb)->h_source, node->macaddress_A, ETH_ALEN);
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci/* 'skb' is a frame meant for another host.
39862306a36Sopenharmony_ci * 'port' is the outgoing interface
39962306a36Sopenharmony_ci *
40062306a36Sopenharmony_ci * Substitute the target (dest) MAC address if necessary, so the it matches the
40162306a36Sopenharmony_ci * recipient interface MAC address, regardless of whether that is the
40262306a36Sopenharmony_ci * recipient's A or B interface.
40362306a36Sopenharmony_ci * This is needed to keep the packets flowing through switches that learn on
40462306a36Sopenharmony_ci * which "side" the different interfaces are.
40562306a36Sopenharmony_ci */
40662306a36Sopenharmony_civoid hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb,
40762306a36Sopenharmony_ci			 struct hsr_port *port)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	struct hsr_node *node_dst;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (!skb_mac_header_was_set(skb)) {
41262306a36Sopenharmony_ci		WARN_ONCE(1, "%s: Mac header not set\n", __func__);
41362306a36Sopenharmony_ci		return;
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	if (!is_unicast_ether_addr(eth_hdr(skb)->h_dest))
41762306a36Sopenharmony_ci		return;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	node_dst = find_node_by_addr_A(&port->hsr->node_db,
42062306a36Sopenharmony_ci				       eth_hdr(skb)->h_dest);
42162306a36Sopenharmony_ci	if (!node_dst) {
42262306a36Sopenharmony_ci		if (port->hsr->prot_version != PRP_V1 && net_ratelimit())
42362306a36Sopenharmony_ci			netdev_err(skb->dev, "%s: Unknown node\n", __func__);
42462306a36Sopenharmony_ci		return;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci	if (port->type != node_dst->addr_B_port)
42762306a36Sopenharmony_ci		return;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (is_valid_ether_addr(node_dst->macaddress_B))
43062306a36Sopenharmony_ci		ether_addr_copy(eth_hdr(skb)->h_dest, node_dst->macaddress_B);
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_civoid hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port,
43462306a36Sopenharmony_ci			   u16 sequence_nr)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	/* Don't register incoming frames without a valid sequence number. This
43762306a36Sopenharmony_ci	 * ensures entries of restarted nodes gets pruned so that they can
43862306a36Sopenharmony_ci	 * re-register and resume communications.
43962306a36Sopenharmony_ci	 */
44062306a36Sopenharmony_ci	if (!(port->dev->features & NETIF_F_HW_HSR_TAG_RM) &&
44162306a36Sopenharmony_ci	    seq_nr_before(sequence_nr, node->seq_out[port->type]))
44262306a36Sopenharmony_ci		return;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	node->time_in[port->type] = jiffies;
44562306a36Sopenharmony_ci	node->time_in_stale[port->type] = false;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci/* 'skb' is a HSR Ethernet frame (with a HSR tag inserted), with a valid
44962306a36Sopenharmony_ci * ethhdr->h_source address and skb->mac_header set.
45062306a36Sopenharmony_ci *
45162306a36Sopenharmony_ci * Return:
45262306a36Sopenharmony_ci *	 1 if frame can be shown to have been sent recently on this interface,
45362306a36Sopenharmony_ci *	 0 otherwise, or
45462306a36Sopenharmony_ci *	 negative error code on error
45562306a36Sopenharmony_ci */
45662306a36Sopenharmony_ciint hsr_register_frame_out(struct hsr_port *port, struct hsr_node *node,
45762306a36Sopenharmony_ci			   u16 sequence_nr)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	spin_lock_bh(&node->seq_out_lock);
46062306a36Sopenharmony_ci	if (seq_nr_before_or_eq(sequence_nr, node->seq_out[port->type]) &&
46162306a36Sopenharmony_ci	    time_is_after_jiffies(node->time_out[port->type] +
46262306a36Sopenharmony_ci	    msecs_to_jiffies(HSR_ENTRY_FORGET_TIME))) {
46362306a36Sopenharmony_ci		spin_unlock_bh(&node->seq_out_lock);
46462306a36Sopenharmony_ci		return 1;
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	node->time_out[port->type] = jiffies;
46862306a36Sopenharmony_ci	node->seq_out[port->type] = sequence_nr;
46962306a36Sopenharmony_ci	spin_unlock_bh(&node->seq_out_lock);
47062306a36Sopenharmony_ci	return 0;
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cistatic struct hsr_port *get_late_port(struct hsr_priv *hsr,
47462306a36Sopenharmony_ci				      struct hsr_node *node)
47562306a36Sopenharmony_ci{
47662306a36Sopenharmony_ci	if (node->time_in_stale[HSR_PT_SLAVE_A])
47762306a36Sopenharmony_ci		return hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A);
47862306a36Sopenharmony_ci	if (node->time_in_stale[HSR_PT_SLAVE_B])
47962306a36Sopenharmony_ci		return hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	if (time_after(node->time_in[HSR_PT_SLAVE_B],
48262306a36Sopenharmony_ci		       node->time_in[HSR_PT_SLAVE_A] +
48362306a36Sopenharmony_ci					msecs_to_jiffies(MAX_SLAVE_DIFF)))
48462306a36Sopenharmony_ci		return hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A);
48562306a36Sopenharmony_ci	if (time_after(node->time_in[HSR_PT_SLAVE_A],
48662306a36Sopenharmony_ci		       node->time_in[HSR_PT_SLAVE_B] +
48762306a36Sopenharmony_ci					msecs_to_jiffies(MAX_SLAVE_DIFF)))
48862306a36Sopenharmony_ci		return hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	return NULL;
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci/* Remove stale sequence_nr records. Called by timer every
49462306a36Sopenharmony_ci * HSR_LIFE_CHECK_INTERVAL (two seconds or so).
49562306a36Sopenharmony_ci */
49662306a36Sopenharmony_civoid hsr_prune_nodes(struct timer_list *t)
49762306a36Sopenharmony_ci{
49862306a36Sopenharmony_ci	struct hsr_priv *hsr = from_timer(hsr, t, prune_timer);
49962306a36Sopenharmony_ci	struct hsr_node *node;
50062306a36Sopenharmony_ci	struct hsr_node *tmp;
50162306a36Sopenharmony_ci	struct hsr_port *port;
50262306a36Sopenharmony_ci	unsigned long timestamp;
50362306a36Sopenharmony_ci	unsigned long time_a, time_b;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	spin_lock_bh(&hsr->list_lock);
50662306a36Sopenharmony_ci	list_for_each_entry_safe(node, tmp, &hsr->node_db, mac_list) {
50762306a36Sopenharmony_ci		/* Don't prune own node. Neither time_in[HSR_PT_SLAVE_A]
50862306a36Sopenharmony_ci		 * nor time_in[HSR_PT_SLAVE_B], will ever be updated for
50962306a36Sopenharmony_ci		 * the master port. Thus the master node will be repeatedly
51062306a36Sopenharmony_ci		 * pruned leading to packet loss.
51162306a36Sopenharmony_ci		 */
51262306a36Sopenharmony_ci		if (hsr_addr_is_self(hsr, node->macaddress_A))
51362306a36Sopenharmony_ci			continue;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci		/* Shorthand */
51662306a36Sopenharmony_ci		time_a = node->time_in[HSR_PT_SLAVE_A];
51762306a36Sopenharmony_ci		time_b = node->time_in[HSR_PT_SLAVE_B];
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		/* Check for timestamps old enough to risk wrap-around */
52062306a36Sopenharmony_ci		if (time_after(jiffies, time_a + MAX_JIFFY_OFFSET / 2))
52162306a36Sopenharmony_ci			node->time_in_stale[HSR_PT_SLAVE_A] = true;
52262306a36Sopenharmony_ci		if (time_after(jiffies, time_b + MAX_JIFFY_OFFSET / 2))
52362306a36Sopenharmony_ci			node->time_in_stale[HSR_PT_SLAVE_B] = true;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci		/* Get age of newest frame from node.
52662306a36Sopenharmony_ci		 * At least one time_in is OK here; nodes get pruned long
52762306a36Sopenharmony_ci		 * before both time_ins can get stale
52862306a36Sopenharmony_ci		 */
52962306a36Sopenharmony_ci		timestamp = time_a;
53062306a36Sopenharmony_ci		if (node->time_in_stale[HSR_PT_SLAVE_A] ||
53162306a36Sopenharmony_ci		    (!node->time_in_stale[HSR_PT_SLAVE_B] &&
53262306a36Sopenharmony_ci		    time_after(time_b, time_a)))
53362306a36Sopenharmony_ci			timestamp = time_b;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci		/* Warn of ring error only as long as we get frames at all */
53662306a36Sopenharmony_ci		if (time_is_after_jiffies(timestamp +
53762306a36Sopenharmony_ci				msecs_to_jiffies(1.5 * MAX_SLAVE_DIFF))) {
53862306a36Sopenharmony_ci			rcu_read_lock();
53962306a36Sopenharmony_ci			port = get_late_port(hsr, node);
54062306a36Sopenharmony_ci			if (port)
54162306a36Sopenharmony_ci				hsr_nl_ringerror(hsr, node->macaddress_A, port);
54262306a36Sopenharmony_ci			rcu_read_unlock();
54362306a36Sopenharmony_ci		}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci		/* Prune old entries */
54662306a36Sopenharmony_ci		if (time_is_before_jiffies(timestamp +
54762306a36Sopenharmony_ci				msecs_to_jiffies(HSR_NODE_FORGET_TIME))) {
54862306a36Sopenharmony_ci			hsr_nl_nodedown(hsr, node->macaddress_A);
54962306a36Sopenharmony_ci			if (!node->removed) {
55062306a36Sopenharmony_ci				list_del_rcu(&node->mac_list);
55162306a36Sopenharmony_ci				node->removed = true;
55262306a36Sopenharmony_ci				/* Note that we need to free this entry later: */
55362306a36Sopenharmony_ci				kfree_rcu(node, rcu_head);
55462306a36Sopenharmony_ci			}
55562306a36Sopenharmony_ci		}
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci	spin_unlock_bh(&hsr->list_lock);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	/* Restart timer */
56062306a36Sopenharmony_ci	mod_timer(&hsr->prune_timer,
56162306a36Sopenharmony_ci		  jiffies + msecs_to_jiffies(PRUNE_PERIOD));
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_civoid *hsr_get_next_node(struct hsr_priv *hsr, void *_pos,
56562306a36Sopenharmony_ci			unsigned char addr[ETH_ALEN])
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	struct hsr_node *node;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (!_pos) {
57062306a36Sopenharmony_ci		node = list_first_or_null_rcu(&hsr->node_db,
57162306a36Sopenharmony_ci					      struct hsr_node, mac_list);
57262306a36Sopenharmony_ci		if (node)
57362306a36Sopenharmony_ci			ether_addr_copy(addr, node->macaddress_A);
57462306a36Sopenharmony_ci		return node;
57562306a36Sopenharmony_ci	}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	node = _pos;
57862306a36Sopenharmony_ci	list_for_each_entry_continue_rcu(node, &hsr->node_db, mac_list) {
57962306a36Sopenharmony_ci		ether_addr_copy(addr, node->macaddress_A);
58062306a36Sopenharmony_ci		return node;
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	return NULL;
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ciint hsr_get_node_data(struct hsr_priv *hsr,
58762306a36Sopenharmony_ci		      const unsigned char *addr,
58862306a36Sopenharmony_ci		      unsigned char addr_b[ETH_ALEN],
58962306a36Sopenharmony_ci		      unsigned int *addr_b_ifindex,
59062306a36Sopenharmony_ci		      int *if1_age,
59162306a36Sopenharmony_ci		      u16 *if1_seq,
59262306a36Sopenharmony_ci		      int *if2_age,
59362306a36Sopenharmony_ci		      u16 *if2_seq)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	struct hsr_node *node;
59662306a36Sopenharmony_ci	struct hsr_port *port;
59762306a36Sopenharmony_ci	unsigned long tdiff;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	node = find_node_by_addr_A(&hsr->node_db, addr);
60062306a36Sopenharmony_ci	if (!node)
60162306a36Sopenharmony_ci		return -ENOENT;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	ether_addr_copy(addr_b, node->macaddress_B);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	tdiff = jiffies - node->time_in[HSR_PT_SLAVE_A];
60662306a36Sopenharmony_ci	if (node->time_in_stale[HSR_PT_SLAVE_A])
60762306a36Sopenharmony_ci		*if1_age = INT_MAX;
60862306a36Sopenharmony_ci#if HZ <= MSEC_PER_SEC
60962306a36Sopenharmony_ci	else if (tdiff > msecs_to_jiffies(INT_MAX))
61062306a36Sopenharmony_ci		*if1_age = INT_MAX;
61162306a36Sopenharmony_ci#endif
61262306a36Sopenharmony_ci	else
61362306a36Sopenharmony_ci		*if1_age = jiffies_to_msecs(tdiff);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	tdiff = jiffies - node->time_in[HSR_PT_SLAVE_B];
61662306a36Sopenharmony_ci	if (node->time_in_stale[HSR_PT_SLAVE_B])
61762306a36Sopenharmony_ci		*if2_age = INT_MAX;
61862306a36Sopenharmony_ci#if HZ <= MSEC_PER_SEC
61962306a36Sopenharmony_ci	else if (tdiff > msecs_to_jiffies(INT_MAX))
62062306a36Sopenharmony_ci		*if2_age = INT_MAX;
62162306a36Sopenharmony_ci#endif
62262306a36Sopenharmony_ci	else
62362306a36Sopenharmony_ci		*if2_age = jiffies_to_msecs(tdiff);
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	/* Present sequence numbers as if they were incoming on interface */
62662306a36Sopenharmony_ci	*if1_seq = node->seq_out[HSR_PT_SLAVE_B];
62762306a36Sopenharmony_ci	*if2_seq = node->seq_out[HSR_PT_SLAVE_A];
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	if (node->addr_B_port != HSR_PT_NONE) {
63062306a36Sopenharmony_ci		port = hsr_port_get_hsr(hsr, node->addr_B_port);
63162306a36Sopenharmony_ci		*addr_b_ifindex = port->dev->ifindex;
63262306a36Sopenharmony_ci	} else {
63362306a36Sopenharmony_ci		*addr_b_ifindex = -1;
63462306a36Sopenharmony_ci	}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	return 0;
63762306a36Sopenharmony_ci}
638