Commit 451d8123 authored by Murali Karicheri's avatar Murali Karicheri Committed by David S. Miller

net: prp: add packet handling support

DAN-P (Dual Attached Nodes PRP) nodes are expected to receive
traditional IP packets as well as PRP (Parallel Redundancy
Protocol) tagged (trailer) packets. PRP trailer is 6 bytes
of PRP protocol unit called RCT, Redundancy Control Trailer
(RCT) similar to HSR tag. PRP network can have traditional
devices such as bridges/switches or PC attached to it and
should be able to communicate. Regular Ethernet devices treat
the RCT as pads.  This patch adds logic to format L2 frames
from network stack to add a trailer (RCT) and send it as
duplicates over the slave interfaces when the protocol is
PRP as per IEC 62439-3. At the ingress, it strips the trailer,
do duplicate detection and rejection and forward a stripped
frame up the network stack. PRP device should accept frames
from Singly Attached Nodes (SAN) and thus the driver mark
the link where the frame came from in the node table.
Signed-off-by: default avatarMurali Karicheri <m-karicheri2@ti.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent fa4dc895
......@@ -443,10 +443,17 @@ static struct hsr_proto_ops hsr_ops = {
.create_tagged_frame = hsr_create_tagged_frame,
.get_untagged_frame = hsr_get_untagged_frame,
.fill_frame_info = hsr_fill_frame_info,
.invalid_dan_ingress_frame = hsr_invalid_dan_ingress_frame,
};
static struct hsr_proto_ops prp_ops = {
.send_sv_frame = send_prp_supervision_frame,
.create_tagged_frame = prp_create_tagged_frame,
.get_untagged_frame = prp_get_untagged_frame,
.drop_frame = prp_drop_frame,
.fill_frame_info = prp_fill_frame_info,
.handle_san_frame = prp_handle_san_frame,
.update_san_info = prp_update_san_info,
};
void hsr_dev_setup(struct net_device *dev)
......@@ -508,15 +515,16 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
ether_addr_copy(hsr_dev->dev_addr, slave[0]->dev_addr);
/* currently PRP is not supported */
if (protocol_version == PRP_V1)
return -EPROTONOSUPPORT;
/* initialize protocol specific functions */
if (protocol_version == PRP_V1)
if (protocol_version == PRP_V1) {
/* For PRP, lan_id has most significant 3 bits holding
* the net_id of PRP_LAN_ID
*/
hsr->net_id = PRP_LAN_ID << 1;
hsr->proto_ops = &prp_ops;
else
} else {
hsr->proto_ops = &hsr_ops;
}
/* Make sure we recognize frames from ourselves in hsr_rcv() */
res = hsr_create_self_node(hsr, hsr_dev->dev_addr,
......
This diff is collapsed.
......@@ -14,10 +14,17 @@
#include "hsr_main.h"
void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port);
struct sk_buff *prp_create_tagged_frame(struct hsr_frame_info *frame,
struct hsr_port *port);
struct sk_buff *hsr_create_tagged_frame(struct hsr_frame_info *frame,
struct hsr_port *port);
struct sk_buff *hsr_get_untagged_frame(struct hsr_frame_info *frame,
struct hsr_port *port);
struct sk_buff *prp_get_untagged_frame(struct hsr_frame_info *frame,
struct hsr_port *port);
bool prp_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port);
void prp_fill_frame_info(__be16 proto, struct sk_buff *skb,
struct hsr_frame_info *frame);
void hsr_fill_frame_info(__be16 proto, struct sk_buff *skb,
struct hsr_frame_info *frame);
#endif /* __HSR_FORWARD_H */
......@@ -127,6 +127,19 @@ void hsr_del_nodes(struct list_head *node_db)
kfree(node);
}
void prp_handle_san_frame(bool san, enum hsr_port_type port,
struct hsr_node *node)
{
/* Mark if the SAN node is over LAN_A or LAN_B */
if (port == HSR_PT_SLAVE_A) {
node->san_a = true;
return;
}
if (port == HSR_PT_SLAVE_B)
node->san_b = true;
}
/* Allocate an hsr_node and add it to node_db. 'addr' is the node's address_A;
* seq_out is used to initialize filtering of outgoing duplicate frames
* originating from the newly added node.
......@@ -134,7 +147,8 @@ void hsr_del_nodes(struct list_head *node_db)
static struct hsr_node *hsr_add_node(struct hsr_priv *hsr,
struct list_head *node_db,
unsigned char addr[],
u16 seq_out)
u16 seq_out, bool san,
enum hsr_port_type rx_port)
{
struct hsr_node *new_node, *node;
unsigned long now;
......@@ -155,6 +169,9 @@ static struct hsr_node *hsr_add_node(struct hsr_priv *hsr,
for (i = 0; i < HSR_PT_PORTS; i++)
new_node->seq_out[i] = seq_out;
if (san && hsr->proto_ops->handle_san_frame)
hsr->proto_ops->handle_san_frame(san, rx_port, new_node);
spin_lock_bh(&hsr->list_lock);
list_for_each_entry_rcu(node, node_db, mac_list,
lockdep_is_held(&hsr->list_lock)) {
......@@ -172,15 +189,26 @@ static struct hsr_node *hsr_add_node(struct hsr_priv *hsr,
return node;
}
void prp_update_san_info(struct hsr_node *node, bool is_sup)
{
if (!is_sup)
return;
node->san_a = false;
node->san_b = false;
}
/* Get the hsr_node from which 'skb' was sent.
*/
struct hsr_node *hsr_get_node(struct hsr_port *port, struct sk_buff *skb,
bool is_sup)
struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db,
struct sk_buff *skb, bool is_sup,
enum hsr_port_type rx_port)
{
struct list_head *node_db = &port->hsr->node_db;
struct hsr_priv *hsr = port->hsr;
struct hsr_node *node;
struct ethhdr *ethhdr;
struct prp_rct *rct;
bool san = false;
u16 seq_out;
if (!skb_mac_header_was_set(skb))
......@@ -189,14 +217,21 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct sk_buff *skb,
ethhdr = (struct ethhdr *)skb_mac_header(skb);
list_for_each_entry_rcu(node, node_db, mac_list) {
if (ether_addr_equal(node->macaddress_A, ethhdr->h_source))
if (ether_addr_equal(node->macaddress_A, ethhdr->h_source)) {
if (hsr->proto_ops->update_san_info)
hsr->proto_ops->update_san_info(node, is_sup);
return node;
if (ether_addr_equal(node->macaddress_B, ethhdr->h_source))
}
if (ether_addr_equal(node->macaddress_B, ethhdr->h_source)) {
if (hsr->proto_ops->update_san_info)
hsr->proto_ops->update_san_info(node, is_sup);
return node;
}
}
/* Everyone may create a node entry, connected node to a HSR device. */
/* Everyone may create a node entry, connected node to a HSR/PRP
* device.
*/
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
......@@ -204,31 +239,47 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct sk_buff *skb,
*/
seq_out = hsr_get_skb_sequence_nr(skb) - 1;
} else {
/* this is called also for frames from master port and
* so warn only for non master ports
*/
if (port->type != HSR_PT_MASTER)
WARN_ONCE(1, "%s: Non-HSR frame\n", __func__);
seq_out = HSR_SEQNR_START;
rct = skb_get_PRP_rct(skb);
if (rct && prp_check_lsdu_size(skb, rct, is_sup)) {
seq_out = prp_get_skb_sequence_nr(rct);
} else {
if (rx_port != HSR_PT_MASTER)
san = true;
seq_out = HSR_SEQNR_START;
}
}
return hsr_add_node(hsr, node_db, ethhdr->h_source, seq_out);
return hsr_add_node(hsr, node_db, ethhdr->h_source, seq_out,
san, rx_port);
}
/* Use the Supervision frame's info about an eventual macaddress_B for merging
* nodes that has previously had their macaddress_B registered as a separate
* node.
*/
void hsr_handle_sup_frame(struct sk_buff *skb, struct hsr_node *node_curr,
struct hsr_port *port_rcv)
void hsr_handle_sup_frame(struct hsr_frame_info *frame)
{
struct hsr_node *node_curr = frame->node_src;
struct hsr_port *port_rcv = frame->port_rcv;
struct hsr_priv *hsr = port_rcv->hsr;
struct hsr_sup_payload *hsr_sp;
struct hsr_node *node_real;
struct sk_buff *skb = NULL;
struct list_head *node_db;
struct ethhdr *ethhdr;
int i;
/* Here either frame->skb_hsr or frame->skb_prp should be
* valid as supervision frame always will have protocol
* header info.
*/
if (frame->skb_hsr)
skb = frame->skb_hsr;
else if (frame->skb_prp)
skb = frame->skb_prp;
if (!skb)
return;
ethhdr = (struct ethhdr *)skb_mac_header(skb);
/* Leave the ethernet header. */
......@@ -249,7 +300,8 @@ void hsr_handle_sup_frame(struct sk_buff *skb, struct hsr_node *node_curr,
if (!node_real)
/* No frame received from AddrA of this node yet */
node_real = hsr_add_node(hsr, node_db, hsr_sp->macaddress_A,
HSR_SEQNR_START - 1);
HSR_SEQNR_START - 1, true,
port_rcv->type);
if (!node_real)
goto done; /* No mem */
if (node_real == node_curr)
......@@ -275,7 +327,11 @@ 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 hsrv1_ethhdr_sp));
/* PRP uses v0 header */
if (ethhdr->h_proto == htons(ETH_P_HSR))
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.
......
......@@ -14,12 +14,26 @@
struct hsr_node;
struct hsr_frame_info {
struct sk_buff *skb_std;
struct sk_buff *skb_hsr;
struct sk_buff *skb_prp;
struct hsr_port *port_rcv;
struct hsr_node *node_src;
u16 sequence_nr;
bool is_supervision;
bool is_vlan;
bool is_local_dest;
bool is_local_exclusive;
bool is_from_san;
};
void hsr_del_self_node(struct hsr_priv *hsr);
void hsr_del_nodes(struct list_head *node_db);
struct hsr_node *hsr_get_node(struct hsr_port *port, struct sk_buff *skb,
bool is_sup);
void hsr_handle_sup_frame(struct sk_buff *skb, struct hsr_node *node_curr,
struct hsr_port *port);
struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db,
struct sk_buff *skb, bool is_sup,
enum hsr_port_type rx_port);
void hsr_handle_sup_frame(struct hsr_frame_info *frame);
bool hsr_addr_is_self(struct hsr_priv *hsr, unsigned char *addr);
void hsr_addr_subst_source(struct hsr_node *node, struct sk_buff *skb);
......@@ -49,6 +63,10 @@ int hsr_get_node_data(struct hsr_priv *hsr,
int *if2_age,
u16 *if2_seq);
void prp_handle_san_frame(bool san, enum hsr_port_type port,
struct hsr_node *node);
void prp_update_san_info(struct hsr_node *node, bool is_sup);
struct hsr_node {
struct list_head mac_list;
unsigned char macaddress_A[ETH_ALEN];
......@@ -57,6 +75,9 @@ struct hsr_node {
enum hsr_port_type addr_B_port;
unsigned long time_in[HSR_PT_PORTS];
bool time_in_stale[HSR_PT_PORTS];
/* if the node is a SAN */
bool san_a;
bool san_b;
u16 seq_out[HSR_PT_PORTS];
struct rcu_head rcu_head;
};
......
......@@ -12,6 +12,7 @@
#include <linux/netdevice.h>
#include <linux/list.h>
#include <linux/if_vlan.h>
/* Time constants as specified in the HSR specification (IEC-62439-3 2010)
* Table 8.
......@@ -86,7 +87,12 @@ struct hsr_ethhdr {
struct hsr_tag hsr_tag;
} __packed;
/* HSR Supervision Frame data types.
struct hsr_vlan_ethhdr {
struct vlan_ethhdr vlanhdr;
struct hsr_tag hsr_tag;
} __packed;
/* HSR/PRP Supervision Frame data types.
* Field names as defined in the IEC:2010 standard for HSR.
*/
struct hsr_sup_tag {
......@@ -142,6 +148,16 @@ struct prp_rct {
__be16 PRP_suffix;
} __packed;
static inline u16 get_prp_LSDU_size(struct prp_rct *rct)
{
return ntohs(rct->lan_id_and_LSDU_size) & 0x0FFF;
}
static inline void set_prp_lan_id(struct prp_rct *rct, u16 lan_id)
{
rct->lan_id_and_LSDU_size = htons((ntohs(rct->lan_id_and_LSDU_size) &
0x0FFF) | (lan_id << 12));
}
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) &
......@@ -163,16 +179,22 @@ enum hsr_version {
};
struct hsr_frame_info;
struct hsr_node;
struct hsr_proto_ops {
/* format and send supervision frame */
void (*send_sv_frame)(struct hsr_port *port, unsigned long *interval);
void (*handle_san_frame)(bool san, enum hsr_port_type port,
struct hsr_node *node);
bool (*drop_frame)(struct hsr_frame_info *frame, struct hsr_port *port);
struct sk_buff * (*get_untagged_frame)(struct hsr_frame_info *frame,
struct hsr_port *port);
struct sk_buff * (*create_tagged_frame)(struct hsr_frame_info *frame,
struct hsr_port *port);
void (*fill_frame_info)(__be16 proto, struct sk_buff *skb,
struct hsr_frame_info *frame);
bool (*invalid_dan_ingress_frame)(__be16 protocol);
void (*update_san_info)(struct hsr_node *node, bool is_sup);
};
struct hsr_priv {
......@@ -189,6 +211,12 @@ struct hsr_priv {
spinlock_t seqnr_lock; /* locking for sequence_nr */
spinlock_t list_lock; /* locking for node list */
struct hsr_proto_ops *proto_ops;
#define PRP_LAN_ID 0x5 /* 0x1010 for A and 0x1011 for B. Bit 0 is set
* based on SLAVE_A or SLAVE_B
*/
u8 net_id; /* for PRP, it occupies most significant 3 bits
* of lan_id
*/
unsigned char sup_multicast_addr[ETH_ALEN];
#ifdef CONFIG_DEBUG_FS
struct dentry *node_tbl_root;
......@@ -209,6 +237,49 @@ static inline u16 hsr_get_skb_sequence_nr(struct sk_buff *skb)
return ntohs(hsr_ethhdr->hsr_tag.sequence_nr);
}
static inline struct prp_rct *skb_get_PRP_rct(struct sk_buff *skb)
{
unsigned char *tail = skb_tail_pointer(skb) - HSR_HLEN;
struct prp_rct *rct = (struct prp_rct *)tail;
if (rct->PRP_suffix == htons(ETH_P_PRP))
return rct;
return NULL;
}
/* Assume caller has confirmed this skb is PRP suffixed */
static inline u16 prp_get_skb_sequence_nr(struct prp_rct *rct)
{
return ntohs(rct->sequence_nr);
}
static inline u16 get_prp_lan_id(struct prp_rct *rct)
{
return ntohs(rct->lan_id_and_LSDU_size) >> 12;
}
/* assume there is a valid rct */
static inline bool prp_check_lsdu_size(struct sk_buff *skb,
struct prp_rct *rct,
bool is_sup)
{
struct ethhdr *ethhdr;
int expected_lsdu_size;
if (is_sup) {
expected_lsdu_size = HSR_V1_SUP_LSDUSIZE;
} else {
ethhdr = (struct ethhdr *)skb_mac_header(skb);
expected_lsdu_size = skb->len - 14;
if (ethhdr->h_proto == htons(ETH_P_8021Q))
expected_lsdu_size -= 4;
}
return (expected_lsdu_size == get_prp_LSDU_size(rct));
}
#if IS_ENABLED(CONFIG_DEBUG_FS)
void hsr_debugfs_rename(struct net_device *dev);
void hsr_debugfs_init(struct hsr_priv *priv, struct net_device *hsr_dev);
......
......@@ -16,12 +16,22 @@
#include "hsr_forward.h"
#include "hsr_framereg.h"
bool hsr_invalid_dan_ingress_frame(__be16 protocol)
{
return (protocol != htons(ETH_P_PRP) && protocol != htons(ETH_P_HSR));
}
static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)
{
struct sk_buff *skb = *pskb;
struct hsr_port *port;
struct hsr_priv *hsr;
__be16 protocol;
/* Packets from dev_loopback_xmit() do not have L2 header, bail out */
if (unlikely(skb->pkt_type == PACKET_LOOPBACK))
return RX_HANDLER_PASS;
if (!skb_mac_header_was_set(skb)) {
WARN_ONCE(1, "%s: skb invalid", __func__);
return RX_HANDLER_PASS;
......@@ -30,6 +40,7 @@ static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)
port = hsr_port_get_rcu(skb->dev);
if (!port)
goto finish_pass;
hsr = port->hsr;
if (hsr_addr_is_self(port->hsr, eth_hdr(skb)->h_source)) {
/* Directly kill frames sent by ourselves */
......@@ -37,12 +48,23 @@ static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)
goto finish_consume;
}
/* For HSR, only tagged frames are expected, but for PRP
* there could be non tagged frames as well from Single
* attached nodes (SANs).
*/
protocol = eth_hdr(skb)->h_proto;
if (protocol != htons(ETH_P_PRP) && protocol != htons(ETH_P_HSR))
if (hsr->proto_ops->invalid_dan_ingress_frame &&
hsr->proto_ops->invalid_dan_ingress_frame(protocol))
goto finish_pass;
skb_push(skb, ETH_HLEN);
if (skb_mac_header(skb) != skb->data) {
WARN_ONCE(1, "%s:%d: Malformed frame at source port %s)\n",
__func__, __LINE__, port->dev->name);
goto finish_consume;
}
hsr_forward_skb(skb, port);
finish_consume:
......
......@@ -32,4 +32,6 @@ static inline struct hsr_port *hsr_port_get_rcu(const struct net_device *dev)
rcu_dereference(dev->rx_handler_data) : NULL;
}
bool hsr_invalid_dan_ingress_frame(__be16 protocol);
#endif /* __HSR_SLAVE_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment