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