net: hsr: Add support for redbox supervision frames
added support for the redbox supervision frames as defined in the IEC-62439-3:2018. Signed-off-by: Andreas Oetken <andreas.oetken@siemens-energy.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
3247e3ffaf
commit
eafaa88b3e
4 changed files with 113 additions and 26 deletions
|
@ -309,9 +309,9 @@ static void send_hsr_supervision_frame(struct hsr_port *master,
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags);
|
spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags);
|
||||||
|
|
||||||
hsr_stag->HSR_TLV_type = type;
|
hsr_stag->tlv.HSR_TLV_type = type;
|
||||||
/* TODO: Why 12 in HSRv0? */
|
/* TODO: Why 12 in HSRv0? */
|
||||||
hsr_stag->HSR_TLV_length = hsr->prot_version ?
|
hsr_stag->tlv.HSR_TLV_length = hsr->prot_version ?
|
||||||
sizeof(struct hsr_sup_payload) : 12;
|
sizeof(struct hsr_sup_payload) : 12;
|
||||||
|
|
||||||
/* Payload: MacAddressA */
|
/* Payload: MacAddressA */
|
||||||
|
@ -350,8 +350,8 @@ static void send_prp_supervision_frame(struct hsr_port *master,
|
||||||
spin_lock_irqsave(&master->hsr->seqnr_lock, irqflags);
|
spin_lock_irqsave(&master->hsr->seqnr_lock, irqflags);
|
||||||
hsr_stag->sequence_nr = htons(hsr->sup_sequence_nr);
|
hsr_stag->sequence_nr = htons(hsr->sup_sequence_nr);
|
||||||
hsr->sup_sequence_nr++;
|
hsr->sup_sequence_nr++;
|
||||||
hsr_stag->HSR_TLV_type = PRP_TLV_LIFE_CHECK_DD;
|
hsr_stag->tlv.HSR_TLV_type = PRP_TLV_LIFE_CHECK_DD;
|
||||||
hsr_stag->HSR_TLV_length = sizeof(struct hsr_sup_payload);
|
hsr_stag->tlv.HSR_TLV_length = sizeof(struct hsr_sup_payload);
|
||||||
|
|
||||||
/* Payload: MacAddressA */
|
/* Payload: MacAddressA */
|
||||||
hsr_sp = skb_put(skb, sizeof(struct hsr_sup_payload));
|
hsr_sp = skb_put(skb, sizeof(struct hsr_sup_payload));
|
||||||
|
|
|
@ -37,6 +37,8 @@ static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb)
|
||||||
struct ethhdr *eth_hdr;
|
struct ethhdr *eth_hdr;
|
||||||
struct hsr_sup_tag *hsr_sup_tag;
|
struct hsr_sup_tag *hsr_sup_tag;
|
||||||
struct hsrv1_ethhdr_sp *hsr_V1_hdr;
|
struct hsrv1_ethhdr_sp *hsr_V1_hdr;
|
||||||
|
struct hsr_sup_tlv *hsr_sup_tlv;
|
||||||
|
u16 total_length = 0;
|
||||||
|
|
||||||
WARN_ON_ONCE(!skb_mac_header_was_set(skb));
|
WARN_ON_ONCE(!skb_mac_header_was_set(skb));
|
||||||
eth_hdr = (struct ethhdr *)skb_mac_header(skb);
|
eth_hdr = (struct ethhdr *)skb_mac_header(skb);
|
||||||
|
@ -53,23 +55,63 @@ static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb)
|
||||||
|
|
||||||
/* Get the supervision header from correct location. */
|
/* Get the supervision header from correct location. */
|
||||||
if (eth_hdr->h_proto == htons(ETH_P_HSR)) { /* Okay HSRv1. */
|
if (eth_hdr->h_proto == htons(ETH_P_HSR)) { /* Okay HSRv1. */
|
||||||
|
total_length = sizeof(struct hsrv1_ethhdr_sp);
|
||||||
|
if (!pskb_may_pull(skb, total_length))
|
||||||
|
return false;
|
||||||
|
|
||||||
hsr_V1_hdr = (struct hsrv1_ethhdr_sp *)skb_mac_header(skb);
|
hsr_V1_hdr = (struct hsrv1_ethhdr_sp *)skb_mac_header(skb);
|
||||||
if (hsr_V1_hdr->hsr.encap_proto != htons(ETH_P_PRP))
|
if (hsr_V1_hdr->hsr.encap_proto != htons(ETH_P_PRP))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
hsr_sup_tag = &hsr_V1_hdr->hsr_sup;
|
hsr_sup_tag = &hsr_V1_hdr->hsr_sup;
|
||||||
} else {
|
} else {
|
||||||
|
total_length = sizeof(struct hsrv0_ethhdr_sp);
|
||||||
|
if (!pskb_may_pull(skb, total_length))
|
||||||
|
return false;
|
||||||
|
|
||||||
hsr_sup_tag =
|
hsr_sup_tag =
|
||||||
&((struct hsrv0_ethhdr_sp *)skb_mac_header(skb))->hsr_sup;
|
&((struct hsrv0_ethhdr_sp *)skb_mac_header(skb))->hsr_sup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hsr_sup_tag->HSR_TLV_type != HSR_TLV_ANNOUNCE &&
|
if (hsr_sup_tag->tlv.HSR_TLV_type != HSR_TLV_ANNOUNCE &&
|
||||||
hsr_sup_tag->HSR_TLV_type != HSR_TLV_LIFE_CHECK &&
|
hsr_sup_tag->tlv.HSR_TLV_type != HSR_TLV_LIFE_CHECK &&
|
||||||
hsr_sup_tag->HSR_TLV_type != PRP_TLV_LIFE_CHECK_DD &&
|
hsr_sup_tag->tlv.HSR_TLV_type != PRP_TLV_LIFE_CHECK_DD &&
|
||||||
hsr_sup_tag->HSR_TLV_type != PRP_TLV_LIFE_CHECK_DA)
|
hsr_sup_tag->tlv.HSR_TLV_type != PRP_TLV_LIFE_CHECK_DA)
|
||||||
return false;
|
return false;
|
||||||
if (hsr_sup_tag->HSR_TLV_length != 12 &&
|
if (hsr_sup_tag->tlv.HSR_TLV_length != 12 &&
|
||||||
hsr_sup_tag->HSR_TLV_length != sizeof(struct hsr_sup_payload))
|
hsr_sup_tag->tlv.HSR_TLV_length != sizeof(struct hsr_sup_payload))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Get next tlv */
|
||||||
|
total_length += sizeof(struct hsr_sup_tlv) + hsr_sup_tag->tlv.HSR_TLV_length;
|
||||||
|
if (!pskb_may_pull(skb, total_length))
|
||||||
|
return false;
|
||||||
|
skb_pull(skb, total_length);
|
||||||
|
hsr_sup_tlv = (struct hsr_sup_tlv *)skb->data;
|
||||||
|
skb_push(skb, total_length);
|
||||||
|
|
||||||
|
/* if this is a redbox supervision frame we need to verify
|
||||||
|
* that more data is available
|
||||||
|
*/
|
||||||
|
if (hsr_sup_tlv->HSR_TLV_type == PRP_TLV_REDBOX_MAC) {
|
||||||
|
/* tlv length must be a length of a mac address */
|
||||||
|
if (hsr_sup_tlv->HSR_TLV_length != sizeof(struct hsr_sup_payload))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* make sure another tlv follows */
|
||||||
|
total_length += sizeof(struct hsr_sup_tlv) + hsr_sup_tlv->HSR_TLV_length;
|
||||||
|
if (!pskb_may_pull(skb, total_length))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* get next tlv */
|
||||||
|
skb_pull(skb, total_length);
|
||||||
|
hsr_sup_tlv = (struct hsr_sup_tlv *)skb->data;
|
||||||
|
skb_push(skb, total_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* end of tlvs must follow at the end */
|
||||||
|
if (hsr_sup_tlv->HSR_TLV_type == HSR_TLV_EOT &&
|
||||||
|
hsr_sup_tlv->HSR_TLV_length != 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -265,11 +265,14 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
|
||||||
struct hsr_port *port_rcv = frame->port_rcv;
|
struct hsr_port *port_rcv = frame->port_rcv;
|
||||||
struct hsr_priv *hsr = port_rcv->hsr;
|
struct hsr_priv *hsr = port_rcv->hsr;
|
||||||
struct hsr_sup_payload *hsr_sp;
|
struct hsr_sup_payload *hsr_sp;
|
||||||
|
struct hsr_sup_tlv *hsr_sup_tlv;
|
||||||
struct hsr_node *node_real;
|
struct hsr_node *node_real;
|
||||||
struct sk_buff *skb = NULL;
|
struct sk_buff *skb = NULL;
|
||||||
struct list_head *node_db;
|
struct list_head *node_db;
|
||||||
struct ethhdr *ethhdr;
|
struct ethhdr *ethhdr;
|
||||||
int i;
|
int i;
|
||||||
|
unsigned int pull_size = 0;
|
||||||
|
unsigned int total_pull_size = 0;
|
||||||
|
|
||||||
/* Here either frame->skb_hsr or frame->skb_prp should be
|
/* Here either frame->skb_hsr or frame->skb_prp should be
|
||||||
* valid as supervision frame always will have protocol
|
* valid as supervision frame always will have protocol
|
||||||
|
@ -284,18 +287,26 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
|
||||||
if (!skb)
|
if (!skb)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* Leave the ethernet header. */
|
||||||
|
pull_size = sizeof(struct ethhdr);
|
||||||
|
skb_pull(skb, pull_size);
|
||||||
|
total_pull_size += pull_size;
|
||||||
|
|
||||||
ethhdr = (struct ethhdr *)skb_mac_header(skb);
|
ethhdr = (struct ethhdr *)skb_mac_header(skb);
|
||||||
|
|
||||||
/* Leave the ethernet header. */
|
|
||||||
skb_pull(skb, sizeof(struct ethhdr));
|
|
||||||
|
|
||||||
/* And leave the HSR tag. */
|
/* And leave the HSR tag. */
|
||||||
if (ethhdr->h_proto == htons(ETH_P_HSR))
|
if (ethhdr->h_proto == htons(ETH_P_HSR)) {
|
||||||
skb_pull(skb, sizeof(struct hsr_tag));
|
pull_size = sizeof(struct ethhdr);
|
||||||
|
skb_pull(skb, pull_size);
|
||||||
|
total_pull_size += pull_size;
|
||||||
|
}
|
||||||
|
|
||||||
/* And leave the HSR sup tag. */
|
/* And leave the HSR sup tag. */
|
||||||
skb_pull(skb, sizeof(struct hsr_sup_tag));
|
pull_size = sizeof(struct hsr_tag);
|
||||||
|
skb_pull(skb, pull_size);
|
||||||
|
total_pull_size += pull_size;
|
||||||
|
|
||||||
|
/* get HSR sup payload */
|
||||||
hsr_sp = (struct hsr_sup_payload *)skb->data;
|
hsr_sp = (struct hsr_sup_payload *)skb->data;
|
||||||
|
|
||||||
/* Merge node_curr (registered on macaddress_B) into node_real */
|
/* Merge node_curr (registered on macaddress_B) into node_real */
|
||||||
|
@ -312,6 +323,37 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
|
||||||
/* Node has already been merged */
|
/* Node has already been merged */
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
/* Leave the first HSR sup payload. */
|
||||||
|
pull_size = sizeof(struct hsr_sup_payload);
|
||||||
|
skb_pull(skb, pull_size);
|
||||||
|
total_pull_size += pull_size;
|
||||||
|
|
||||||
|
/* Get second supervision tlv */
|
||||||
|
hsr_sup_tlv = (struct hsr_sup_tlv *)skb->data;
|
||||||
|
/* And check if it is a redbox mac TLV */
|
||||||
|
if (hsr_sup_tlv->HSR_TLV_type == PRP_TLV_REDBOX_MAC) {
|
||||||
|
/* We could stop here after pushing hsr_sup_payload,
|
||||||
|
* or proceed and allow macaddress_B and for redboxes.
|
||||||
|
*/
|
||||||
|
/* Sanity check length */
|
||||||
|
if (hsr_sup_tlv->HSR_TLV_length != 6)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
/* Leave the second HSR sup tlv. */
|
||||||
|
pull_size = sizeof(struct hsr_sup_tlv);
|
||||||
|
skb_pull(skb, pull_size);
|
||||||
|
total_pull_size += pull_size;
|
||||||
|
|
||||||
|
/* Get redbox mac address. */
|
||||||
|
hsr_sp = (struct hsr_sup_payload *)skb->data;
|
||||||
|
|
||||||
|
/* Check if redbox mac and node mac are equal. */
|
||||||
|
if (!ether_addr_equal(node_real->macaddress_A, hsr_sp->macaddress_A)) {
|
||||||
|
/* This is a redbox supervision frame for a VDAN! */
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ether_addr_copy(node_real->macaddress_B, ethhdr->h_source);
|
ether_addr_copy(node_real->macaddress_B, ethhdr->h_source);
|
||||||
for (i = 0; i < HSR_PT_PORTS; i++) {
|
for (i = 0; i < HSR_PT_PORTS; i++) {
|
||||||
if (!node_curr->time_in_stale[i] &&
|
if (!node_curr->time_in_stale[i] &&
|
||||||
|
@ -331,11 +373,8 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
|
||||||
kfree_rcu(node_curr, rcu_head);
|
kfree_rcu(node_curr, rcu_head);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
/* PRP uses v0 header */
|
/* Push back here */
|
||||||
if (ethhdr->h_proto == htons(ETH_P_HSR))
|
skb_push(skb, total_pull_size);
|
||||||
skb_push(skb, sizeof(struct hsrv1_ethhdr_sp));
|
|
||||||
else
|
|
||||||
skb_push(skb, sizeof(struct hsrv0_ethhdr_sp));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 'skb' is a frame meant for this host, that is to be passed to upper layers.
|
/* 'skb' is a frame meant for this host, that is to be passed to upper layers.
|
||||||
|
|
|
@ -35,13 +35,15 @@
|
||||||
* HSR_NODE_FORGET_TIME?
|
* HSR_NODE_FORGET_TIME?
|
||||||
*/
|
*/
|
||||||
#define PRUNE_PERIOD 3000 /* ms */
|
#define PRUNE_PERIOD 3000 /* ms */
|
||||||
|
#define HSR_TLV_EOT 0 /* End of TLVs */
|
||||||
#define HSR_TLV_ANNOUNCE 22
|
#define HSR_TLV_ANNOUNCE 22
|
||||||
#define HSR_TLV_LIFE_CHECK 23
|
#define HSR_TLV_LIFE_CHECK 23
|
||||||
/* PRP V1 life check for Duplicate discard */
|
/* PRP V1 life check for Duplicate discard */
|
||||||
#define PRP_TLV_LIFE_CHECK_DD 20
|
#define PRP_TLV_LIFE_CHECK_DD 20
|
||||||
/* PRP V1 life check for Duplicate Accept */
|
/* PRP V1 life check for Duplicate Accept */
|
||||||
#define PRP_TLV_LIFE_CHECK_DA 21
|
#define PRP_TLV_LIFE_CHECK_DA 21
|
||||||
|
/* PRP V1 life redundancy box MAC address */
|
||||||
|
#define PRP_TLV_REDBOX_MAC 30
|
||||||
|
|
||||||
/* HSR Tag.
|
/* HSR Tag.
|
||||||
* As defined in IEC-62439-3:2010, the HSR tag is really { ethertype = 0x88FB,
|
* As defined in IEC-62439-3:2010, the HSR tag is really { ethertype = 0x88FB,
|
||||||
|
@ -94,14 +96,18 @@ struct hsr_vlan_ethhdr {
|
||||||
struct hsr_tag hsr_tag;
|
struct hsr_tag hsr_tag;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
struct hsr_sup_tlv {
|
||||||
|
u8 HSR_TLV_type;
|
||||||
|
u8 HSR_TLV_length;
|
||||||
|
};
|
||||||
|
|
||||||
/* HSR/PRP Supervision Frame data types.
|
/* HSR/PRP Supervision Frame data types.
|
||||||
* Field names as defined in the IEC:2010 standard for HSR.
|
* Field names as defined in the IEC:2010 standard for HSR.
|
||||||
*/
|
*/
|
||||||
struct hsr_sup_tag {
|
struct hsr_sup_tag {
|
||||||
__be16 path_and_HSR_ver;
|
__be16 path_and_HSR_ver;
|
||||||
__be16 sequence_nr;
|
__be16 sequence_nr;
|
||||||
__u8 HSR_TLV_type;
|
struct hsr_sup_tlv tlv;
|
||||||
__u8 HSR_TLV_length;
|
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
struct hsr_sup_payload {
|
struct hsr_sup_payload {
|
||||||
|
|
Loading…
Add table
Reference in a new issue