net/hsr: Added support for HSR v1
authorPeter Heise <mail@pheise.de>
Wed, 13 Apr 2016 11:52:22 +0000 (13:52 +0200)
committerDavid S. Miller <davem@davemloft.net>
Fri, 15 Apr 2016 21:06:48 +0000 (17:06 -0400)
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 <peter.heise@airbus.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/uapi/linux/if_ether.h
include/uapi/linux/if_link.h
net/hsr/Kconfig
net/hsr/hsr_device.c
net/hsr/hsr_device.h
net/hsr/hsr_forward.c
net/hsr/hsr_framereg.c
net/hsr/hsr_main.h
net/hsr/hsr_netlink.c
net/hsr/hsr_slave.c

index 4a93051..cec849a 100644 (file)
@@ -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 ] */
index 9427f17..bb3a90b 100644 (file)
@@ -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,
index 0d3d709..4b683fd 100644 (file)
@@ -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!
index c7d1adc..386cbce 100644 (file)
@@ -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 +
@@ -250,31 +251,22 @@ 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;
@@ -282,32 +274,48 @@ static void send_hsr_supervision_frame(struct hsr_port *master, u8 type)
        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;
 
@@ -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);
@@ -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;
@@ -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;
@@ -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.
index 108a5d5..9975e31 100644 (file)
@@ -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);
index 7871ed6..5ee1d43 100644 (file)
@@ -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;
@@ -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;
@@ -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,
@@ -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;
 }
@@ -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);
index bace124..7ea9258 100644 (file)
@@ -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);
@@ -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;
@@ -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])) {
@@ -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));
 }
 
 
index 5a9c699..9b9909e 100644 (file)
@@ -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
@@ -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).
@@ -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;
 
@@ -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];
 };
index a2c7e4c..5425d87 100644 (file)
@@ -23,6 +23,7 @@ static const struct nla_policy hsr_policy[IFLA_HSR_MAX + 1] = {
        [IFLA_HSR_SLAVE1]               = { .type = NLA_U32 },
        [IFLA_HSR_SLAVE2]               = { .type = NLA_U32 },
        [IFLA_HSR_MULTICAST_SPEC]       = { .type = NLA_U8 },
+       [IFLA_HSR_VERSION]      = { .type = NLA_U8 },
        [IFLA_HSR_SUPERVISION_ADDR]     = { .type = NLA_BINARY, .len = ETH_ALEN },
        [IFLA_HSR_SEQ_NR]               = { .type = NLA_U16 },
 };
@@ -35,7 +36,7 @@ static int hsr_newlink(struct net *src_net, struct net_device *dev,
                       struct nlattr *tb[], struct nlattr *data[])
 {
        struct net_device *link[2];
-       unsigned char multicast_spec;
+       unsigned char multicast_spec, hsr_version;
 
        if (!data) {
                netdev_info(dev, "HSR: No slave devices specified\n");
@@ -62,7 +63,12 @@ static int hsr_newlink(struct net *src_net, struct net_device *dev,
        else
                multicast_spec = nla_get_u8(data[IFLA_HSR_MULTICAST_SPEC]);
 
-       return hsr_dev_finalize(dev, link, multicast_spec);
+       if (!data[IFLA_HSR_VERSION])
+               hsr_version = 0;
+       else
+               hsr_version = nla_get_u8(data[IFLA_HSR_VERSION]);
+
+       return hsr_dev_finalize(dev, link, multicast_spec, hsr_version);
 }
 
 static int hsr_fill_info(struct sk_buff *skb, const struct net_device *dev)
index 7d37366..f5b6038 100644 (file)
@@ -22,6 +22,7 @@ static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)
 {
        struct sk_buff *skb = *pskb;
        struct hsr_port *port;
+       u16 protocol;
 
        if (!skb_mac_header_was_set(skb)) {
                WARN_ONCE(1, "%s: skb invalid", __func__);
@@ -37,7 +38,8 @@ static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)
                goto finish_consume;
        }
 
-       if (eth_hdr(skb)->h_proto != htons(ETH_P_PRP))
+       protocol = eth_hdr(skb)->h_proto;
+       if (protocol != htons(ETH_P_PRP) && protocol != htons(ETH_P_HSR))
                goto finish_pass;
 
        skb_push(skb, ETH_HLEN);