diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c
index f49d962337f8b..d2a11d4560309 100644
--- a/net/hsr/hsr_forward.c
+++ b/net/hsr/hsr_forward.c
@@ -426,7 +426,7 @@ static void hsr_deliver_master(struct sk_buff *skb, struct net_device *dev,
static int hsr_xmit(struct sk_buff *skb, struct hsr_port *port,
struct hsr_frame_info *frame)
{
- if (frame->port_rcv->type == HSR_PT_MASTER) {
+ if (frame->port_rcv->type == HSR_PT_MASTER && !frame->has_hsr_header) {
hsr_addr_subst_dest(frame->node_src, skb, port);
/* Address substitution (IEC62439-3 pp 26, 50): replace mac@@ -552,6 +552,7 @@ static void hsr_forward_do(struct hsr_frame_info *frame)
hsr_for_each_port(frame->port_rcv->hsr, port) {
struct hsr_priv *hsr = port->hsr;
+
/* Don't send frame back the way it came */
if (port == frame->port_rcv)
continue;@@ -570,6 +571,19 @@ static void hsr_forward_do(struct hsr_frame_info *frame)
if ((port->dev->features & NETIF_F_HW_HSR_DUP) && sent)
continue;
+ /* If this frame has a specific outgoing port specified (by the
+ * sender) then send it on this port only.
+ */
+ if (frame->req_tx_port != HSR_PT_NONE && frame->req_tx_port != port->type)
+ continue;
+ /* If this TX frame already has a HSR header then forward the
+ * frame with it and don't add one.
+ */
+ if (frame->has_hsr_header && frame->skb_std) {
+ skb = skb_clone(frame->skb_std, GFP_ATOMIC);
+ goto inject_into_stack;
+ }
+
/* Don't send frame over port where it has been sent before.
* Also for SAN, this shouldn't be done.
*/@@ -597,6 +611,7 @@ static void hsr_forward_do(struct hsr_frame_info *frame)
else
skb = hsr->proto_ops->get_untagged_frame(frame, port);
+inject_into_stack:
if (!skb) {
frame->port_rcv->dev->stats.rx_dropped++;
continue;@@ -661,6 +676,13 @@ int hsr_fill_frame_info(__be16 proto, struct sk_buff *skb,
struct hsr_port *port = frame->port_rcv;
struct hsr_priv *hsr = port->hsr;
+ if (frame->has_hsr_header) {
+ frame->skb_std = skb;
+
+ WARN_ON_ONCE(port->type != HSR_PT_MASTER);
+ WARN_ON_ONCE(skb->mac_len < sizeof(struct hsr_ethhdr));
+ return 0;
+ }
/* HSRv0 supervisory frames double as a tag so treat them as tagged. */
if ((!hsr->prot_version && proto == htons(ETH_P_PRP)) ||
proto == htons(ETH_P_HSR)) {@@ -702,7 +724,8 @@ int prp_fill_frame_info(__be16 proto, struct sk_buff *skb,
}
static int fill_frame_info(struct hsr_frame_info *frame,
- struct sk_buff *skb, struct hsr_port *port)
+ struct sk_buff *skb, struct hsr_port *port,
+ enum hsr_port_type req_tx_port, bool has_hsr_header)
{
struct hsr_priv *hsr = port->hsr;
struct hsr_vlan_ethhdr *vlan_hdr;@@ -725,10 +748,15 @@ static int fill_frame_info(struct hsr_frame_info *frame,
if (port->type == HSR_PT_INTERLINK)
n_db = &hsr->proxy_node_db;
- frame->node_src = hsr_get_node(port, n_db, skb,
- frame->is_supervision, port->type);
- if (!frame->node_src)
- return -1; /* Unknown node and !is_supervision, or no mem */
+ frame->req_tx_port = req_tx_port;
+ frame->has_hsr_header = has_hsr_header;
+
+ if (!frame->has_hsr_header) {
+ frame->node_src = hsr_get_node(port, n_db, skb,
+ frame->is_supervision, port->type);
+ if (!frame->node_src)
+ return -1; /* Unknown node and !is_supervision, or no mem */
+ }
ethhdr = (struct ethhdr *)skb_mac_header(skb);
frame->is_vlan = false;@@ -777,10 +805,11 @@ void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port,
struct hsr_frame_info frame;
rcu_read_lock();
- if (fill_frame_info(&frame, skb, port) < 0)
+ if (fill_frame_info(&frame, skb, port, req_tx_port, has_hsr_header) < 0)
goto out_drop;
- hsr_register_frame_in(frame.node_src, port, frame.sequence_nr);
+ if (!frame.has_hsr_header)
+ hsr_register_frame_in(frame.node_src, port, frame.sequence_nr);
hsr_forward_do(&frame);
rcu_read_unlock();
/* Gets called for ingress frames as well as egress from master port.
diff --git a/net/hsr/hsr_framereg.h b/net/hsr/hsr_framereg.h
index 127a3fb64d5f0..dc13819d04098 100644
--- a/net/hsr/hsr_framereg.h
+++ b/net/hsr/hsr_framereg.h
@@ -29,6 +29,8 @@ struct hsr_frame_info {
bool is_from_san;
bool dst_in_node_db;
bool dst_in_proxy_node_db;
+ bool has_hsr_header;
+ enum hsr_port_type req_tx_port;
};
void hsr_del_self_node(struct hsr_priv *hsr);
--
2.55.0