Skip to content

Commit

Permalink
net/hsr: Added support for HSR v1
Browse files Browse the repository at this point in the history
This patch adds support for the newer version 1 of the HSR
networking standard. Version 0 is still default and the new
version has to be selected via iproute2.

Main changes are in the supervision frame handling and its
ethertype field.

Signed-off-by: Peter Heise <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
peterheise authored and davem330 committed Apr 15, 2016
1 parent 125c8d1 commit ee1c279
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 63 deletions.
1 change: 1 addition & 0 deletions include/uapi/linux/if_ether.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
#define ETH_P_TDLS 0x890D /* TDLS */
#define ETH_P_FIP 0x8914 /* FCoE Initialization Protocol */
#define ETH_P_80221 0x8917 /* IEEE 802.21 Media Independent Handover Protocol */
#define ETH_P_HSR 0x892F /* IEC 62439-3 HSRv1 */
#define ETH_P_LOOPBACK 0x9000 /* Ethernet loopback packet, per IEEE 802.3 */
#define ETH_P_QINQ1 0x9100 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
#define ETH_P_QINQ2 0x9200 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
Expand Down
1 change: 1 addition & 0 deletions include/uapi/linux/if_link.h
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,7 @@ enum {
IFLA_HSR_SLAVE1,
IFLA_HSR_SLAVE2,
IFLA_HSR_MULTICAST_SPEC, /* Last byte of supervision addr */
IFLA_HSR_VERSION, /* HSR version */
IFLA_HSR_SUPERVISION_ADDR, /* Supervision frame multicast addr */
IFLA_HSR_SEQ_NR,
__IFLA_HSR_MAX,
Expand Down
5 changes: 3 additions & 2 deletions net/hsr/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ config HSR
earlier.

This code is a "best effort" to comply with the HSR standard as
described in IEC 62439-3:2010 (HSRv0), but no compliancy tests have
been made.
described in IEC 62439-3:2010 (HSRv0) and IEC 62439-3:2012 (HSRv1),
but no compliancy tests have been made. Use iproute2 to select
the version you desire.

You need to perform any and all necessary tests yourself before
relying on this code in a safety critical system!
Expand Down
80 changes: 46 additions & 34 deletions net/hsr/hsr_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ static void hsr_check_announce(struct net_device *hsr_dev,

hsr = netdev_priv(hsr_dev);

if ((hsr_dev->operstate == IF_OPER_UP) && (old_operstate != IF_OPER_UP)) {
if ((hsr_dev->operstate == IF_OPER_UP)
&& (old_operstate != IF_OPER_UP)) {
/* Went up */
hsr->announce_count = 0;
hsr->announce_timer.expires = jiffies +
Expand Down Expand Up @@ -250,64 +251,71 @@ static const struct header_ops hsr_header_ops = {
.parse = eth_header_parse,
};


/* HSR:2010 supervision frames should be padded so that the whole frame,
* including headers and FCS, is 64 bytes (without VLAN).
*/
static int hsr_pad(int size)
{
const int min_size = ETH_ZLEN - HSR_HLEN - ETH_HLEN;

if (size >= min_size)
return size;
return min_size;
}

static void send_hsr_supervision_frame(struct hsr_port *master, u8 type)
static void send_hsr_supervision_frame(struct hsr_port *master,
u8 type, u8 hsrVer)
{
struct sk_buff *skb;
int hlen, tlen;
struct hsr_tag *hsr_tag;
struct hsr_sup_tag *hsr_stag;
struct hsr_sup_payload *hsr_sp;
unsigned long irqflags;

hlen = LL_RESERVED_SPACE(master->dev);
tlen = master->dev->needed_tailroom;
skb = alloc_skb(hsr_pad(sizeof(struct hsr_sup_payload)) + hlen + tlen,
GFP_ATOMIC);
skb = dev_alloc_skb(
sizeof(struct hsr_tag) +
sizeof(struct hsr_sup_tag) +
sizeof(struct hsr_sup_payload) + hlen + tlen);

if (skb == NULL)
return;

skb_reserve(skb, hlen);

skb->dev = master->dev;
skb->protocol = htons(ETH_P_PRP);
skb->protocol = htons(hsrVer ? ETH_P_HSR : ETH_P_PRP);
skb->priority = TC_PRIO_CONTROL;

if (dev_hard_header(skb, skb->dev, ETH_P_PRP,
if (dev_hard_header(skb, skb->dev, (hsrVer ? ETH_P_HSR : ETH_P_PRP),
master->hsr->sup_multicast_addr,
skb->dev->dev_addr, skb->len) <= 0)
goto out;
skb_reset_mac_header(skb);

hsr_stag = (typeof(hsr_stag)) skb_put(skb, sizeof(*hsr_stag));
if (hsrVer > 0) {
hsr_tag = (typeof(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);
}

set_hsr_stag_path(hsr_stag, 0xf);
set_hsr_stag_HSR_Ver(hsr_stag, 0);
hsr_stag = (typeof(hsr_stag)) skb_put(skb, sizeof(struct hsr_sup_tag));
set_hsr_stag_path(hsr_stag, (hsrVer ? 0x0 : 0xf));
set_hsr_stag_HSR_Ver(hsr_stag, hsrVer);

/* From HSRv1 on we have separate supervision sequence numbers. */
spin_lock_irqsave(&master->hsr->seqnr_lock, irqflags);
hsr_stag->sequence_nr = htons(master->hsr->sequence_nr);
master->hsr->sequence_nr++;
if (hsrVer > 0) {
hsr_stag->sequence_nr = htons(master->hsr->sup_sequence_nr);
hsr_tag->sequence_nr = htons(master->hsr->sequence_nr);
master->hsr->sup_sequence_nr++;
master->hsr->sequence_nr++;
} else {
hsr_stag->sequence_nr = htons(master->hsr->sequence_nr);
master->hsr->sequence_nr++;
}
spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags);

hsr_stag->HSR_TLV_Type = type;
hsr_stag->HSR_TLV_Length = 12;
/* TODO: Why 12 in HSRv0? */
hsr_stag->HSR_TLV_Length = hsrVer ? sizeof(struct hsr_sup_payload) : 12;

/* Payload: MacAddressA */
hsr_sp = (typeof(hsr_sp)) skb_put(skb, sizeof(*hsr_sp));
hsr_sp = (typeof(hsr_sp)) skb_put(skb, sizeof(struct hsr_sup_payload));
ether_addr_copy(hsr_sp->MacAddressA, master->dev->dev_addr);

skb_put_padto(skb, ETH_ZLEN + HSR_HLEN);

hsr_forward_skb(skb, master);
return;

Expand All @@ -329,19 +337,20 @@ static void hsr_announce(unsigned long data)
rcu_read_lock();
master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);

if (hsr->announce_count < 3) {
send_hsr_supervision_frame(master, HSR_TLV_ANNOUNCE);
if (hsr->announce_count < 3 && hsr->protVersion == 0) {
send_hsr_supervision_frame(master, HSR_TLV_ANNOUNCE,
hsr->protVersion);
hsr->announce_count++;
} else {
send_hsr_supervision_frame(master, HSR_TLV_LIFE_CHECK);
}

if (hsr->announce_count < 3)
hsr->announce_timer.expires = jiffies +
msecs_to_jiffies(HSR_ANNOUNCE_INTERVAL);
else
} else {
send_hsr_supervision_frame(master, HSR_TLV_LIFE_CHECK,
hsr->protVersion);

hsr->announce_timer.expires = jiffies +
msecs_to_jiffies(HSR_LIFE_CHECK_INTERVAL);
}

if (is_admin_up(master->dev))
add_timer(&hsr->announce_timer);
Expand Down Expand Up @@ -428,7 +437,7 @@ static const unsigned char def_multicast_addr[ETH_ALEN] __aligned(2) = {
};

int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
unsigned char multicast_spec)
unsigned char multicast_spec, u8 protocol_version)
{
struct hsr_priv *hsr;
struct hsr_port *port;
Expand All @@ -450,6 +459,7 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
spin_lock_init(&hsr->seqnr_lock);
/* Overflow soon to find bugs easier: */
hsr->sequence_nr = HSR_SEQNR_START;
hsr->sup_sequence_nr = HSR_SUP_SEQNR_START;

init_timer(&hsr->announce_timer);
hsr->announce_timer.function = hsr_announce;
Expand All @@ -462,6 +472,8 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
ether_addr_copy(hsr->sup_multicast_addr, def_multicast_addr);
hsr->sup_multicast_addr[ETH_ALEN - 1] = multicast_spec;

hsr->protVersion = protocol_version;

/* FIXME: should I modify the value of these?
*
* - hsr_dev->flags - i.e.
Expand Down
2 changes: 1 addition & 1 deletion net/hsr/hsr_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

void hsr_dev_setup(struct net_device *dev);
int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
unsigned char multicast_spec);
unsigned char multicast_spec, u8 protocol_version);
void hsr_check_carrier_and_operstate(struct hsr_priv *hsr);
bool is_hsr_master(struct net_device *dev);
int hsr_get_max_mtu(struct hsr_priv *hsr);
Expand Down
43 changes: 32 additions & 11 deletions net/hsr/hsr_forward.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,21 +50,40 @@ struct hsr_frame_info {
*/
static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb)
{
struct hsr_ethhdr_sp *hdr;
struct ethhdr *ethHdr;
struct hsr_sup_tag *hsrSupTag;
struct hsrv1_ethhdr_sp *hsrV1Hdr;

WARN_ON_ONCE(!skb_mac_header_was_set(skb));
hdr = (struct hsr_ethhdr_sp *) skb_mac_header(skb);
ethHdr = (struct ethhdr *) skb_mac_header(skb);

if (!ether_addr_equal(hdr->ethhdr.h_dest,
/* Correct addr? */
if (!ether_addr_equal(ethHdr->h_dest,
hsr->sup_multicast_addr))
return false;

if (get_hsr_stag_path(&hdr->hsr_sup) != 0x0f)
/* Correct ether type?. */
if (!(ethHdr->h_proto == htons(ETH_P_PRP)
|| ethHdr->h_proto == htons(ETH_P_HSR)))
return false;
if ((hdr->hsr_sup.HSR_TLV_Type != HSR_TLV_ANNOUNCE) &&
(hdr->hsr_sup.HSR_TLV_Type != HSR_TLV_LIFE_CHECK))

/* Get the supervision header from correct location. */
if (ethHdr->h_proto == htons(ETH_P_HSR)) { /* Okay HSRv1. */
hsrV1Hdr = (struct hsrv1_ethhdr_sp *) skb_mac_header(skb);
if (hsrV1Hdr->hsr.encap_proto != htons(ETH_P_PRP))
return false;

hsrSupTag = &hsrV1Hdr->hsr_sup;
} else {
hsrSupTag = &((struct hsrv0_ethhdr_sp *) skb_mac_header(skb))->hsr_sup;
}

if ((hsrSupTag->HSR_TLV_Type != HSR_TLV_ANNOUNCE) &&
(hsrSupTag->HSR_TLV_Type != HSR_TLV_LIFE_CHECK))
return false;
if (hdr->hsr_sup.HSR_TLV_Length != 12)
if ((hsrSupTag->HSR_TLV_Length != 12) &&
(hsrSupTag->HSR_TLV_Length !=
sizeof(struct hsr_sup_payload)))
return false;

return true;
Expand Down Expand Up @@ -110,7 +129,7 @@ static struct sk_buff *frame_get_stripped_skb(struct hsr_frame_info *frame,


static void hsr_fill_tag(struct sk_buff *skb, struct hsr_frame_info *frame,
struct hsr_port *port)
struct hsr_port *port, u8 protoVersion)
{
struct hsr_ethhdr *hsr_ethhdr;
int lane_id;
Expand All @@ -131,7 +150,8 @@ static void hsr_fill_tag(struct sk_buff *skb, struct hsr_frame_info *frame,
set_hsr_tag_LSDU_size(&hsr_ethhdr->hsr_tag, lsdu_size);
hsr_ethhdr->hsr_tag.sequence_nr = htons(frame->sequence_nr);
hsr_ethhdr->hsr_tag.encap_proto = hsr_ethhdr->ethhdr.h_proto;
hsr_ethhdr->ethhdr.h_proto = htons(ETH_P_PRP);
hsr_ethhdr->ethhdr.h_proto = htons(protoVersion ?
ETH_P_HSR : ETH_P_PRP);
}

static struct sk_buff *create_tagged_skb(struct sk_buff *skb_o,
Expand Down Expand Up @@ -160,7 +180,7 @@ static struct sk_buff *create_tagged_skb(struct sk_buff *skb_o,
memmove(dst, src, movelen);
skb_reset_mac_header(skb);

hsr_fill_tag(skb, frame, port);
hsr_fill_tag(skb, frame, port, port->hsr->protVersion);

return skb;
}
Expand Down Expand Up @@ -320,7 +340,8 @@ static int hsr_fill_frame_info(struct hsr_frame_info *frame,
/* FIXME: */
WARN_ONCE(1, "HSR: VLAN not yet supported");
}
if (ethhdr->h_proto == htons(ETH_P_PRP)) {
if (ethhdr->h_proto == htons(ETH_P_PRP)
|| ethhdr->h_proto == htons(ETH_P_HSR)) {
frame->skb_std = NULL;
frame->skb_hsr = skb;
frame->sequence_nr = hsr_get_skb_sequence_nr(skb);
Expand Down
30 changes: 19 additions & 11 deletions net/hsr/hsr_framereg.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,17 +177,17 @@ struct hsr_node *hsr_get_node(struct list_head *node_db, struct sk_buff *skb,
return node;
}

if (!is_sup)
return NULL; /* Only supervision frame may create node entry */
/* Everyone may create a node entry, connected node to a HSR device. */

if (ethhdr->h_proto == htons(ETH_P_PRP)) {
if (ethhdr->h_proto == htons(ETH_P_PRP)
|| ethhdr->h_proto == htons(ETH_P_HSR)) {
/* Use the existing sequence_nr from the tag as starting point
* for filtering duplicate frames.
*/
seq_out = hsr_get_skb_sequence_nr(skb) - 1;
} else {
WARN_ONCE(1, "%s: Non-HSR frame\n", __func__);
seq_out = 0;
seq_out = HSR_SEQNR_START;
}

return hsr_add_node(node_db, ethhdr->h_source, seq_out);
Expand All @@ -200,17 +200,25 @@ struct hsr_node *hsr_get_node(struct list_head *node_db, struct sk_buff *skb,
void hsr_handle_sup_frame(struct sk_buff *skb, struct hsr_node *node_curr,
struct hsr_port *port_rcv)
{
struct ethhdr *ethhdr;
struct hsr_node *node_real;
struct hsr_sup_payload *hsr_sp;
struct list_head *node_db;
int i;

skb_pull(skb, sizeof(struct hsr_ethhdr_sp));
hsr_sp = (struct hsr_sup_payload *) skb->data;
ethhdr = (struct ethhdr *) skb_mac_header(skb);

if (ether_addr_equal(eth_hdr(skb)->h_source, hsr_sp->MacAddressA))
/* Not sent from MacAddressB of a PICS_SUBS capable node */
goto done;
/* Leave the ethernet header. */
skb_pull(skb, sizeof(struct ethhdr));

/* And leave the HSR tag. */
if (ethhdr->h_proto == htons(ETH_P_HSR))
skb_pull(skb, sizeof(struct hsr_tag));

/* And leave the HSR sup tag. */
skb_pull(skb, sizeof(struct hsr_sup_tag));

hsr_sp = (struct hsr_sup_payload *) skb->data;

/* Merge node_curr (registered on MacAddressB) into node_real */
node_db = &port_rcv->hsr->node_db;
Expand All @@ -225,7 +233,7 @@ void hsr_handle_sup_frame(struct sk_buff *skb, struct hsr_node *node_curr,
/* Node has already been merged */
goto done;

ether_addr_copy(node_real->MacAddressB, eth_hdr(skb)->h_source);
ether_addr_copy(node_real->MacAddressB, ethhdr->h_source);
for (i = 0; i < HSR_PT_PORTS; i++) {
if (!node_curr->time_in_stale[i] &&
time_after(node_curr->time_in[i], node_real->time_in[i])) {
Expand All @@ -241,7 +249,7 @@ void hsr_handle_sup_frame(struct sk_buff *skb, struct hsr_node *node_curr,
kfree_rcu(node_curr, rcu_head);

done:
skb_push(skb, sizeof(struct hsr_ethhdr_sp));
skb_push(skb, sizeof(struct hsrv1_ethhdr_sp));
}


Expand Down
13 changes: 12 additions & 1 deletion net/hsr/hsr_main.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
*/
#define MAX_SLAVE_DIFF 3000 /* ms */
#define HSR_SEQNR_START (USHRT_MAX - 1024)
#define HSR_SUP_SEQNR_START (HSR_SEQNR_START / 2)


/* How often shall we check for broken ring and remove node entries older than
Expand Down Expand Up @@ -58,6 +59,8 @@ struct hsr_tag {

#define HSR_HLEN 6

#define HSR_V1_SUP_LSDUSIZE 52

/* The helper functions below assumes that 'path' occupies the 4 most
* significant bits of the 16-bit field shared by 'path' and 'LSDU_size' (or
* equivalently, the 4 most significant bits of HSR tag byte 14).
Expand Down Expand Up @@ -131,8 +134,14 @@ static inline void set_hsr_stag_HSR_Ver(struct hsr_sup_tag *hst, u16 HSR_Ver)
set_hsr_tag_LSDU_size((struct hsr_tag *) hst, HSR_Ver);
}

struct hsr_ethhdr_sp {
struct hsrv0_ethhdr_sp {
struct ethhdr ethhdr;
struct hsr_sup_tag hsr_sup;
} __packed;

struct hsrv1_ethhdr_sp {
struct ethhdr ethhdr;
struct hsr_tag hsr;
struct hsr_sup_tag hsr_sup;
} __packed;

Expand Down Expand Up @@ -162,6 +171,8 @@ struct hsr_priv {
struct timer_list prune_timer;
int announce_count;
u16 sequence_nr;
u16 sup_sequence_nr; /* For HSRv1 separate seq_nr for supervision */
u8 protVersion; /* Indicate if HSRv0 or HSRv1. */
spinlock_t seqnr_lock; /* locking for sequence_nr */
unsigned char sup_multicast_addr[ETH_ALEN];
};
Expand Down
Loading

0 comments on commit ee1c279

Please sign in to comment.