18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright 2011-2014 Autronica Fire and Security AS
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Author(s):
58c2ecf20Sopenharmony_ci *	2011-2014 Arvid Brodin, arvid.brodin@alten.se
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * The HSR spec says never to forward the same frame twice on the same
88c2ecf20Sopenharmony_ci * interface. A frame is identified by its source MAC address and its HSR
98c2ecf20Sopenharmony_ci * sequence number. This code keeps track of senders and their sequence numbers
108c2ecf20Sopenharmony_ci * to allow filtering of duplicate frames, and to detect HSR ring errors.
118c2ecf20Sopenharmony_ci * Same code handles filtering of duplicates for PRP as well.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
158c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/rculist.h>
188c2ecf20Sopenharmony_ci#include "hsr_main.h"
198c2ecf20Sopenharmony_ci#include "hsr_framereg.h"
208c2ecf20Sopenharmony_ci#include "hsr_netlink.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/*	TODO: use hash lists for mac addresses (linux/jhash.h)?    */
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/* seq_nr_after(a, b) - return true if a is after (higher in sequence than) b,
258c2ecf20Sopenharmony_ci * false otherwise.
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_cistatic bool seq_nr_after(u16 a, u16 b)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	/* Remove inconsistency where
308c2ecf20Sopenharmony_ci	 * seq_nr_after(a, b) == seq_nr_before(a, b)
318c2ecf20Sopenharmony_ci	 */
328c2ecf20Sopenharmony_ci	if ((int)b - a == 32768)
338c2ecf20Sopenharmony_ci		return false;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	return (((s16)(b - a)) < 0);
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define seq_nr_before(a, b)		seq_nr_after((b), (a))
398c2ecf20Sopenharmony_ci#define seq_nr_before_or_eq(a, b)	(!seq_nr_after((a), (b)))
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cibool hsr_addr_is_self(struct hsr_priv *hsr, unsigned char *addr)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	struct hsr_node *node;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	node = list_first_or_null_rcu(&hsr->self_node_db, struct hsr_node,
468c2ecf20Sopenharmony_ci				      mac_list);
478c2ecf20Sopenharmony_ci	if (!node) {
488c2ecf20Sopenharmony_ci		WARN_ONCE(1, "HSR: No self node\n");
498c2ecf20Sopenharmony_ci		return false;
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	if (ether_addr_equal(addr, node->macaddress_A))
538c2ecf20Sopenharmony_ci		return true;
548c2ecf20Sopenharmony_ci	if (ether_addr_equal(addr, node->macaddress_B))
558c2ecf20Sopenharmony_ci		return true;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return false;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/* Search for mac entry. Caller must hold rcu read lock.
618c2ecf20Sopenharmony_ci */
628c2ecf20Sopenharmony_cistatic struct hsr_node *find_node_by_addr_A(struct list_head *node_db,
638c2ecf20Sopenharmony_ci					    const unsigned char addr[ETH_ALEN])
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct hsr_node *node;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(node, node_db, mac_list) {
688c2ecf20Sopenharmony_ci		if (ether_addr_equal(node->macaddress_A, addr))
698c2ecf20Sopenharmony_ci			return node;
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	return NULL;
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/* Helper for device init; the self_node_db is used in hsr_rcv() to recognize
768c2ecf20Sopenharmony_ci * frames from self that's been looped over the HSR ring.
778c2ecf20Sopenharmony_ci */
788c2ecf20Sopenharmony_ciint hsr_create_self_node(struct hsr_priv *hsr,
798c2ecf20Sopenharmony_ci			 unsigned char addr_a[ETH_ALEN],
808c2ecf20Sopenharmony_ci			 unsigned char addr_b[ETH_ALEN])
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct list_head *self_node_db = &hsr->self_node_db;
838c2ecf20Sopenharmony_ci	struct hsr_node *node, *oldnode;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	node = kmalloc(sizeof(*node), GFP_KERNEL);
868c2ecf20Sopenharmony_ci	if (!node)
878c2ecf20Sopenharmony_ci		return -ENOMEM;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	ether_addr_copy(node->macaddress_A, addr_a);
908c2ecf20Sopenharmony_ci	ether_addr_copy(node->macaddress_B, addr_b);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	spin_lock_bh(&hsr->list_lock);
938c2ecf20Sopenharmony_ci	oldnode = list_first_or_null_rcu(self_node_db,
948c2ecf20Sopenharmony_ci					 struct hsr_node, mac_list);
958c2ecf20Sopenharmony_ci	if (oldnode) {
968c2ecf20Sopenharmony_ci		list_replace_rcu(&oldnode->mac_list, &node->mac_list);
978c2ecf20Sopenharmony_ci		spin_unlock_bh(&hsr->list_lock);
988c2ecf20Sopenharmony_ci		kfree_rcu(oldnode, rcu_head);
998c2ecf20Sopenharmony_ci	} else {
1008c2ecf20Sopenharmony_ci		list_add_tail_rcu(&node->mac_list, self_node_db);
1018c2ecf20Sopenharmony_ci		spin_unlock_bh(&hsr->list_lock);
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	return 0;
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_civoid hsr_del_self_node(struct hsr_priv *hsr)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	struct list_head *self_node_db = &hsr->self_node_db;
1108c2ecf20Sopenharmony_ci	struct hsr_node *node;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	spin_lock_bh(&hsr->list_lock);
1138c2ecf20Sopenharmony_ci	node = list_first_or_null_rcu(self_node_db, struct hsr_node, mac_list);
1148c2ecf20Sopenharmony_ci	if (node) {
1158c2ecf20Sopenharmony_ci		list_del_rcu(&node->mac_list);
1168c2ecf20Sopenharmony_ci		kfree_rcu(node, rcu_head);
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci	spin_unlock_bh(&hsr->list_lock);
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_civoid hsr_del_nodes(struct list_head *node_db)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct hsr_node *node;
1248c2ecf20Sopenharmony_ci	struct hsr_node *tmp;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	list_for_each_entry_safe(node, tmp, node_db, mac_list)
1278c2ecf20Sopenharmony_ci		kfree(node);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_civoid prp_handle_san_frame(bool san, enum hsr_port_type port,
1318c2ecf20Sopenharmony_ci			  struct hsr_node *node)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	/* Mark if the SAN node is over LAN_A or LAN_B */
1348c2ecf20Sopenharmony_ci	if (port == HSR_PT_SLAVE_A) {
1358c2ecf20Sopenharmony_ci		node->san_a = true;
1368c2ecf20Sopenharmony_ci		return;
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if (port == HSR_PT_SLAVE_B)
1408c2ecf20Sopenharmony_ci		node->san_b = true;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci/* Allocate an hsr_node and add it to node_db. 'addr' is the node's address_A;
1448c2ecf20Sopenharmony_ci * seq_out is used to initialize filtering of outgoing duplicate frames
1458c2ecf20Sopenharmony_ci * originating from the newly added node.
1468c2ecf20Sopenharmony_ci */
1478c2ecf20Sopenharmony_cistatic struct hsr_node *hsr_add_node(struct hsr_priv *hsr,
1488c2ecf20Sopenharmony_ci				     struct list_head *node_db,
1498c2ecf20Sopenharmony_ci				     unsigned char addr[],
1508c2ecf20Sopenharmony_ci				     u16 seq_out, bool san,
1518c2ecf20Sopenharmony_ci				     enum hsr_port_type rx_port)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct hsr_node *new_node, *node;
1548c2ecf20Sopenharmony_ci	unsigned long now;
1558c2ecf20Sopenharmony_ci	int i;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	new_node = kzalloc(sizeof(*new_node), GFP_ATOMIC);
1588c2ecf20Sopenharmony_ci	if (!new_node)
1598c2ecf20Sopenharmony_ci		return NULL;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	ether_addr_copy(new_node->macaddress_A, addr);
1628c2ecf20Sopenharmony_ci	spin_lock_init(&new_node->seq_out_lock);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	/* We are only interested in time diffs here, so use current jiffies
1658c2ecf20Sopenharmony_ci	 * as initialization. (0 could trigger an spurious ring error warning).
1668c2ecf20Sopenharmony_ci	 */
1678c2ecf20Sopenharmony_ci	now = jiffies;
1688c2ecf20Sopenharmony_ci	for (i = 0; i < HSR_PT_PORTS; i++) {
1698c2ecf20Sopenharmony_ci		new_node->time_in[i] = now;
1708c2ecf20Sopenharmony_ci		new_node->time_out[i] = now;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci	for (i = 0; i < HSR_PT_PORTS; i++)
1738c2ecf20Sopenharmony_ci		new_node->seq_out[i] = seq_out;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (san && hsr->proto_ops->handle_san_frame)
1768c2ecf20Sopenharmony_ci		hsr->proto_ops->handle_san_frame(san, rx_port, new_node);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	spin_lock_bh(&hsr->list_lock);
1798c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(node, node_db, mac_list,
1808c2ecf20Sopenharmony_ci				lockdep_is_held(&hsr->list_lock)) {
1818c2ecf20Sopenharmony_ci		if (ether_addr_equal(node->macaddress_A, addr))
1828c2ecf20Sopenharmony_ci			goto out;
1838c2ecf20Sopenharmony_ci		if (ether_addr_equal(node->macaddress_B, addr))
1848c2ecf20Sopenharmony_ci			goto out;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci	list_add_tail_rcu(&new_node->mac_list, node_db);
1878c2ecf20Sopenharmony_ci	spin_unlock_bh(&hsr->list_lock);
1888c2ecf20Sopenharmony_ci	return new_node;
1898c2ecf20Sopenharmony_ciout:
1908c2ecf20Sopenharmony_ci	spin_unlock_bh(&hsr->list_lock);
1918c2ecf20Sopenharmony_ci	kfree(new_node);
1928c2ecf20Sopenharmony_ci	return node;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_civoid prp_update_san_info(struct hsr_node *node, bool is_sup)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	if (!is_sup)
1988c2ecf20Sopenharmony_ci		return;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	node->san_a = false;
2018c2ecf20Sopenharmony_ci	node->san_b = false;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci/* Get the hsr_node from which 'skb' was sent.
2058c2ecf20Sopenharmony_ci */
2068c2ecf20Sopenharmony_cistruct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db,
2078c2ecf20Sopenharmony_ci			      struct sk_buff *skb, bool is_sup,
2088c2ecf20Sopenharmony_ci			      enum hsr_port_type rx_port)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	struct hsr_priv *hsr = port->hsr;
2118c2ecf20Sopenharmony_ci	struct hsr_node *node;
2128c2ecf20Sopenharmony_ci	struct ethhdr *ethhdr;
2138c2ecf20Sopenharmony_ci	struct prp_rct *rct;
2148c2ecf20Sopenharmony_ci	bool san = false;
2158c2ecf20Sopenharmony_ci	u16 seq_out;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (!skb_mac_header_was_set(skb))
2188c2ecf20Sopenharmony_ci		return NULL;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	ethhdr = (struct ethhdr *)skb_mac_header(skb);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(node, node_db, mac_list) {
2238c2ecf20Sopenharmony_ci		if (ether_addr_equal(node->macaddress_A, ethhdr->h_source)) {
2248c2ecf20Sopenharmony_ci			if (hsr->proto_ops->update_san_info)
2258c2ecf20Sopenharmony_ci				hsr->proto_ops->update_san_info(node, is_sup);
2268c2ecf20Sopenharmony_ci			return node;
2278c2ecf20Sopenharmony_ci		}
2288c2ecf20Sopenharmony_ci		if (ether_addr_equal(node->macaddress_B, ethhdr->h_source)) {
2298c2ecf20Sopenharmony_ci			if (hsr->proto_ops->update_san_info)
2308c2ecf20Sopenharmony_ci				hsr->proto_ops->update_san_info(node, is_sup);
2318c2ecf20Sopenharmony_ci			return node;
2328c2ecf20Sopenharmony_ci		}
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	/* Everyone may create a node entry, connected node to a HSR/PRP
2368c2ecf20Sopenharmony_ci	 * device.
2378c2ecf20Sopenharmony_ci	 */
2388c2ecf20Sopenharmony_ci	if (ethhdr->h_proto == htons(ETH_P_PRP) ||
2398c2ecf20Sopenharmony_ci	    ethhdr->h_proto == htons(ETH_P_HSR)) {
2408c2ecf20Sopenharmony_ci		/* Use the existing sequence_nr from the tag as starting point
2418c2ecf20Sopenharmony_ci		 * for filtering duplicate frames.
2428c2ecf20Sopenharmony_ci		 */
2438c2ecf20Sopenharmony_ci		seq_out = hsr_get_skb_sequence_nr(skb) - 1;
2448c2ecf20Sopenharmony_ci	} else {
2458c2ecf20Sopenharmony_ci		rct = skb_get_PRP_rct(skb);
2468c2ecf20Sopenharmony_ci		if (rct && prp_check_lsdu_size(skb, rct, is_sup)) {
2478c2ecf20Sopenharmony_ci			seq_out = prp_get_skb_sequence_nr(rct);
2488c2ecf20Sopenharmony_ci		} else {
2498c2ecf20Sopenharmony_ci			if (rx_port != HSR_PT_MASTER)
2508c2ecf20Sopenharmony_ci				san = true;
2518c2ecf20Sopenharmony_ci			seq_out = HSR_SEQNR_START;
2528c2ecf20Sopenharmony_ci		}
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	return hsr_add_node(hsr, node_db, ethhdr->h_source, seq_out,
2568c2ecf20Sopenharmony_ci			    san, rx_port);
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci/* Use the Supervision frame's info about an eventual macaddress_B for merging
2608c2ecf20Sopenharmony_ci * nodes that has previously had their macaddress_B registered as a separate
2618c2ecf20Sopenharmony_ci * node.
2628c2ecf20Sopenharmony_ci */
2638c2ecf20Sopenharmony_civoid hsr_handle_sup_frame(struct hsr_frame_info *frame)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	struct hsr_node *node_curr = frame->node_src;
2668c2ecf20Sopenharmony_ci	struct hsr_port *port_rcv = frame->port_rcv;
2678c2ecf20Sopenharmony_ci	struct hsr_priv *hsr = port_rcv->hsr;
2688c2ecf20Sopenharmony_ci	struct hsr_sup_payload *hsr_sp;
2698c2ecf20Sopenharmony_ci	struct hsr_node *node_real;
2708c2ecf20Sopenharmony_ci	struct sk_buff *skb = NULL;
2718c2ecf20Sopenharmony_ci	struct list_head *node_db;
2728c2ecf20Sopenharmony_ci	struct ethhdr *ethhdr;
2738c2ecf20Sopenharmony_ci	int i;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	/* Here either frame->skb_hsr or frame->skb_prp should be
2768c2ecf20Sopenharmony_ci	 * valid as supervision frame always will have protocol
2778c2ecf20Sopenharmony_ci	 * header info.
2788c2ecf20Sopenharmony_ci	 */
2798c2ecf20Sopenharmony_ci	if (frame->skb_hsr)
2808c2ecf20Sopenharmony_ci		skb = frame->skb_hsr;
2818c2ecf20Sopenharmony_ci	else if (frame->skb_prp)
2828c2ecf20Sopenharmony_ci		skb = frame->skb_prp;
2838c2ecf20Sopenharmony_ci	if (!skb)
2848c2ecf20Sopenharmony_ci		return;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	ethhdr = (struct ethhdr *)skb_mac_header(skb);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	/* Leave the ethernet header. */
2898c2ecf20Sopenharmony_ci	skb_pull(skb, sizeof(struct ethhdr));
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	/* And leave the HSR tag. */
2928c2ecf20Sopenharmony_ci	if (ethhdr->h_proto == htons(ETH_P_HSR))
2938c2ecf20Sopenharmony_ci		skb_pull(skb, sizeof(struct hsr_tag));
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	/* And leave the HSR sup tag. */
2968c2ecf20Sopenharmony_ci	skb_pull(skb, sizeof(struct hsr_sup_tag));
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	hsr_sp = (struct hsr_sup_payload *)skb->data;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	/* Merge node_curr (registered on macaddress_B) into node_real */
3018c2ecf20Sopenharmony_ci	node_db = &port_rcv->hsr->node_db;
3028c2ecf20Sopenharmony_ci	node_real = find_node_by_addr_A(node_db, hsr_sp->macaddress_A);
3038c2ecf20Sopenharmony_ci	if (!node_real)
3048c2ecf20Sopenharmony_ci		/* No frame received from AddrA of this node yet */
3058c2ecf20Sopenharmony_ci		node_real = hsr_add_node(hsr, node_db, hsr_sp->macaddress_A,
3068c2ecf20Sopenharmony_ci					 HSR_SEQNR_START - 1, true,
3078c2ecf20Sopenharmony_ci					 port_rcv->type);
3088c2ecf20Sopenharmony_ci	if (!node_real)
3098c2ecf20Sopenharmony_ci		goto done; /* No mem */
3108c2ecf20Sopenharmony_ci	if (node_real == node_curr)
3118c2ecf20Sopenharmony_ci		/* Node has already been merged */
3128c2ecf20Sopenharmony_ci		goto done;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	ether_addr_copy(node_real->macaddress_B, ethhdr->h_source);
3158c2ecf20Sopenharmony_ci	spin_lock_bh(&node_real->seq_out_lock);
3168c2ecf20Sopenharmony_ci	for (i = 0; i < HSR_PT_PORTS; i++) {
3178c2ecf20Sopenharmony_ci		if (!node_curr->time_in_stale[i] &&
3188c2ecf20Sopenharmony_ci		    time_after(node_curr->time_in[i], node_real->time_in[i])) {
3198c2ecf20Sopenharmony_ci			node_real->time_in[i] = node_curr->time_in[i];
3208c2ecf20Sopenharmony_ci			node_real->time_in_stale[i] =
3218c2ecf20Sopenharmony_ci						node_curr->time_in_stale[i];
3228c2ecf20Sopenharmony_ci		}
3238c2ecf20Sopenharmony_ci		if (seq_nr_after(node_curr->seq_out[i], node_real->seq_out[i]))
3248c2ecf20Sopenharmony_ci			node_real->seq_out[i] = node_curr->seq_out[i];
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci	spin_unlock_bh(&node_real->seq_out_lock);
3278c2ecf20Sopenharmony_ci	node_real->addr_B_port = port_rcv->type;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	spin_lock_bh(&hsr->list_lock);
3308c2ecf20Sopenharmony_ci	list_del_rcu(&node_curr->mac_list);
3318c2ecf20Sopenharmony_ci	spin_unlock_bh(&hsr->list_lock);
3328c2ecf20Sopenharmony_ci	kfree_rcu(node_curr, rcu_head);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cidone:
3358c2ecf20Sopenharmony_ci	/* PRP uses v0 header */
3368c2ecf20Sopenharmony_ci	if (ethhdr->h_proto == htons(ETH_P_HSR))
3378c2ecf20Sopenharmony_ci		skb_push(skb, sizeof(struct hsrv1_ethhdr_sp));
3388c2ecf20Sopenharmony_ci	else
3398c2ecf20Sopenharmony_ci		skb_push(skb, sizeof(struct hsrv0_ethhdr_sp));
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci/* 'skb' is a frame meant for this host, that is to be passed to upper layers.
3438c2ecf20Sopenharmony_ci *
3448c2ecf20Sopenharmony_ci * If the frame was sent by a node's B interface, replace the source
3458c2ecf20Sopenharmony_ci * address with that node's "official" address (macaddress_A) so that upper
3468c2ecf20Sopenharmony_ci * layers recognize where it came from.
3478c2ecf20Sopenharmony_ci */
3488c2ecf20Sopenharmony_civoid hsr_addr_subst_source(struct hsr_node *node, struct sk_buff *skb)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	if (!skb_mac_header_was_set(skb)) {
3518c2ecf20Sopenharmony_ci		WARN_ONCE(1, "%s: Mac header not set\n", __func__);
3528c2ecf20Sopenharmony_ci		return;
3538c2ecf20Sopenharmony_ci	}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	memcpy(&eth_hdr(skb)->h_source, node->macaddress_A, ETH_ALEN);
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci/* 'skb' is a frame meant for another host.
3598c2ecf20Sopenharmony_ci * 'port' is the outgoing interface
3608c2ecf20Sopenharmony_ci *
3618c2ecf20Sopenharmony_ci * Substitute the target (dest) MAC address if necessary, so the it matches the
3628c2ecf20Sopenharmony_ci * recipient interface MAC address, regardless of whether that is the
3638c2ecf20Sopenharmony_ci * recipient's A or B interface.
3648c2ecf20Sopenharmony_ci * This is needed to keep the packets flowing through switches that learn on
3658c2ecf20Sopenharmony_ci * which "side" the different interfaces are.
3668c2ecf20Sopenharmony_ci */
3678c2ecf20Sopenharmony_civoid hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb,
3688c2ecf20Sopenharmony_ci			 struct hsr_port *port)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct hsr_node *node_dst;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (!skb_mac_header_was_set(skb)) {
3738c2ecf20Sopenharmony_ci		WARN_ONCE(1, "%s: Mac header not set\n", __func__);
3748c2ecf20Sopenharmony_ci		return;
3758c2ecf20Sopenharmony_ci	}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	if (!is_unicast_ether_addr(eth_hdr(skb)->h_dest))
3788c2ecf20Sopenharmony_ci		return;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	node_dst = find_node_by_addr_A(&port->hsr->node_db,
3818c2ecf20Sopenharmony_ci				       eth_hdr(skb)->h_dest);
3828c2ecf20Sopenharmony_ci	if (!node_dst) {
3838c2ecf20Sopenharmony_ci		if (port->hsr->prot_version != PRP_V1 && net_ratelimit())
3848c2ecf20Sopenharmony_ci			netdev_err(skb->dev, "%s: Unknown node\n", __func__);
3858c2ecf20Sopenharmony_ci		return;
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci	if (port->type != node_dst->addr_B_port)
3888c2ecf20Sopenharmony_ci		return;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	if (is_valid_ether_addr(node_dst->macaddress_B))
3918c2ecf20Sopenharmony_ci		ether_addr_copy(eth_hdr(skb)->h_dest, node_dst->macaddress_B);
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_civoid hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port,
3958c2ecf20Sopenharmony_ci			   u16 sequence_nr)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	/* Don't register incoming frames without a valid sequence number. This
3988c2ecf20Sopenharmony_ci	 * ensures entries of restarted nodes gets pruned so that they can
3998c2ecf20Sopenharmony_ci	 * re-register and resume communications.
4008c2ecf20Sopenharmony_ci	 */
4018c2ecf20Sopenharmony_ci	if (seq_nr_before(sequence_nr, node->seq_out[port->type]))
4028c2ecf20Sopenharmony_ci		return;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	node->time_in[port->type] = jiffies;
4058c2ecf20Sopenharmony_ci	node->time_in_stale[port->type] = false;
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci/* 'skb' is a HSR Ethernet frame (with a HSR tag inserted), with a valid
4098c2ecf20Sopenharmony_ci * ethhdr->h_source address and skb->mac_header set.
4108c2ecf20Sopenharmony_ci *
4118c2ecf20Sopenharmony_ci * Return:
4128c2ecf20Sopenharmony_ci *	 1 if frame can be shown to have been sent recently on this interface,
4138c2ecf20Sopenharmony_ci *	 0 otherwise, or
4148c2ecf20Sopenharmony_ci *	 negative error code on error
4158c2ecf20Sopenharmony_ci */
4168c2ecf20Sopenharmony_ciint hsr_register_frame_out(struct hsr_port *port, struct hsr_node *node,
4178c2ecf20Sopenharmony_ci			   u16 sequence_nr)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	spin_lock_bh(&node->seq_out_lock);
4208c2ecf20Sopenharmony_ci	if (seq_nr_before_or_eq(sequence_nr, node->seq_out[port->type]) &&
4218c2ecf20Sopenharmony_ci	    time_is_after_jiffies(node->time_out[port->type] +
4228c2ecf20Sopenharmony_ci	    msecs_to_jiffies(HSR_ENTRY_FORGET_TIME))) {
4238c2ecf20Sopenharmony_ci		spin_unlock_bh(&node->seq_out_lock);
4248c2ecf20Sopenharmony_ci		return 1;
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	node->time_out[port->type] = jiffies;
4288c2ecf20Sopenharmony_ci	node->seq_out[port->type] = sequence_nr;
4298c2ecf20Sopenharmony_ci	spin_unlock_bh(&node->seq_out_lock);
4308c2ecf20Sopenharmony_ci	return 0;
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cistatic struct hsr_port *get_late_port(struct hsr_priv *hsr,
4348c2ecf20Sopenharmony_ci				      struct hsr_node *node)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	if (node->time_in_stale[HSR_PT_SLAVE_A])
4378c2ecf20Sopenharmony_ci		return hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A);
4388c2ecf20Sopenharmony_ci	if (node->time_in_stale[HSR_PT_SLAVE_B])
4398c2ecf20Sopenharmony_ci		return hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if (time_after(node->time_in[HSR_PT_SLAVE_B],
4428c2ecf20Sopenharmony_ci		       node->time_in[HSR_PT_SLAVE_A] +
4438c2ecf20Sopenharmony_ci					msecs_to_jiffies(MAX_SLAVE_DIFF)))
4448c2ecf20Sopenharmony_ci		return hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A);
4458c2ecf20Sopenharmony_ci	if (time_after(node->time_in[HSR_PT_SLAVE_A],
4468c2ecf20Sopenharmony_ci		       node->time_in[HSR_PT_SLAVE_B] +
4478c2ecf20Sopenharmony_ci					msecs_to_jiffies(MAX_SLAVE_DIFF)))
4488c2ecf20Sopenharmony_ci		return hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	return NULL;
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci/* Remove stale sequence_nr records. Called by timer every
4548c2ecf20Sopenharmony_ci * HSR_LIFE_CHECK_INTERVAL (two seconds or so).
4558c2ecf20Sopenharmony_ci */
4568c2ecf20Sopenharmony_civoid hsr_prune_nodes(struct timer_list *t)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	struct hsr_priv *hsr = from_timer(hsr, t, prune_timer);
4598c2ecf20Sopenharmony_ci	struct hsr_node *node;
4608c2ecf20Sopenharmony_ci	struct hsr_node *tmp;
4618c2ecf20Sopenharmony_ci	struct hsr_port *port;
4628c2ecf20Sopenharmony_ci	unsigned long timestamp;
4638c2ecf20Sopenharmony_ci	unsigned long time_a, time_b;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	spin_lock_bh(&hsr->list_lock);
4668c2ecf20Sopenharmony_ci	list_for_each_entry_safe(node, tmp, &hsr->node_db, mac_list) {
4678c2ecf20Sopenharmony_ci		/* Don't prune own node. Neither time_in[HSR_PT_SLAVE_A]
4688c2ecf20Sopenharmony_ci		 * nor time_in[HSR_PT_SLAVE_B], will ever be updated for
4698c2ecf20Sopenharmony_ci		 * the master port. Thus the master node will be repeatedly
4708c2ecf20Sopenharmony_ci		 * pruned leading to packet loss.
4718c2ecf20Sopenharmony_ci		 */
4728c2ecf20Sopenharmony_ci		if (hsr_addr_is_self(hsr, node->macaddress_A))
4738c2ecf20Sopenharmony_ci			continue;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci		/* Shorthand */
4768c2ecf20Sopenharmony_ci		time_a = node->time_in[HSR_PT_SLAVE_A];
4778c2ecf20Sopenharmony_ci		time_b = node->time_in[HSR_PT_SLAVE_B];
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci		/* Check for timestamps old enough to risk wrap-around */
4808c2ecf20Sopenharmony_ci		if (time_after(jiffies, time_a + MAX_JIFFY_OFFSET / 2))
4818c2ecf20Sopenharmony_ci			node->time_in_stale[HSR_PT_SLAVE_A] = true;
4828c2ecf20Sopenharmony_ci		if (time_after(jiffies, time_b + MAX_JIFFY_OFFSET / 2))
4838c2ecf20Sopenharmony_ci			node->time_in_stale[HSR_PT_SLAVE_B] = true;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci		/* Get age of newest frame from node.
4868c2ecf20Sopenharmony_ci		 * At least one time_in is OK here; nodes get pruned long
4878c2ecf20Sopenharmony_ci		 * before both time_ins can get stale
4888c2ecf20Sopenharmony_ci		 */
4898c2ecf20Sopenharmony_ci		timestamp = time_a;
4908c2ecf20Sopenharmony_ci		if (node->time_in_stale[HSR_PT_SLAVE_A] ||
4918c2ecf20Sopenharmony_ci		    (!node->time_in_stale[HSR_PT_SLAVE_B] &&
4928c2ecf20Sopenharmony_ci		    time_after(time_b, time_a)))
4938c2ecf20Sopenharmony_ci			timestamp = time_b;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci		/* Warn of ring error only as long as we get frames at all */
4968c2ecf20Sopenharmony_ci		if (time_is_after_jiffies(timestamp +
4978c2ecf20Sopenharmony_ci				msecs_to_jiffies(1.5 * MAX_SLAVE_DIFF))) {
4988c2ecf20Sopenharmony_ci			rcu_read_lock();
4998c2ecf20Sopenharmony_ci			port = get_late_port(hsr, node);
5008c2ecf20Sopenharmony_ci			if (port)
5018c2ecf20Sopenharmony_ci				hsr_nl_ringerror(hsr, node->macaddress_A, port);
5028c2ecf20Sopenharmony_ci			rcu_read_unlock();
5038c2ecf20Sopenharmony_ci		}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci		/* Prune old entries */
5068c2ecf20Sopenharmony_ci		if (time_is_before_jiffies(timestamp +
5078c2ecf20Sopenharmony_ci				msecs_to_jiffies(HSR_NODE_FORGET_TIME))) {
5088c2ecf20Sopenharmony_ci			hsr_nl_nodedown(hsr, node->macaddress_A);
5098c2ecf20Sopenharmony_ci			list_del_rcu(&node->mac_list);
5108c2ecf20Sopenharmony_ci			/* Note that we need to free this entry later: */
5118c2ecf20Sopenharmony_ci			kfree_rcu(node, rcu_head);
5128c2ecf20Sopenharmony_ci		}
5138c2ecf20Sopenharmony_ci	}
5148c2ecf20Sopenharmony_ci	spin_unlock_bh(&hsr->list_lock);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	/* Restart timer */
5178c2ecf20Sopenharmony_ci	mod_timer(&hsr->prune_timer,
5188c2ecf20Sopenharmony_ci		  jiffies + msecs_to_jiffies(PRUNE_PERIOD));
5198c2ecf20Sopenharmony_ci}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_civoid *hsr_get_next_node(struct hsr_priv *hsr, void *_pos,
5228c2ecf20Sopenharmony_ci			unsigned char addr[ETH_ALEN])
5238c2ecf20Sopenharmony_ci{
5248c2ecf20Sopenharmony_ci	struct hsr_node *node;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	if (!_pos) {
5278c2ecf20Sopenharmony_ci		node = list_first_or_null_rcu(&hsr->node_db,
5288c2ecf20Sopenharmony_ci					      struct hsr_node, mac_list);
5298c2ecf20Sopenharmony_ci		if (node)
5308c2ecf20Sopenharmony_ci			ether_addr_copy(addr, node->macaddress_A);
5318c2ecf20Sopenharmony_ci		return node;
5328c2ecf20Sopenharmony_ci	}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	node = _pos;
5358c2ecf20Sopenharmony_ci	list_for_each_entry_continue_rcu(node, &hsr->node_db, mac_list) {
5368c2ecf20Sopenharmony_ci		ether_addr_copy(addr, node->macaddress_A);
5378c2ecf20Sopenharmony_ci		return node;
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	return NULL;
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ciint hsr_get_node_data(struct hsr_priv *hsr,
5448c2ecf20Sopenharmony_ci		      const unsigned char *addr,
5458c2ecf20Sopenharmony_ci		      unsigned char addr_b[ETH_ALEN],
5468c2ecf20Sopenharmony_ci		      unsigned int *addr_b_ifindex,
5478c2ecf20Sopenharmony_ci		      int *if1_age,
5488c2ecf20Sopenharmony_ci		      u16 *if1_seq,
5498c2ecf20Sopenharmony_ci		      int *if2_age,
5508c2ecf20Sopenharmony_ci		      u16 *if2_seq)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	struct hsr_node *node;
5538c2ecf20Sopenharmony_ci	struct hsr_port *port;
5548c2ecf20Sopenharmony_ci	unsigned long tdiff;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	node = find_node_by_addr_A(&hsr->node_db, addr);
5578c2ecf20Sopenharmony_ci	if (!node)
5588c2ecf20Sopenharmony_ci		return -ENOENT;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	ether_addr_copy(addr_b, node->macaddress_B);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	tdiff = jiffies - node->time_in[HSR_PT_SLAVE_A];
5638c2ecf20Sopenharmony_ci	if (node->time_in_stale[HSR_PT_SLAVE_A])
5648c2ecf20Sopenharmony_ci		*if1_age = INT_MAX;
5658c2ecf20Sopenharmony_ci#if HZ <= MSEC_PER_SEC
5668c2ecf20Sopenharmony_ci	else if (tdiff > msecs_to_jiffies(INT_MAX))
5678c2ecf20Sopenharmony_ci		*if1_age = INT_MAX;
5688c2ecf20Sopenharmony_ci#endif
5698c2ecf20Sopenharmony_ci	else
5708c2ecf20Sopenharmony_ci		*if1_age = jiffies_to_msecs(tdiff);
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	tdiff = jiffies - node->time_in[HSR_PT_SLAVE_B];
5738c2ecf20Sopenharmony_ci	if (node->time_in_stale[HSR_PT_SLAVE_B])
5748c2ecf20Sopenharmony_ci		*if2_age = INT_MAX;
5758c2ecf20Sopenharmony_ci#if HZ <= MSEC_PER_SEC
5768c2ecf20Sopenharmony_ci	else if (tdiff > msecs_to_jiffies(INT_MAX))
5778c2ecf20Sopenharmony_ci		*if2_age = INT_MAX;
5788c2ecf20Sopenharmony_ci#endif
5798c2ecf20Sopenharmony_ci	else
5808c2ecf20Sopenharmony_ci		*if2_age = jiffies_to_msecs(tdiff);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	/* Present sequence numbers as if they were incoming on interface */
5838c2ecf20Sopenharmony_ci	*if1_seq = node->seq_out[HSR_PT_SLAVE_B];
5848c2ecf20Sopenharmony_ci	*if2_seq = node->seq_out[HSR_PT_SLAVE_A];
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	if (node->addr_B_port != HSR_PT_NONE) {
5878c2ecf20Sopenharmony_ci		port = hsr_port_get_hsr(hsr, node->addr_B_port);
5888c2ecf20Sopenharmony_ci		*addr_b_ifindex = port->dev->ifindex;
5898c2ecf20Sopenharmony_ci	} else {
5908c2ecf20Sopenharmony_ci		*addr_b_ifindex = -1;
5918c2ecf20Sopenharmony_ci	}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	return 0;
5948c2ecf20Sopenharmony_ci}
595