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(ð_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