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 * Frame router for HSR and PRP. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "hsr_forward.h" 118c2ecf20Sopenharmony_ci#include <linux/types.h> 128c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 138c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 148c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 158c2ecf20Sopenharmony_ci#include "hsr_main.h" 168c2ecf20Sopenharmony_ci#include "hsr_framereg.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistruct hsr_node; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* The uses I can see for these HSR supervision frames are: 218c2ecf20Sopenharmony_ci * 1) Use the frames that are sent after node initialization ("HSR_TLV.Type = 228c2ecf20Sopenharmony_ci * 22") to reset any sequence_nr counters belonging to that node. Useful if 238c2ecf20Sopenharmony_ci * the other node's counter has been reset for some reason. 248c2ecf20Sopenharmony_ci * -- 258c2ecf20Sopenharmony_ci * Or not - resetting the counter and bridging the frame would create a 268c2ecf20Sopenharmony_ci * loop, unfortunately. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * 2) Use the LifeCheck frames to detect ring breaks. I.e. if no LifeCheck 298c2ecf20Sopenharmony_ci * frame is received from a particular node, we know something is wrong. 308c2ecf20Sopenharmony_ci * We just register these (as with normal frames) and throw them away. 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * 3) Allow different MAC addresses for the two slave interfaces, using the 338c2ecf20Sopenharmony_ci * MacAddressA field. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_cistatic bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci struct ethhdr *eth_hdr; 388c2ecf20Sopenharmony_ci struct hsr_sup_tag *hsr_sup_tag; 398c2ecf20Sopenharmony_ci struct hsrv1_ethhdr_sp *hsr_V1_hdr; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci WARN_ON_ONCE(!skb_mac_header_was_set(skb)); 428c2ecf20Sopenharmony_ci eth_hdr = (struct ethhdr *)skb_mac_header(skb); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci /* Correct addr? */ 458c2ecf20Sopenharmony_ci if (!ether_addr_equal(eth_hdr->h_dest, 468c2ecf20Sopenharmony_ci hsr->sup_multicast_addr)) 478c2ecf20Sopenharmony_ci return false; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci /* Correct ether type?. */ 508c2ecf20Sopenharmony_ci if (!(eth_hdr->h_proto == htons(ETH_P_PRP) || 518c2ecf20Sopenharmony_ci eth_hdr->h_proto == htons(ETH_P_HSR))) 528c2ecf20Sopenharmony_ci return false; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* Get the supervision header from correct location. */ 558c2ecf20Sopenharmony_ci if (eth_hdr->h_proto == htons(ETH_P_HSR)) { /* Okay HSRv1. */ 568c2ecf20Sopenharmony_ci hsr_V1_hdr = (struct hsrv1_ethhdr_sp *)skb_mac_header(skb); 578c2ecf20Sopenharmony_ci if (hsr_V1_hdr->hsr.encap_proto != htons(ETH_P_PRP)) 588c2ecf20Sopenharmony_ci return false; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci hsr_sup_tag = &hsr_V1_hdr->hsr_sup; 618c2ecf20Sopenharmony_ci } else { 628c2ecf20Sopenharmony_ci hsr_sup_tag = 638c2ecf20Sopenharmony_ci &((struct hsrv0_ethhdr_sp *)skb_mac_header(skb))->hsr_sup; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (hsr_sup_tag->HSR_TLV_type != HSR_TLV_ANNOUNCE && 678c2ecf20Sopenharmony_ci hsr_sup_tag->HSR_TLV_type != HSR_TLV_LIFE_CHECK && 688c2ecf20Sopenharmony_ci hsr_sup_tag->HSR_TLV_type != PRP_TLV_LIFE_CHECK_DD && 698c2ecf20Sopenharmony_ci hsr_sup_tag->HSR_TLV_type != PRP_TLV_LIFE_CHECK_DA) 708c2ecf20Sopenharmony_ci return false; 718c2ecf20Sopenharmony_ci if (hsr_sup_tag->HSR_TLV_length != 12 && 728c2ecf20Sopenharmony_ci hsr_sup_tag->HSR_TLV_length != sizeof(struct hsr_sup_payload)) 738c2ecf20Sopenharmony_ci return false; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci return true; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic struct sk_buff *create_stripped_skb_hsr(struct sk_buff *skb_in, 798c2ecf20Sopenharmony_ci struct hsr_frame_info *frame) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct sk_buff *skb; 828c2ecf20Sopenharmony_ci int copylen; 838c2ecf20Sopenharmony_ci unsigned char *dst, *src; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci skb_pull(skb_in, HSR_HLEN); 868c2ecf20Sopenharmony_ci skb = __pskb_copy(skb_in, skb_headroom(skb_in) - HSR_HLEN, GFP_ATOMIC); 878c2ecf20Sopenharmony_ci skb_push(skb_in, HSR_HLEN); 888c2ecf20Sopenharmony_ci if (!skb) 898c2ecf20Sopenharmony_ci return NULL; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) 948c2ecf20Sopenharmony_ci skb->csum_start -= HSR_HLEN; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci copylen = 2 * ETH_ALEN; 978c2ecf20Sopenharmony_ci if (frame->is_vlan) 988c2ecf20Sopenharmony_ci copylen += VLAN_HLEN; 998c2ecf20Sopenharmony_ci src = skb_mac_header(skb_in); 1008c2ecf20Sopenharmony_ci dst = skb_mac_header(skb); 1018c2ecf20Sopenharmony_ci memcpy(dst, src, copylen); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci skb->protocol = eth_hdr(skb)->h_proto; 1048c2ecf20Sopenharmony_ci return skb; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistruct sk_buff *hsr_get_untagged_frame(struct hsr_frame_info *frame, 1088c2ecf20Sopenharmony_ci struct hsr_port *port) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci if (!frame->skb_std) { 1118c2ecf20Sopenharmony_ci if (frame->skb_hsr) 1128c2ecf20Sopenharmony_ci frame->skb_std = 1138c2ecf20Sopenharmony_ci create_stripped_skb_hsr(frame->skb_hsr, frame); 1148c2ecf20Sopenharmony_ci else 1158c2ecf20Sopenharmony_ci netdev_warn_once(port->dev, 1168c2ecf20Sopenharmony_ci "Unexpected frame received in hsr_get_untagged_frame()\n"); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (!frame->skb_std) 1198c2ecf20Sopenharmony_ci return NULL; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return skb_clone(frame->skb_std, GFP_ATOMIC); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistruct sk_buff *prp_get_untagged_frame(struct hsr_frame_info *frame, 1268c2ecf20Sopenharmony_ci struct hsr_port *port) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci if (!frame->skb_std) { 1298c2ecf20Sopenharmony_ci if (frame->skb_prp) { 1308c2ecf20Sopenharmony_ci /* trim the skb by len - HSR_HLEN to exclude RCT */ 1318c2ecf20Sopenharmony_ci skb_trim(frame->skb_prp, 1328c2ecf20Sopenharmony_ci frame->skb_prp->len - HSR_HLEN); 1338c2ecf20Sopenharmony_ci frame->skb_std = 1348c2ecf20Sopenharmony_ci __pskb_copy(frame->skb_prp, 1358c2ecf20Sopenharmony_ci skb_headroom(frame->skb_prp), 1368c2ecf20Sopenharmony_ci GFP_ATOMIC); 1378c2ecf20Sopenharmony_ci } else { 1388c2ecf20Sopenharmony_ci /* Unexpected */ 1398c2ecf20Sopenharmony_ci WARN_ONCE(1, "%s:%d: Unexpected frame received (port_src %s)\n", 1408c2ecf20Sopenharmony_ci __FILE__, __LINE__, port->dev->name); 1418c2ecf20Sopenharmony_ci return NULL; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return skb_clone(frame->skb_std, GFP_ATOMIC); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic void prp_set_lan_id(struct prp_rct *trailer, 1498c2ecf20Sopenharmony_ci struct hsr_port *port) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci int lane_id; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (port->type == HSR_PT_SLAVE_A) 1548c2ecf20Sopenharmony_ci lane_id = 0; 1558c2ecf20Sopenharmony_ci else 1568c2ecf20Sopenharmony_ci lane_id = 1; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* Add net_id in the upper 3 bits of lane_id */ 1598c2ecf20Sopenharmony_ci lane_id |= port->hsr->net_id; 1608c2ecf20Sopenharmony_ci set_prp_lan_id(trailer, lane_id); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci/* Tailroom for PRP rct should have been created before calling this */ 1648c2ecf20Sopenharmony_cistatic struct sk_buff *prp_fill_rct(struct sk_buff *skb, 1658c2ecf20Sopenharmony_ci struct hsr_frame_info *frame, 1668c2ecf20Sopenharmony_ci struct hsr_port *port) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct prp_rct *trailer; 1698c2ecf20Sopenharmony_ci int min_size = ETH_ZLEN; 1708c2ecf20Sopenharmony_ci int lsdu_size; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (!skb) 1738c2ecf20Sopenharmony_ci return skb; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (frame->is_vlan) 1768c2ecf20Sopenharmony_ci min_size = VLAN_ETH_ZLEN; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (skb_put_padto(skb, min_size)) 1798c2ecf20Sopenharmony_ci return NULL; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci trailer = (struct prp_rct *)skb_put(skb, HSR_HLEN); 1828c2ecf20Sopenharmony_ci lsdu_size = skb->len - 14; 1838c2ecf20Sopenharmony_ci if (frame->is_vlan) 1848c2ecf20Sopenharmony_ci lsdu_size -= 4; 1858c2ecf20Sopenharmony_ci prp_set_lan_id(trailer, port); 1868c2ecf20Sopenharmony_ci set_prp_LSDU_size(trailer, lsdu_size); 1878c2ecf20Sopenharmony_ci trailer->sequence_nr = htons(frame->sequence_nr); 1888c2ecf20Sopenharmony_ci trailer->PRP_suffix = htons(ETH_P_PRP); 1898c2ecf20Sopenharmony_ci skb->protocol = eth_hdr(skb)->h_proto; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return skb; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic void hsr_set_path_id(struct hsr_ethhdr *hsr_ethhdr, 1958c2ecf20Sopenharmony_ci struct hsr_port *port) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci int path_id; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (port->type == HSR_PT_SLAVE_A) 2008c2ecf20Sopenharmony_ci path_id = 0; 2018c2ecf20Sopenharmony_ci else 2028c2ecf20Sopenharmony_ci path_id = 1; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci set_hsr_tag_path(&hsr_ethhdr->hsr_tag, path_id); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic struct sk_buff *hsr_fill_tag(struct sk_buff *skb, 2088c2ecf20Sopenharmony_ci struct hsr_frame_info *frame, 2098c2ecf20Sopenharmony_ci struct hsr_port *port, u8 proto_version) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct hsr_ethhdr *hsr_ethhdr; 2128c2ecf20Sopenharmony_ci int lsdu_size; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* pad to minimum packet size which is 60 + 6 (HSR tag) */ 2158c2ecf20Sopenharmony_ci if (skb_put_padto(skb, ETH_ZLEN + HSR_HLEN)) 2168c2ecf20Sopenharmony_ci return NULL; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci lsdu_size = skb->len - 14; 2198c2ecf20Sopenharmony_ci if (frame->is_vlan) 2208c2ecf20Sopenharmony_ci lsdu_size -= 4; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci hsr_ethhdr = (struct hsr_ethhdr *)skb_mac_header(skb); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci hsr_set_path_id(hsr_ethhdr, port); 2258c2ecf20Sopenharmony_ci set_hsr_tag_LSDU_size(&hsr_ethhdr->hsr_tag, lsdu_size); 2268c2ecf20Sopenharmony_ci hsr_ethhdr->hsr_tag.sequence_nr = htons(frame->sequence_nr); 2278c2ecf20Sopenharmony_ci hsr_ethhdr->hsr_tag.encap_proto = hsr_ethhdr->ethhdr.h_proto; 2288c2ecf20Sopenharmony_ci hsr_ethhdr->ethhdr.h_proto = htons(proto_version ? 2298c2ecf20Sopenharmony_ci ETH_P_HSR : ETH_P_PRP); 2308c2ecf20Sopenharmony_ci skb->protocol = hsr_ethhdr->ethhdr.h_proto; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return skb; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/* If the original frame was an HSR tagged frame, just clone it to be sent 2368c2ecf20Sopenharmony_ci * unchanged. Otherwise, create a private frame especially tagged for 'port'. 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_cistruct sk_buff *hsr_create_tagged_frame(struct hsr_frame_info *frame, 2398c2ecf20Sopenharmony_ci struct hsr_port *port) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci unsigned char *dst, *src; 2428c2ecf20Sopenharmony_ci struct sk_buff *skb; 2438c2ecf20Sopenharmony_ci int movelen; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (frame->skb_hsr) { 2468c2ecf20Sopenharmony_ci struct hsr_ethhdr *hsr_ethhdr = 2478c2ecf20Sopenharmony_ci (struct hsr_ethhdr *)skb_mac_header(frame->skb_hsr); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* set the lane id properly */ 2508c2ecf20Sopenharmony_ci hsr_set_path_id(hsr_ethhdr, port); 2518c2ecf20Sopenharmony_ci return skb_clone(frame->skb_hsr, GFP_ATOMIC); 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* Create the new skb with enough headroom to fit the HSR tag */ 2558c2ecf20Sopenharmony_ci skb = __pskb_copy(frame->skb_std, 2568c2ecf20Sopenharmony_ci skb_headroom(frame->skb_std) + HSR_HLEN, GFP_ATOMIC); 2578c2ecf20Sopenharmony_ci if (!skb) 2588c2ecf20Sopenharmony_ci return NULL; 2598c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) 2628c2ecf20Sopenharmony_ci skb->csum_start += HSR_HLEN; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci movelen = ETH_HLEN; 2658c2ecf20Sopenharmony_ci if (frame->is_vlan) 2668c2ecf20Sopenharmony_ci movelen += VLAN_HLEN; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci src = skb_mac_header(skb); 2698c2ecf20Sopenharmony_ci dst = skb_push(skb, HSR_HLEN); 2708c2ecf20Sopenharmony_ci memmove(dst, src, movelen); 2718c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* skb_put_padto free skb on error and hsr_fill_tag returns NULL in 2748c2ecf20Sopenharmony_ci * that case 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_ci return hsr_fill_tag(skb, frame, port, port->hsr->prot_version); 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistruct sk_buff *prp_create_tagged_frame(struct hsr_frame_info *frame, 2808c2ecf20Sopenharmony_ci struct hsr_port *port) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct sk_buff *skb; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (frame->skb_prp) { 2858c2ecf20Sopenharmony_ci struct prp_rct *trailer = skb_get_PRP_rct(frame->skb_prp); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (trailer) { 2888c2ecf20Sopenharmony_ci prp_set_lan_id(trailer, port); 2898c2ecf20Sopenharmony_ci } else { 2908c2ecf20Sopenharmony_ci WARN_ONCE(!trailer, "errored PRP skb"); 2918c2ecf20Sopenharmony_ci return NULL; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci return skb_clone(frame->skb_prp, GFP_ATOMIC); 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci skb = skb_copy_expand(frame->skb_std, 0, 2978c2ecf20Sopenharmony_ci skb_tailroom(frame->skb_std) + HSR_HLEN, 2988c2ecf20Sopenharmony_ci GFP_ATOMIC); 2998c2ecf20Sopenharmony_ci return prp_fill_rct(skb, frame, port); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic void hsr_deliver_master(struct sk_buff *skb, struct net_device *dev, 3038c2ecf20Sopenharmony_ci struct hsr_node *node_src) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci bool was_multicast_frame; 3068c2ecf20Sopenharmony_ci int res, recv_len; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci was_multicast_frame = (skb->pkt_type == PACKET_MULTICAST); 3098c2ecf20Sopenharmony_ci hsr_addr_subst_source(node_src, skb); 3108c2ecf20Sopenharmony_ci skb_pull(skb, ETH_HLEN); 3118c2ecf20Sopenharmony_ci recv_len = skb->len; 3128c2ecf20Sopenharmony_ci res = netif_rx(skb); 3138c2ecf20Sopenharmony_ci if (res == NET_RX_DROP) { 3148c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 3158c2ecf20Sopenharmony_ci } else { 3168c2ecf20Sopenharmony_ci dev->stats.rx_packets++; 3178c2ecf20Sopenharmony_ci dev->stats.rx_bytes += recv_len; 3188c2ecf20Sopenharmony_ci if (was_multicast_frame) 3198c2ecf20Sopenharmony_ci dev->stats.multicast++; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic int hsr_xmit(struct sk_buff *skb, struct hsr_port *port, 3248c2ecf20Sopenharmony_ci struct hsr_frame_info *frame) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci if (frame->port_rcv->type == HSR_PT_MASTER) { 3278c2ecf20Sopenharmony_ci hsr_addr_subst_dest(frame->node_src, skb, port); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* Address substitution (IEC62439-3 pp 26, 50): replace mac 3308c2ecf20Sopenharmony_ci * address of outgoing frame with that of the outgoing slave's. 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_ci ether_addr_copy(eth_hdr(skb)->h_source, port->dev->dev_addr); 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci return dev_queue_xmit(skb); 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cibool prp_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci return ((frame->port_rcv->type == HSR_PT_SLAVE_A && 3408c2ecf20Sopenharmony_ci port->type == HSR_PT_SLAVE_B) || 3418c2ecf20Sopenharmony_ci (frame->port_rcv->type == HSR_PT_SLAVE_B && 3428c2ecf20Sopenharmony_ci port->type == HSR_PT_SLAVE_A)); 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci/* Forward the frame through all devices except: 3468c2ecf20Sopenharmony_ci * - Back through the receiving device 3478c2ecf20Sopenharmony_ci * - If it's a HSR frame: through a device where it has passed before 3488c2ecf20Sopenharmony_ci * - if it's a PRP frame: through another PRP slave device (no bridge) 3498c2ecf20Sopenharmony_ci * - To the local HSR master only if the frame is directly addressed to it, or 3508c2ecf20Sopenharmony_ci * a non-supervision multicast or broadcast frame. 3518c2ecf20Sopenharmony_ci * 3528c2ecf20Sopenharmony_ci * HSR slave devices should insert a HSR tag into the frame, or forward the 3538c2ecf20Sopenharmony_ci * frame unchanged if it's already tagged. Interlink devices should strip HSR 3548c2ecf20Sopenharmony_ci * tags if they're of the non-HSR type (but only after duplicate discard). The 3558c2ecf20Sopenharmony_ci * master device always strips HSR tags. 3568c2ecf20Sopenharmony_ci */ 3578c2ecf20Sopenharmony_cistatic void hsr_forward_do(struct hsr_frame_info *frame) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct hsr_port *port; 3608c2ecf20Sopenharmony_ci struct sk_buff *skb; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci hsr_for_each_port(frame->port_rcv->hsr, port) { 3638c2ecf20Sopenharmony_ci struct hsr_priv *hsr = port->hsr; 3648c2ecf20Sopenharmony_ci /* Don't send frame back the way it came */ 3658c2ecf20Sopenharmony_ci if (port == frame->port_rcv) 3668c2ecf20Sopenharmony_ci continue; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* Don't deliver locally unless we should */ 3698c2ecf20Sopenharmony_ci if (port->type == HSR_PT_MASTER && !frame->is_local_dest) 3708c2ecf20Sopenharmony_ci continue; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* Deliver frames directly addressed to us to master only */ 3738c2ecf20Sopenharmony_ci if (port->type != HSR_PT_MASTER && frame->is_local_exclusive) 3748c2ecf20Sopenharmony_ci continue; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* Don't send frame over port where it has been sent before. 3778c2ecf20Sopenharmony_ci * Also fro SAN, this shouldn't be done. 3788c2ecf20Sopenharmony_ci */ 3798c2ecf20Sopenharmony_ci if (!frame->is_from_san && 3808c2ecf20Sopenharmony_ci hsr_register_frame_out(port, frame->node_src, 3818c2ecf20Sopenharmony_ci frame->sequence_nr)) 3828c2ecf20Sopenharmony_ci continue; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (frame->is_supervision && port->type == HSR_PT_MASTER) { 3858c2ecf20Sopenharmony_ci hsr_handle_sup_frame(frame); 3868c2ecf20Sopenharmony_ci continue; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* Check if frame is to be dropped. Eg. for PRP no forward 3908c2ecf20Sopenharmony_ci * between ports. 3918c2ecf20Sopenharmony_ci */ 3928c2ecf20Sopenharmony_ci if (hsr->proto_ops->drop_frame && 3938c2ecf20Sopenharmony_ci hsr->proto_ops->drop_frame(frame, port)) 3948c2ecf20Sopenharmony_ci continue; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (port->type != HSR_PT_MASTER) 3978c2ecf20Sopenharmony_ci skb = hsr->proto_ops->create_tagged_frame(frame, port); 3988c2ecf20Sopenharmony_ci else 3998c2ecf20Sopenharmony_ci skb = hsr->proto_ops->get_untagged_frame(frame, port); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (!skb) { 4028c2ecf20Sopenharmony_ci frame->port_rcv->dev->stats.rx_dropped++; 4038c2ecf20Sopenharmony_ci continue; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci skb->dev = port->dev; 4078c2ecf20Sopenharmony_ci if (port->type == HSR_PT_MASTER) 4088c2ecf20Sopenharmony_ci hsr_deliver_master(skb, port->dev, frame->node_src); 4098c2ecf20Sopenharmony_ci else 4108c2ecf20Sopenharmony_ci hsr_xmit(skb, port, frame); 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic void check_local_dest(struct hsr_priv *hsr, struct sk_buff *skb, 4158c2ecf20Sopenharmony_ci struct hsr_frame_info *frame) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci if (hsr_addr_is_self(hsr, eth_hdr(skb)->h_dest)) { 4188c2ecf20Sopenharmony_ci frame->is_local_exclusive = true; 4198c2ecf20Sopenharmony_ci skb->pkt_type = PACKET_HOST; 4208c2ecf20Sopenharmony_ci } else { 4218c2ecf20Sopenharmony_ci frame->is_local_exclusive = false; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (skb->pkt_type == PACKET_HOST || 4258c2ecf20Sopenharmony_ci skb->pkt_type == PACKET_MULTICAST || 4268c2ecf20Sopenharmony_ci skb->pkt_type == PACKET_BROADCAST) { 4278c2ecf20Sopenharmony_ci frame->is_local_dest = true; 4288c2ecf20Sopenharmony_ci } else { 4298c2ecf20Sopenharmony_ci frame->is_local_dest = false; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic void handle_std_frame(struct sk_buff *skb, 4348c2ecf20Sopenharmony_ci struct hsr_frame_info *frame) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci struct hsr_port *port = frame->port_rcv; 4378c2ecf20Sopenharmony_ci struct hsr_priv *hsr = port->hsr; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci frame->skb_hsr = NULL; 4408c2ecf20Sopenharmony_ci frame->skb_prp = NULL; 4418c2ecf20Sopenharmony_ci frame->skb_std = skb; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (port->type != HSR_PT_MASTER) { 4448c2ecf20Sopenharmony_ci frame->is_from_san = true; 4458c2ecf20Sopenharmony_ci } else { 4468c2ecf20Sopenharmony_ci /* Sequence nr for the master node */ 4478c2ecf20Sopenharmony_ci lockdep_assert_held(&hsr->seqnr_lock); 4488c2ecf20Sopenharmony_ci frame->sequence_nr = hsr->sequence_nr; 4498c2ecf20Sopenharmony_ci hsr->sequence_nr++; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ciint hsr_fill_frame_info(__be16 proto, struct sk_buff *skb, 4548c2ecf20Sopenharmony_ci struct hsr_frame_info *frame) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci struct hsr_port *port = frame->port_rcv; 4578c2ecf20Sopenharmony_ci struct hsr_priv *hsr = port->hsr; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* HSRv0 supervisory frames double as a tag so treat them as tagged. */ 4608c2ecf20Sopenharmony_ci if ((!hsr->prot_version && proto == htons(ETH_P_PRP)) || 4618c2ecf20Sopenharmony_ci proto == htons(ETH_P_HSR)) { 4628c2ecf20Sopenharmony_ci /* Check if skb contains hsr_ethhdr */ 4638c2ecf20Sopenharmony_ci if (skb->mac_len < sizeof(struct hsr_ethhdr)) 4648c2ecf20Sopenharmony_ci return -EINVAL; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* HSR tagged frame :- Data or Supervision */ 4678c2ecf20Sopenharmony_ci frame->skb_std = NULL; 4688c2ecf20Sopenharmony_ci frame->skb_prp = NULL; 4698c2ecf20Sopenharmony_ci frame->skb_hsr = skb; 4708c2ecf20Sopenharmony_ci frame->sequence_nr = hsr_get_skb_sequence_nr(skb); 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* Standard frame or PRP from master port */ 4758c2ecf20Sopenharmony_ci handle_std_frame(skb, frame); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci return 0; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ciint prp_fill_frame_info(__be16 proto, struct sk_buff *skb, 4818c2ecf20Sopenharmony_ci struct hsr_frame_info *frame) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci /* Supervision frame */ 4848c2ecf20Sopenharmony_ci struct prp_rct *rct = skb_get_PRP_rct(skb); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (rct && 4878c2ecf20Sopenharmony_ci prp_check_lsdu_size(skb, rct, frame->is_supervision)) { 4888c2ecf20Sopenharmony_ci frame->skb_hsr = NULL; 4898c2ecf20Sopenharmony_ci frame->skb_std = NULL; 4908c2ecf20Sopenharmony_ci frame->skb_prp = skb; 4918c2ecf20Sopenharmony_ci frame->sequence_nr = prp_get_skb_sequence_nr(rct); 4928c2ecf20Sopenharmony_ci return 0; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci handle_std_frame(skb, frame); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci return 0; 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cistatic int fill_frame_info(struct hsr_frame_info *frame, 5008c2ecf20Sopenharmony_ci struct sk_buff *skb, struct hsr_port *port) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci struct hsr_priv *hsr = port->hsr; 5038c2ecf20Sopenharmony_ci struct hsr_vlan_ethhdr *vlan_hdr; 5048c2ecf20Sopenharmony_ci struct ethhdr *ethhdr; 5058c2ecf20Sopenharmony_ci __be16 proto; 5068c2ecf20Sopenharmony_ci int ret; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* Check if skb contains ethhdr */ 5098c2ecf20Sopenharmony_ci if (skb->mac_len < sizeof(struct ethhdr)) 5108c2ecf20Sopenharmony_ci return -EINVAL; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci memset(frame, 0, sizeof(*frame)); 5138c2ecf20Sopenharmony_ci frame->is_supervision = is_supervision_frame(port->hsr, skb); 5148c2ecf20Sopenharmony_ci frame->node_src = hsr_get_node(port, &hsr->node_db, skb, 5158c2ecf20Sopenharmony_ci frame->is_supervision, 5168c2ecf20Sopenharmony_ci port->type); 5178c2ecf20Sopenharmony_ci if (!frame->node_src) 5188c2ecf20Sopenharmony_ci return -1; /* Unknown node and !is_supervision, or no mem */ 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci ethhdr = (struct ethhdr *)skb_mac_header(skb); 5218c2ecf20Sopenharmony_ci frame->is_vlan = false; 5228c2ecf20Sopenharmony_ci proto = ethhdr->h_proto; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci if (proto == htons(ETH_P_8021Q)) 5258c2ecf20Sopenharmony_ci frame->is_vlan = true; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci if (frame->is_vlan) { 5288c2ecf20Sopenharmony_ci vlan_hdr = (struct hsr_vlan_ethhdr *)ethhdr; 5298c2ecf20Sopenharmony_ci proto = vlan_hdr->vlanhdr.h_vlan_encapsulated_proto; 5308c2ecf20Sopenharmony_ci /* FIXME: */ 5318c2ecf20Sopenharmony_ci netdev_warn_once(skb->dev, "VLAN not yet supported"); 5328c2ecf20Sopenharmony_ci return -EINVAL; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci frame->is_from_san = false; 5368c2ecf20Sopenharmony_ci frame->port_rcv = port; 5378c2ecf20Sopenharmony_ci ret = hsr->proto_ops->fill_frame_info(proto, skb, frame); 5388c2ecf20Sopenharmony_ci if (ret) 5398c2ecf20Sopenharmony_ci return ret; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci check_local_dest(port->hsr, skb, frame); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci return 0; 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci/* Must be called holding rcu read lock (because of the port parameter) */ 5478c2ecf20Sopenharmony_civoid hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci struct hsr_frame_info frame; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci rcu_read_lock(); 5528c2ecf20Sopenharmony_ci if (fill_frame_info(&frame, skb, port) < 0) 5538c2ecf20Sopenharmony_ci goto out_drop; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci hsr_register_frame_in(frame.node_src, port, frame.sequence_nr); 5568c2ecf20Sopenharmony_ci hsr_forward_do(&frame); 5578c2ecf20Sopenharmony_ci rcu_read_unlock(); 5588c2ecf20Sopenharmony_ci /* Gets called for ingress frames as well as egress from master port. 5598c2ecf20Sopenharmony_ci * So check and increment stats for master port only here. 5608c2ecf20Sopenharmony_ci */ 5618c2ecf20Sopenharmony_ci if (port->type == HSR_PT_MASTER) { 5628c2ecf20Sopenharmony_ci port->dev->stats.tx_packets++; 5638c2ecf20Sopenharmony_ci port->dev->stats.tx_bytes += skb->len; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci kfree_skb(frame.skb_hsr); 5678c2ecf20Sopenharmony_ci kfree_skb(frame.skb_prp); 5688c2ecf20Sopenharmony_ci kfree_skb(frame.skb_std); 5698c2ecf20Sopenharmony_ci return; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ciout_drop: 5728c2ecf20Sopenharmony_ci rcu_read_unlock(); 5738c2ecf20Sopenharmony_ci port->dev->stats.tx_dropped++; 5748c2ecf20Sopenharmony_ci kfree_skb(skb); 5758c2ecf20Sopenharmony_ci} 576