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 * Frame router for HSR and PRP. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "hsr_forward.h" 1162306a36Sopenharmony_ci#include <linux/types.h> 1262306a36Sopenharmony_ci#include <linux/skbuff.h> 1362306a36Sopenharmony_ci#include <linux/etherdevice.h> 1462306a36Sopenharmony_ci#include <linux/if_vlan.h> 1562306a36Sopenharmony_ci#include "hsr_main.h" 1662306a36Sopenharmony_ci#include "hsr_framereg.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistruct hsr_node; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* The uses I can see for these HSR supervision frames are: 2162306a36Sopenharmony_ci * 1) Use the frames that are sent after node initialization ("HSR_TLV.Type = 2262306a36Sopenharmony_ci * 22") to reset any sequence_nr counters belonging to that node. Useful if 2362306a36Sopenharmony_ci * the other node's counter has been reset for some reason. 2462306a36Sopenharmony_ci * -- 2562306a36Sopenharmony_ci * Or not - resetting the counter and bridging the frame would create a 2662306a36Sopenharmony_ci * loop, unfortunately. 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * 2) Use the LifeCheck frames to detect ring breaks. I.e. if no LifeCheck 2962306a36Sopenharmony_ci * frame is received from a particular node, we know something is wrong. 3062306a36Sopenharmony_ci * We just register these (as with normal frames) and throw them away. 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * 3) Allow different MAC addresses for the two slave interfaces, using the 3362306a36Sopenharmony_ci * MacAddressA field. 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_cistatic bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct ethhdr *eth_hdr; 3862306a36Sopenharmony_ci struct hsr_sup_tag *hsr_sup_tag; 3962306a36Sopenharmony_ci struct hsrv1_ethhdr_sp *hsr_V1_hdr; 4062306a36Sopenharmony_ci struct hsr_sup_tlv *hsr_sup_tlv; 4162306a36Sopenharmony_ci u16 total_length = 0; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci WARN_ON_ONCE(!skb_mac_header_was_set(skb)); 4462306a36Sopenharmony_ci eth_hdr = (struct ethhdr *)skb_mac_header(skb); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* Correct addr? */ 4762306a36Sopenharmony_ci if (!ether_addr_equal(eth_hdr->h_dest, 4862306a36Sopenharmony_ci hsr->sup_multicast_addr)) 4962306a36Sopenharmony_ci return false; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci /* Correct ether type?. */ 5262306a36Sopenharmony_ci if (!(eth_hdr->h_proto == htons(ETH_P_PRP) || 5362306a36Sopenharmony_ci eth_hdr->h_proto == htons(ETH_P_HSR))) 5462306a36Sopenharmony_ci return false; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* Get the supervision header from correct location. */ 5762306a36Sopenharmony_ci if (eth_hdr->h_proto == htons(ETH_P_HSR)) { /* Okay HSRv1. */ 5862306a36Sopenharmony_ci total_length = sizeof(struct hsrv1_ethhdr_sp); 5962306a36Sopenharmony_ci if (!pskb_may_pull(skb, total_length)) 6062306a36Sopenharmony_ci return false; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci hsr_V1_hdr = (struct hsrv1_ethhdr_sp *)skb_mac_header(skb); 6362306a36Sopenharmony_ci if (hsr_V1_hdr->hsr.encap_proto != htons(ETH_P_PRP)) 6462306a36Sopenharmony_ci return false; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci hsr_sup_tag = &hsr_V1_hdr->hsr_sup; 6762306a36Sopenharmony_ci } else { 6862306a36Sopenharmony_ci total_length = sizeof(struct hsrv0_ethhdr_sp); 6962306a36Sopenharmony_ci if (!pskb_may_pull(skb, total_length)) 7062306a36Sopenharmony_ci return false; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci hsr_sup_tag = 7362306a36Sopenharmony_ci &((struct hsrv0_ethhdr_sp *)skb_mac_header(skb))->hsr_sup; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (hsr_sup_tag->tlv.HSR_TLV_type != HSR_TLV_ANNOUNCE && 7762306a36Sopenharmony_ci hsr_sup_tag->tlv.HSR_TLV_type != HSR_TLV_LIFE_CHECK && 7862306a36Sopenharmony_ci hsr_sup_tag->tlv.HSR_TLV_type != PRP_TLV_LIFE_CHECK_DD && 7962306a36Sopenharmony_ci hsr_sup_tag->tlv.HSR_TLV_type != PRP_TLV_LIFE_CHECK_DA) 8062306a36Sopenharmony_ci return false; 8162306a36Sopenharmony_ci if (hsr_sup_tag->tlv.HSR_TLV_length != 12 && 8262306a36Sopenharmony_ci hsr_sup_tag->tlv.HSR_TLV_length != sizeof(struct hsr_sup_payload)) 8362306a36Sopenharmony_ci return false; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* Get next tlv */ 8662306a36Sopenharmony_ci total_length += hsr_sup_tag->tlv.HSR_TLV_length; 8762306a36Sopenharmony_ci if (!pskb_may_pull(skb, total_length)) 8862306a36Sopenharmony_ci return false; 8962306a36Sopenharmony_ci skb_pull(skb, total_length); 9062306a36Sopenharmony_ci hsr_sup_tlv = (struct hsr_sup_tlv *)skb->data; 9162306a36Sopenharmony_ci skb_push(skb, total_length); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* if this is a redbox supervision frame we need to verify 9462306a36Sopenharmony_ci * that more data is available 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_ci if (hsr_sup_tlv->HSR_TLV_type == PRP_TLV_REDBOX_MAC) { 9762306a36Sopenharmony_ci /* tlv length must be a length of a mac address */ 9862306a36Sopenharmony_ci if (hsr_sup_tlv->HSR_TLV_length != sizeof(struct hsr_sup_payload)) 9962306a36Sopenharmony_ci return false; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* make sure another tlv follows */ 10262306a36Sopenharmony_ci total_length += sizeof(struct hsr_sup_tlv) + hsr_sup_tlv->HSR_TLV_length; 10362306a36Sopenharmony_ci if (!pskb_may_pull(skb, total_length)) 10462306a36Sopenharmony_ci return false; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* get next tlv */ 10762306a36Sopenharmony_ci skb_pull(skb, total_length); 10862306a36Sopenharmony_ci hsr_sup_tlv = (struct hsr_sup_tlv *)skb->data; 10962306a36Sopenharmony_ci skb_push(skb, total_length); 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* end of tlvs must follow at the end */ 11362306a36Sopenharmony_ci if (hsr_sup_tlv->HSR_TLV_type == HSR_TLV_EOT && 11462306a36Sopenharmony_ci hsr_sup_tlv->HSR_TLV_length != 0) 11562306a36Sopenharmony_ci return false; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return true; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic struct sk_buff *create_stripped_skb_hsr(struct sk_buff *skb_in, 12162306a36Sopenharmony_ci struct hsr_frame_info *frame) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct sk_buff *skb; 12462306a36Sopenharmony_ci int copylen; 12562306a36Sopenharmony_ci unsigned char *dst, *src; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci skb_pull(skb_in, HSR_HLEN); 12862306a36Sopenharmony_ci skb = __pskb_copy(skb_in, skb_headroom(skb_in) - HSR_HLEN, GFP_ATOMIC); 12962306a36Sopenharmony_ci skb_push(skb_in, HSR_HLEN); 13062306a36Sopenharmony_ci if (!skb) 13162306a36Sopenharmony_ci return NULL; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci skb_reset_mac_header(skb); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) 13662306a36Sopenharmony_ci skb->csum_start -= HSR_HLEN; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci copylen = 2 * ETH_ALEN; 13962306a36Sopenharmony_ci if (frame->is_vlan) 14062306a36Sopenharmony_ci copylen += VLAN_HLEN; 14162306a36Sopenharmony_ci src = skb_mac_header(skb_in); 14262306a36Sopenharmony_ci dst = skb_mac_header(skb); 14362306a36Sopenharmony_ci memcpy(dst, src, copylen); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci skb->protocol = eth_hdr(skb)->h_proto; 14662306a36Sopenharmony_ci return skb; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistruct sk_buff *hsr_get_untagged_frame(struct hsr_frame_info *frame, 15062306a36Sopenharmony_ci struct hsr_port *port) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci if (!frame->skb_std) { 15362306a36Sopenharmony_ci if (frame->skb_hsr) 15462306a36Sopenharmony_ci frame->skb_std = 15562306a36Sopenharmony_ci create_stripped_skb_hsr(frame->skb_hsr, frame); 15662306a36Sopenharmony_ci else 15762306a36Sopenharmony_ci netdev_warn_once(port->dev, 15862306a36Sopenharmony_ci "Unexpected frame received in hsr_get_untagged_frame()\n"); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (!frame->skb_std) 16162306a36Sopenharmony_ci return NULL; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return skb_clone(frame->skb_std, GFP_ATOMIC); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistruct sk_buff *prp_get_untagged_frame(struct hsr_frame_info *frame, 16862306a36Sopenharmony_ci struct hsr_port *port) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci if (!frame->skb_std) { 17162306a36Sopenharmony_ci if (frame->skb_prp) { 17262306a36Sopenharmony_ci /* trim the skb by len - HSR_HLEN to exclude RCT */ 17362306a36Sopenharmony_ci skb_trim(frame->skb_prp, 17462306a36Sopenharmony_ci frame->skb_prp->len - HSR_HLEN); 17562306a36Sopenharmony_ci frame->skb_std = 17662306a36Sopenharmony_ci __pskb_copy(frame->skb_prp, 17762306a36Sopenharmony_ci skb_headroom(frame->skb_prp), 17862306a36Sopenharmony_ci GFP_ATOMIC); 17962306a36Sopenharmony_ci } else { 18062306a36Sopenharmony_ci /* Unexpected */ 18162306a36Sopenharmony_ci WARN_ONCE(1, "%s:%d: Unexpected frame received (port_src %s)\n", 18262306a36Sopenharmony_ci __FILE__, __LINE__, port->dev->name); 18362306a36Sopenharmony_ci return NULL; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return skb_clone(frame->skb_std, GFP_ATOMIC); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic void prp_set_lan_id(struct prp_rct *trailer, 19162306a36Sopenharmony_ci struct hsr_port *port) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci int lane_id; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (port->type == HSR_PT_SLAVE_A) 19662306a36Sopenharmony_ci lane_id = 0; 19762306a36Sopenharmony_ci else 19862306a36Sopenharmony_ci lane_id = 1; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* Add net_id in the upper 3 bits of lane_id */ 20162306a36Sopenharmony_ci lane_id |= port->hsr->net_id; 20262306a36Sopenharmony_ci set_prp_lan_id(trailer, lane_id); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci/* Tailroom for PRP rct should have been created before calling this */ 20662306a36Sopenharmony_cistatic struct sk_buff *prp_fill_rct(struct sk_buff *skb, 20762306a36Sopenharmony_ci struct hsr_frame_info *frame, 20862306a36Sopenharmony_ci struct hsr_port *port) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct prp_rct *trailer; 21162306a36Sopenharmony_ci int min_size = ETH_ZLEN; 21262306a36Sopenharmony_ci int lsdu_size; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (!skb) 21562306a36Sopenharmony_ci return skb; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (frame->is_vlan) 21862306a36Sopenharmony_ci min_size = VLAN_ETH_ZLEN; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (skb_put_padto(skb, min_size)) 22162306a36Sopenharmony_ci return NULL; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci trailer = (struct prp_rct *)skb_put(skb, HSR_HLEN); 22462306a36Sopenharmony_ci lsdu_size = skb->len - 14; 22562306a36Sopenharmony_ci if (frame->is_vlan) 22662306a36Sopenharmony_ci lsdu_size -= 4; 22762306a36Sopenharmony_ci prp_set_lan_id(trailer, port); 22862306a36Sopenharmony_ci set_prp_LSDU_size(trailer, lsdu_size); 22962306a36Sopenharmony_ci trailer->sequence_nr = htons(frame->sequence_nr); 23062306a36Sopenharmony_ci trailer->PRP_suffix = htons(ETH_P_PRP); 23162306a36Sopenharmony_ci skb->protocol = eth_hdr(skb)->h_proto; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return skb; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic void hsr_set_path_id(struct hsr_ethhdr *hsr_ethhdr, 23762306a36Sopenharmony_ci struct hsr_port *port) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci int path_id; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (port->type == HSR_PT_SLAVE_A) 24262306a36Sopenharmony_ci path_id = 0; 24362306a36Sopenharmony_ci else 24462306a36Sopenharmony_ci path_id = 1; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci set_hsr_tag_path(&hsr_ethhdr->hsr_tag, path_id); 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic struct sk_buff *hsr_fill_tag(struct sk_buff *skb, 25062306a36Sopenharmony_ci struct hsr_frame_info *frame, 25162306a36Sopenharmony_ci struct hsr_port *port, u8 proto_version) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct hsr_ethhdr *hsr_ethhdr; 25462306a36Sopenharmony_ci int lsdu_size; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* pad to minimum packet size which is 60 + 6 (HSR tag) */ 25762306a36Sopenharmony_ci if (skb_put_padto(skb, ETH_ZLEN + HSR_HLEN)) 25862306a36Sopenharmony_ci return NULL; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci lsdu_size = skb->len - 14; 26162306a36Sopenharmony_ci if (frame->is_vlan) 26262306a36Sopenharmony_ci lsdu_size -= 4; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci hsr_ethhdr = (struct hsr_ethhdr *)skb_mac_header(skb); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci hsr_set_path_id(hsr_ethhdr, port); 26762306a36Sopenharmony_ci set_hsr_tag_LSDU_size(&hsr_ethhdr->hsr_tag, lsdu_size); 26862306a36Sopenharmony_ci hsr_ethhdr->hsr_tag.sequence_nr = htons(frame->sequence_nr); 26962306a36Sopenharmony_ci hsr_ethhdr->hsr_tag.encap_proto = hsr_ethhdr->ethhdr.h_proto; 27062306a36Sopenharmony_ci hsr_ethhdr->ethhdr.h_proto = htons(proto_version ? 27162306a36Sopenharmony_ci ETH_P_HSR : ETH_P_PRP); 27262306a36Sopenharmony_ci skb->protocol = hsr_ethhdr->ethhdr.h_proto; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci return skb; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/* If the original frame was an HSR tagged frame, just clone it to be sent 27862306a36Sopenharmony_ci * unchanged. Otherwise, create a private frame especially tagged for 'port'. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_cistruct sk_buff *hsr_create_tagged_frame(struct hsr_frame_info *frame, 28162306a36Sopenharmony_ci struct hsr_port *port) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci unsigned char *dst, *src; 28462306a36Sopenharmony_ci struct sk_buff *skb; 28562306a36Sopenharmony_ci int movelen; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (frame->skb_hsr) { 28862306a36Sopenharmony_ci struct hsr_ethhdr *hsr_ethhdr = 28962306a36Sopenharmony_ci (struct hsr_ethhdr *)skb_mac_header(frame->skb_hsr); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* set the lane id properly */ 29262306a36Sopenharmony_ci hsr_set_path_id(hsr_ethhdr, port); 29362306a36Sopenharmony_ci return skb_clone(frame->skb_hsr, GFP_ATOMIC); 29462306a36Sopenharmony_ci } else if (port->dev->features & NETIF_F_HW_HSR_TAG_INS) { 29562306a36Sopenharmony_ci return skb_clone(frame->skb_std, GFP_ATOMIC); 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* Create the new skb with enough headroom to fit the HSR tag */ 29962306a36Sopenharmony_ci skb = __pskb_copy(frame->skb_std, 30062306a36Sopenharmony_ci skb_headroom(frame->skb_std) + HSR_HLEN, GFP_ATOMIC); 30162306a36Sopenharmony_ci if (!skb) 30262306a36Sopenharmony_ci return NULL; 30362306a36Sopenharmony_ci skb_reset_mac_header(skb); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) 30662306a36Sopenharmony_ci skb->csum_start += HSR_HLEN; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci movelen = ETH_HLEN; 30962306a36Sopenharmony_ci if (frame->is_vlan) 31062306a36Sopenharmony_ci movelen += VLAN_HLEN; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci src = skb_mac_header(skb); 31362306a36Sopenharmony_ci dst = skb_push(skb, HSR_HLEN); 31462306a36Sopenharmony_ci memmove(dst, src, movelen); 31562306a36Sopenharmony_ci skb_reset_mac_header(skb); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* skb_put_padto free skb on error and hsr_fill_tag returns NULL in 31862306a36Sopenharmony_ci * that case 31962306a36Sopenharmony_ci */ 32062306a36Sopenharmony_ci return hsr_fill_tag(skb, frame, port, port->hsr->prot_version); 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistruct sk_buff *prp_create_tagged_frame(struct hsr_frame_info *frame, 32462306a36Sopenharmony_ci struct hsr_port *port) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct sk_buff *skb; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (frame->skb_prp) { 32962306a36Sopenharmony_ci struct prp_rct *trailer = skb_get_PRP_rct(frame->skb_prp); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (trailer) { 33262306a36Sopenharmony_ci prp_set_lan_id(trailer, port); 33362306a36Sopenharmony_ci } else { 33462306a36Sopenharmony_ci WARN_ONCE(!trailer, "errored PRP skb"); 33562306a36Sopenharmony_ci return NULL; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci return skb_clone(frame->skb_prp, GFP_ATOMIC); 33862306a36Sopenharmony_ci } else if (port->dev->features & NETIF_F_HW_HSR_TAG_INS) { 33962306a36Sopenharmony_ci return skb_clone(frame->skb_std, GFP_ATOMIC); 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci skb = skb_copy_expand(frame->skb_std, 0, 34362306a36Sopenharmony_ci skb_tailroom(frame->skb_std) + HSR_HLEN, 34462306a36Sopenharmony_ci GFP_ATOMIC); 34562306a36Sopenharmony_ci return prp_fill_rct(skb, frame, port); 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic void hsr_deliver_master(struct sk_buff *skb, struct net_device *dev, 34962306a36Sopenharmony_ci struct hsr_node *node_src) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci bool was_multicast_frame; 35262306a36Sopenharmony_ci int res, recv_len; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci was_multicast_frame = (skb->pkt_type == PACKET_MULTICAST); 35562306a36Sopenharmony_ci hsr_addr_subst_source(node_src, skb); 35662306a36Sopenharmony_ci skb_pull(skb, ETH_HLEN); 35762306a36Sopenharmony_ci recv_len = skb->len; 35862306a36Sopenharmony_ci res = netif_rx(skb); 35962306a36Sopenharmony_ci if (res == NET_RX_DROP) { 36062306a36Sopenharmony_ci dev->stats.rx_dropped++; 36162306a36Sopenharmony_ci } else { 36262306a36Sopenharmony_ci dev->stats.rx_packets++; 36362306a36Sopenharmony_ci dev->stats.rx_bytes += recv_len; 36462306a36Sopenharmony_ci if (was_multicast_frame) 36562306a36Sopenharmony_ci dev->stats.multicast++; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic int hsr_xmit(struct sk_buff *skb, struct hsr_port *port, 37062306a36Sopenharmony_ci struct hsr_frame_info *frame) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci if (frame->port_rcv->type == HSR_PT_MASTER) { 37362306a36Sopenharmony_ci hsr_addr_subst_dest(frame->node_src, skb, port); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* Address substitution (IEC62439-3 pp 26, 50): replace mac 37662306a36Sopenharmony_ci * address of outgoing frame with that of the outgoing slave's. 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_ci ether_addr_copy(eth_hdr(skb)->h_source, port->dev->dev_addr); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci return dev_queue_xmit(skb); 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cibool prp_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci return ((frame->port_rcv->type == HSR_PT_SLAVE_A && 38662306a36Sopenharmony_ci port->type == HSR_PT_SLAVE_B) || 38762306a36Sopenharmony_ci (frame->port_rcv->type == HSR_PT_SLAVE_B && 38862306a36Sopenharmony_ci port->type == HSR_PT_SLAVE_A)); 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cibool hsr_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci if (port->dev->features & NETIF_F_HW_HSR_FWD) 39462306a36Sopenharmony_ci return prp_drop_frame(frame, port); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci return false; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci/* Forward the frame through all devices except: 40062306a36Sopenharmony_ci * - Back through the receiving device 40162306a36Sopenharmony_ci * - If it's a HSR frame: through a device where it has passed before 40262306a36Sopenharmony_ci * - if it's a PRP frame: through another PRP slave device (no bridge) 40362306a36Sopenharmony_ci * - To the local HSR master only if the frame is directly addressed to it, or 40462306a36Sopenharmony_ci * a non-supervision multicast or broadcast frame. 40562306a36Sopenharmony_ci * 40662306a36Sopenharmony_ci * HSR slave devices should insert a HSR tag into the frame, or forward the 40762306a36Sopenharmony_ci * frame unchanged if it's already tagged. Interlink devices should strip HSR 40862306a36Sopenharmony_ci * tags if they're of the non-HSR type (but only after duplicate discard). The 40962306a36Sopenharmony_ci * master device always strips HSR tags. 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_cistatic void hsr_forward_do(struct hsr_frame_info *frame) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci struct hsr_port *port; 41462306a36Sopenharmony_ci struct sk_buff *skb; 41562306a36Sopenharmony_ci bool sent = false; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci hsr_for_each_port(frame->port_rcv->hsr, port) { 41862306a36Sopenharmony_ci struct hsr_priv *hsr = port->hsr; 41962306a36Sopenharmony_ci /* Don't send frame back the way it came */ 42062306a36Sopenharmony_ci if (port == frame->port_rcv) 42162306a36Sopenharmony_ci continue; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* Don't deliver locally unless we should */ 42462306a36Sopenharmony_ci if (port->type == HSR_PT_MASTER && !frame->is_local_dest) 42562306a36Sopenharmony_ci continue; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* Deliver frames directly addressed to us to master only */ 42862306a36Sopenharmony_ci if (port->type != HSR_PT_MASTER && frame->is_local_exclusive) 42962306a36Sopenharmony_ci continue; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* If hardware duplicate generation is enabled, only send out 43262306a36Sopenharmony_ci * one port. 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_ci if ((port->dev->features & NETIF_F_HW_HSR_DUP) && sent) 43562306a36Sopenharmony_ci continue; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* Don't send frame over port where it has been sent before. 43862306a36Sopenharmony_ci * Also fro SAN, this shouldn't be done. 43962306a36Sopenharmony_ci */ 44062306a36Sopenharmony_ci if (!frame->is_from_san && 44162306a36Sopenharmony_ci hsr_register_frame_out(port, frame->node_src, 44262306a36Sopenharmony_ci frame->sequence_nr)) 44362306a36Sopenharmony_ci continue; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (frame->is_supervision && port->type == HSR_PT_MASTER) { 44662306a36Sopenharmony_ci hsr_handle_sup_frame(frame); 44762306a36Sopenharmony_ci continue; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* Check if frame is to be dropped. Eg. for PRP no forward 45162306a36Sopenharmony_ci * between ports. 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_ci if (hsr->proto_ops->drop_frame && 45462306a36Sopenharmony_ci hsr->proto_ops->drop_frame(frame, port)) 45562306a36Sopenharmony_ci continue; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (port->type != HSR_PT_MASTER) 45862306a36Sopenharmony_ci skb = hsr->proto_ops->create_tagged_frame(frame, port); 45962306a36Sopenharmony_ci else 46062306a36Sopenharmony_ci skb = hsr->proto_ops->get_untagged_frame(frame, port); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (!skb) { 46362306a36Sopenharmony_ci frame->port_rcv->dev->stats.rx_dropped++; 46462306a36Sopenharmony_ci continue; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci skb->dev = port->dev; 46862306a36Sopenharmony_ci if (port->type == HSR_PT_MASTER) { 46962306a36Sopenharmony_ci hsr_deliver_master(skb, port->dev, frame->node_src); 47062306a36Sopenharmony_ci } else { 47162306a36Sopenharmony_ci if (!hsr_xmit(skb, port, frame)) 47262306a36Sopenharmony_ci sent = true; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic void check_local_dest(struct hsr_priv *hsr, struct sk_buff *skb, 47862306a36Sopenharmony_ci struct hsr_frame_info *frame) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci if (hsr_addr_is_self(hsr, eth_hdr(skb)->h_dest)) { 48162306a36Sopenharmony_ci frame->is_local_exclusive = true; 48262306a36Sopenharmony_ci skb->pkt_type = PACKET_HOST; 48362306a36Sopenharmony_ci } else { 48462306a36Sopenharmony_ci frame->is_local_exclusive = false; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (skb->pkt_type == PACKET_HOST || 48862306a36Sopenharmony_ci skb->pkt_type == PACKET_MULTICAST || 48962306a36Sopenharmony_ci skb->pkt_type == PACKET_BROADCAST) { 49062306a36Sopenharmony_ci frame->is_local_dest = true; 49162306a36Sopenharmony_ci } else { 49262306a36Sopenharmony_ci frame->is_local_dest = false; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic void handle_std_frame(struct sk_buff *skb, 49762306a36Sopenharmony_ci struct hsr_frame_info *frame) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci struct hsr_port *port = frame->port_rcv; 50062306a36Sopenharmony_ci struct hsr_priv *hsr = port->hsr; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci frame->skb_hsr = NULL; 50362306a36Sopenharmony_ci frame->skb_prp = NULL; 50462306a36Sopenharmony_ci frame->skb_std = skb; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (port->type != HSR_PT_MASTER) { 50762306a36Sopenharmony_ci frame->is_from_san = true; 50862306a36Sopenharmony_ci } else { 50962306a36Sopenharmony_ci /* Sequence nr for the master node */ 51062306a36Sopenharmony_ci lockdep_assert_held(&hsr->seqnr_lock); 51162306a36Sopenharmony_ci frame->sequence_nr = hsr->sequence_nr; 51262306a36Sopenharmony_ci hsr->sequence_nr++; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ciint hsr_fill_frame_info(__be16 proto, struct sk_buff *skb, 51762306a36Sopenharmony_ci struct hsr_frame_info *frame) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci struct hsr_port *port = frame->port_rcv; 52062306a36Sopenharmony_ci struct hsr_priv *hsr = port->hsr; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* HSRv0 supervisory frames double as a tag so treat them as tagged. */ 52362306a36Sopenharmony_ci if ((!hsr->prot_version && proto == htons(ETH_P_PRP)) || 52462306a36Sopenharmony_ci proto == htons(ETH_P_HSR)) { 52562306a36Sopenharmony_ci /* Check if skb contains hsr_ethhdr */ 52662306a36Sopenharmony_ci if (skb->mac_len < sizeof(struct hsr_ethhdr)) 52762306a36Sopenharmony_ci return -EINVAL; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* HSR tagged frame :- Data or Supervision */ 53062306a36Sopenharmony_ci frame->skb_std = NULL; 53162306a36Sopenharmony_ci frame->skb_prp = NULL; 53262306a36Sopenharmony_ci frame->skb_hsr = skb; 53362306a36Sopenharmony_ci frame->sequence_nr = hsr_get_skb_sequence_nr(skb); 53462306a36Sopenharmony_ci return 0; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci /* Standard frame or PRP from master port */ 53862306a36Sopenharmony_ci handle_std_frame(skb, frame); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci return 0; 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ciint prp_fill_frame_info(__be16 proto, struct sk_buff *skb, 54462306a36Sopenharmony_ci struct hsr_frame_info *frame) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci /* Supervision frame */ 54762306a36Sopenharmony_ci struct prp_rct *rct = skb_get_PRP_rct(skb); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (rct && 55062306a36Sopenharmony_ci prp_check_lsdu_size(skb, rct, frame->is_supervision)) { 55162306a36Sopenharmony_ci frame->skb_hsr = NULL; 55262306a36Sopenharmony_ci frame->skb_std = NULL; 55362306a36Sopenharmony_ci frame->skb_prp = skb; 55462306a36Sopenharmony_ci frame->sequence_nr = prp_get_skb_sequence_nr(rct); 55562306a36Sopenharmony_ci return 0; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci handle_std_frame(skb, frame); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return 0; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic int fill_frame_info(struct hsr_frame_info *frame, 56362306a36Sopenharmony_ci struct sk_buff *skb, struct hsr_port *port) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci struct hsr_priv *hsr = port->hsr; 56662306a36Sopenharmony_ci struct hsr_vlan_ethhdr *vlan_hdr; 56762306a36Sopenharmony_ci struct ethhdr *ethhdr; 56862306a36Sopenharmony_ci __be16 proto; 56962306a36Sopenharmony_ci int ret; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci /* Check if skb contains ethhdr */ 57262306a36Sopenharmony_ci if (skb->mac_len < sizeof(struct ethhdr)) 57362306a36Sopenharmony_ci return -EINVAL; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci memset(frame, 0, sizeof(*frame)); 57662306a36Sopenharmony_ci frame->is_supervision = is_supervision_frame(port->hsr, skb); 57762306a36Sopenharmony_ci frame->node_src = hsr_get_node(port, &hsr->node_db, skb, 57862306a36Sopenharmony_ci frame->is_supervision, 57962306a36Sopenharmony_ci port->type); 58062306a36Sopenharmony_ci if (!frame->node_src) 58162306a36Sopenharmony_ci return -1; /* Unknown node and !is_supervision, or no mem */ 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci ethhdr = (struct ethhdr *)skb_mac_header(skb); 58462306a36Sopenharmony_ci frame->is_vlan = false; 58562306a36Sopenharmony_ci proto = ethhdr->h_proto; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (proto == htons(ETH_P_8021Q)) 58862306a36Sopenharmony_ci frame->is_vlan = true; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (frame->is_vlan) { 59162306a36Sopenharmony_ci vlan_hdr = (struct hsr_vlan_ethhdr *)ethhdr; 59262306a36Sopenharmony_ci proto = vlan_hdr->vlanhdr.h_vlan_encapsulated_proto; 59362306a36Sopenharmony_ci /* FIXME: */ 59462306a36Sopenharmony_ci netdev_warn_once(skb->dev, "VLAN not yet supported"); 59562306a36Sopenharmony_ci return -EINVAL; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci frame->is_from_san = false; 59962306a36Sopenharmony_ci frame->port_rcv = port; 60062306a36Sopenharmony_ci ret = hsr->proto_ops->fill_frame_info(proto, skb, frame); 60162306a36Sopenharmony_ci if (ret) 60262306a36Sopenharmony_ci return ret; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci check_local_dest(port->hsr, skb, frame); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci return 0; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci/* Must be called holding rcu read lock (because of the port parameter) */ 61062306a36Sopenharmony_civoid hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct hsr_frame_info frame; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci rcu_read_lock(); 61562306a36Sopenharmony_ci if (fill_frame_info(&frame, skb, port) < 0) 61662306a36Sopenharmony_ci goto out_drop; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci hsr_register_frame_in(frame.node_src, port, frame.sequence_nr); 61962306a36Sopenharmony_ci hsr_forward_do(&frame); 62062306a36Sopenharmony_ci rcu_read_unlock(); 62162306a36Sopenharmony_ci /* Gets called for ingress frames as well as egress from master port. 62262306a36Sopenharmony_ci * So check and increment stats for master port only here. 62362306a36Sopenharmony_ci */ 62462306a36Sopenharmony_ci if (port->type == HSR_PT_MASTER) { 62562306a36Sopenharmony_ci port->dev->stats.tx_packets++; 62662306a36Sopenharmony_ci port->dev->stats.tx_bytes += skb->len; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci kfree_skb(frame.skb_hsr); 63062306a36Sopenharmony_ci kfree_skb(frame.skb_prp); 63162306a36Sopenharmony_ci kfree_skb(frame.skb_std); 63262306a36Sopenharmony_ci return; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ciout_drop: 63562306a36Sopenharmony_ci rcu_read_unlock(); 63662306a36Sopenharmony_ci port->dev->stats.tx_dropped++; 63762306a36Sopenharmony_ci kfree_skb(skb); 63862306a36Sopenharmony_ci} 639