mirror of https://gitee.com/openkylin/linux.git
net/hsr: Added support for HSR v1
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>
This commit is contained in:
parent
125c8d1233
commit
ee1c279772
|
@ -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 ] */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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,11 +134,17 @@ 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;
|
||||
|
||||
|
||||
enum hsr_port_type {
|
||||
HSR_PT_NONE = 0, /* Must be 0, used by framereg */
|
||||
|
@ -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];
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue