[net-next RFC PATCH 11/13] net: prp: add supervision frame generation and handling support

From: Murali Karicheri
Date: Wed May 06 2020 - 12:31:06 EST


Add support for sending and handling supervision frames. For PRP,
supervision frame format is similar to HSR version 0, but have a
PRP Redunancy Control Trailor (RCT) added.

Signed-off-by: Murali Karicheri <m-karicheri2@xxxxxx>
---
net/hsr-prp/hsr_prp_device.c | 50 ++++++++++++++++++++++++++++-------
net/hsr-prp/hsr_prp_forward.c | 4 ++-
net/hsr-prp/hsr_prp_main.h | 22 +++++++++++++++
3 files changed, 65 insertions(+), 11 deletions(-)

diff --git a/net/hsr-prp/hsr_prp_device.c b/net/hsr-prp/hsr_prp_device.c
index 501de23a97f5..3c463e185f64 100644
--- a/net/hsr-prp/hsr_prp_device.c
+++ b/net/hsr-prp/hsr_prp_device.c
@@ -237,13 +237,20 @@ static void send_hsr_prp_supervision_frame(struct hsr_prp_port *master,
{
struct sk_buff *skb;
int hlen, tlen;
- struct hsr_tag *hsr_tag;
+ struct hsr_tag *hsr_tag = NULL;
+ struct prp_rct *rct;
struct hsr_prp_sup_tag *hsr_stag;
struct hsr_prp_sup_payload *hsr_sp;
unsigned long irqflags;
+ u16 proto;
+ u8 *tail;

hlen = LL_RESERVED_SPACE(master->dev);
tlen = master->dev->needed_tailroom;
+ /* skb size is same for PRP/HSR frames, only difference
+ * being for PRP, it is a trailor and for HSR it is a
+ * header
+ */
skb = dev_alloc_skb(sizeof(struct hsr_tag) +
sizeof(struct hsr_prp_sup_tag) +
sizeof(struct hsr_prp_sup_payload) + hlen + tlen);
@@ -252,12 +259,15 @@ static void send_hsr_prp_supervision_frame(struct hsr_prp_port *master,
return;

skb_reserve(skb, hlen);
-
+ if (!proto_ver)
+ proto = ETH_P_PRP;
+ else
+ proto = (proto_ver == HSR_V1) ? ETH_P_HSR : ETH_P_PRP;
skb->dev = master->dev;
- skb->protocol = htons(proto_ver ? ETH_P_HSR : ETH_P_PRP);
+ skb->protocol = htons(proto);
skb->priority = TC_PRIO_CONTROL;

- if (dev_hard_header(skb, skb->dev, (proto_ver ? ETH_P_HSR : ETH_P_PRP),
+ if (dev_hard_header(skb, skb->dev, proto,
master->priv->sup_multicast_addr,
skb->dev->dev_addr, skb->len) <= 0)
goto out;
@@ -265,7 +275,7 @@ static void send_hsr_prp_supervision_frame(struct hsr_prp_port *master,
skb_reset_network_header(skb);
skb_reset_transport_header(skb);

- if (proto_ver > 0) {
+ if (proto_ver == HSR_V1) {
hsr_tag = skb_put(skb, sizeof(struct hsr_tag));
hsr_tag->encap_proto = htons(ETH_P_PRP);
set_hsr_tag_LSDU_size(hsr_tag, HSR_V1_SUP_LSDUSIZE);
@@ -273,15 +283,19 @@ static void send_hsr_prp_supervision_frame(struct hsr_prp_port *master,

hsr_stag = skb_put(skb, sizeof(struct hsr_prp_sup_tag));
set_hsr_stag_path(hsr_stag, (proto_ver ? 0x0 : 0xf));
- set_hsr_stag_HSR_ver(hsr_stag, proto_ver);
+ set_hsr_stag_HSR_ver(hsr_stag, proto_ver ? 0x1 : 0x0);

/* From HSRv1 on we have separate supervision sequence numbers. */
spin_lock_irqsave(&master->priv->seqnr_lock, irqflags);
if (proto_ver > 0) {
hsr_stag->sequence_nr = htons(master->priv->sup_sequence_nr);
- hsr_tag->sequence_nr = htons(master->priv->sequence_nr);
+ if (hsr_tag)
+ hsr_tag->sequence_nr = htons(master->priv->sequence_nr);
master->priv->sup_sequence_nr++;
- master->priv->sequence_nr++;
+ if (proto_ver == HSR_V1) {
+ hsr_tag->sequence_nr = htons(master->priv->sequence_nr);
+ master->priv->sequence_nr++;
+ }
} else {
hsr_stag->sequence_nr = htons(master->priv->sequence_nr);
master->priv->sequence_nr++;
@@ -300,6 +314,16 @@ static void send_hsr_prp_supervision_frame(struct hsr_prp_port *master,
if (skb_put_padto(skb, ETH_ZLEN + HSR_PRP_HLEN))
return;

+ spin_lock_irqsave(&master->priv->seqnr_lock, irqflags);
+ if (proto_ver == PRP_V1) {
+ tail = skb_tail_pointer(skb) - HSR_PRP_HLEN;
+ rct = (struct prp_rct *)tail;
+ rct->PRP_suffix = htons(ETH_P_PRP);
+ set_prp_LSDU_size(rct, HSR_V1_SUP_LSDUSIZE);
+ rct->sequence_nr = htons(master->priv->sequence_nr);
+ master->priv->sequence_nr++;
+ }
+ spin_unlock_irqrestore(&master->priv->seqnr_lock, irqflags);
hsr_prp_forward_skb(skb, master);
return;

@@ -328,8 +352,14 @@ static void hsr_prp_announce(struct timer_list *t)

interval = msecs_to_jiffies(HSR_PRP_ANNOUNCE_INTERVAL);
} else {
- send_hsr_prp_supervision_frame(master, HSR_TLV_LIFE_CHECK,
- priv->prot_version);
+ if (priv->prot_version <= HSR_V1)
+ send_hsr_prp_supervision_frame(master,
+ HSR_TLV_LIFE_CHECK,
+ priv->prot_version);
+ else /* PRP */
+ send_hsr_prp_supervision_frame(master,
+ PRP_TLV_LIFE_CHECK_DD,
+ priv->prot_version);

interval = msecs_to_jiffies(HSR_PRP_LIFE_CHECK_INTERVAL);
}
diff --git a/net/hsr-prp/hsr_prp_forward.c b/net/hsr-prp/hsr_prp_forward.c
index 59b33d711ea6..d7e975919322 100644
--- a/net/hsr-prp/hsr_prp_forward.c
+++ b/net/hsr-prp/hsr_prp_forward.c
@@ -74,7 +74,9 @@ static bool is_supervision_frame(struct hsr_prp_priv *priv, struct sk_buff *skb)
}

if (hsr_sup_tag->HSR_TLV_type != HSR_TLV_ANNOUNCE &&
- hsr_sup_tag->HSR_TLV_type != HSR_TLV_LIFE_CHECK)
+ hsr_sup_tag->HSR_TLV_type != HSR_TLV_LIFE_CHECK &&
+ hsr_sup_tag->HSR_TLV_type != PRP_TLV_LIFE_CHECK_DD &&
+ hsr_sup_tag->HSR_TLV_type != PRP_TLV_LIFE_CHECK_DA)
return false;
if (hsr_sup_tag->HSR_TLV_length != 12 &&
hsr_sup_tag->HSR_TLV_length != sizeof(struct hsr_prp_sup_payload))
diff --git a/net/hsr-prp/hsr_prp_main.h b/net/hsr-prp/hsr_prp_main.h
index 00c312e5189f..17049d040226 100644
--- a/net/hsr-prp/hsr_prp_main.h
+++ b/net/hsr-prp/hsr_prp_main.h
@@ -33,6 +33,10 @@

#define HSR_TLV_ANNOUNCE 22
#define HSR_TLV_LIFE_CHECK 23
+/* PRP V1 life check for Duplicate discard */
+#define PRP_TLV_LIFE_CHECK_DD 20
+/* PRP V1 life check for Duplicate Accept */
+#define PRP_TLV_LIFE_CHECK_DA 21

/* HSR Tag.
* As defined in IEC-62439-3:2010, the HSR tag is really { ethertype = 0x88FB,
@@ -125,6 +129,24 @@ enum hsr_prp_port_type {
HSR_PRP_PT_PORTS, /* This must be the last item in the enum */
};

+/* PRP Redunancy Control Trailor (RCT).
+ * As defined in IEC-62439-4:2012, the PRP RCT is really { sequence Nr,
+ * Lan indentifier (LanId), LSDU_size and PRP_suffix = 0x88FB }.
+ *
+ * Field names as defined in the IEC:2012 standard for PRP.
+ */
+struct prp_rct {
+ __be16 sequence_nr;
+ __be16 lan_id_and_LSDU_size;
+ __be16 PRP_suffix;
+} __packed;
+
+static inline void set_prp_LSDU_size(struct prp_rct *rct, u16 LSDU_size)
+{
+ rct->lan_id_and_LSDU_size = htons((ntohs(rct->lan_id_and_LSDU_size) &
+ 0xF000) | (LSDU_size & 0x0FFF));
+}
+
struct hsr_prp_port {
struct list_head port_list;
struct net_device *dev;
--
2.17.1