net: hsr: fix mac_len checks

[ Upstream commit 48b491a5cc74333c4a6a82fe21cea42c055a3b0b ]

Commit 2e9f60932a2c ("net: hsr: check skb can contain struct hsr_ethhdr
in fill_frame_info") added the following which resulted in -EINVAL
always being returned:
	if (skb->mac_len < sizeof(struct hsr_ethhdr))
		return -EINVAL;

mac_len was not being set correctly so this check completely broke
HSR/PRP since it was always 14, not 20.

Set mac_len correctly and modify the mac_len checks to test in the
correct places since sometimes it is legitimately 14.

Fixes: 2e9f60932a2c ("net: hsr: check skb can contain struct hsr_ethhdr in fill_frame_info")
Signed-off-by: George McCollister <george.mccollister@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
George McCollister 2021-05-24 13:50:54 -05:00 committed by Greg Kroah-Hartman
parent a6a0af3c90
commit f6442ee08f
5 changed files with 34 additions and 21 deletions

View File

@ -218,6 +218,7 @@ static netdev_tx_t hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev)
if (master) { if (master) {
skb->dev = master->dev; skb->dev = master->dev;
skb_reset_mac_header(skb); skb_reset_mac_header(skb);
skb_reset_mac_len(skb);
hsr_forward_skb(skb, master); hsr_forward_skb(skb, master);
} else { } else {
atomic_long_inc(&dev->tx_dropped); atomic_long_inc(&dev->tx_dropped);
@ -261,6 +262,7 @@ static struct sk_buff *hsr_init_skb(struct hsr_port *master, u16 proto)
goto out; goto out;
skb_reset_mac_header(skb); skb_reset_mac_header(skb);
skb_reset_mac_len(skb);
skb_reset_network_header(skb); skb_reset_network_header(skb);
skb_reset_transport_header(skb); skb_reset_transport_header(skb);

View File

@ -451,25 +451,31 @@ static void handle_std_frame(struct sk_buff *skb,
} }
} }
void hsr_fill_frame_info(__be16 proto, struct sk_buff *skb, int hsr_fill_frame_info(__be16 proto, struct sk_buff *skb,
struct hsr_frame_info *frame) struct hsr_frame_info *frame)
{ {
if (proto == htons(ETH_P_PRP) || if (proto == htons(ETH_P_PRP) ||
proto == htons(ETH_P_HSR)) { proto == htons(ETH_P_HSR)) {
/* Check if skb contains hsr_ethhdr */
if (skb->mac_len < sizeof(struct hsr_ethhdr))
return -EINVAL;
/* HSR tagged frame :- Data or Supervision */ /* HSR tagged frame :- Data or Supervision */
frame->skb_std = NULL; frame->skb_std = NULL;
frame->skb_prp = NULL; frame->skb_prp = NULL;
frame->skb_hsr = skb; frame->skb_hsr = skb;
frame->sequence_nr = hsr_get_skb_sequence_nr(skb); frame->sequence_nr = hsr_get_skb_sequence_nr(skb);
return; return 0;
} }
/* Standard frame or PRP from master port */ /* Standard frame or PRP from master port */
handle_std_frame(skb, frame); handle_std_frame(skb, frame);
return 0;
} }
void prp_fill_frame_info(__be16 proto, struct sk_buff *skb, int prp_fill_frame_info(__be16 proto, struct sk_buff *skb,
struct hsr_frame_info *frame) struct hsr_frame_info *frame)
{ {
/* Supervision frame */ /* Supervision frame */
struct prp_rct *rct = skb_get_PRP_rct(skb); struct prp_rct *rct = skb_get_PRP_rct(skb);
@ -480,9 +486,11 @@ void prp_fill_frame_info(__be16 proto, struct sk_buff *skb,
frame->skb_std = NULL; frame->skb_std = NULL;
frame->skb_prp = skb; frame->skb_prp = skb;
frame->sequence_nr = prp_get_skb_sequence_nr(rct); frame->sequence_nr = prp_get_skb_sequence_nr(rct);
return; return 0;
} }
handle_std_frame(skb, frame); handle_std_frame(skb, frame);
return 0;
} }
static int fill_frame_info(struct hsr_frame_info *frame, static int fill_frame_info(struct hsr_frame_info *frame,
@ -492,9 +500,10 @@ static int fill_frame_info(struct hsr_frame_info *frame,
struct hsr_vlan_ethhdr *vlan_hdr; struct hsr_vlan_ethhdr *vlan_hdr;
struct ethhdr *ethhdr; struct ethhdr *ethhdr;
__be16 proto; __be16 proto;
int ret;
/* Check if skb contains hsr_ethhdr */ /* Check if skb contains ethhdr */
if (skb->mac_len < sizeof(struct hsr_ethhdr)) if (skb->mac_len < sizeof(struct ethhdr))
return -EINVAL; return -EINVAL;
memset(frame, 0, sizeof(*frame)); memset(frame, 0, sizeof(*frame));
@ -521,7 +530,10 @@ static int fill_frame_info(struct hsr_frame_info *frame,
frame->is_from_san = false; frame->is_from_san = false;
frame->port_rcv = port; frame->port_rcv = port;
hsr->proto_ops->fill_frame_info(proto, skb, frame); ret = hsr->proto_ops->fill_frame_info(proto, skb, frame);
if (ret)
return ret;
check_local_dest(port->hsr, skb, frame); check_local_dest(port->hsr, skb, frame);
return 0; return 0;

View File

@ -23,8 +23,8 @@ struct sk_buff *hsr_get_untagged_frame(struct hsr_frame_info *frame,
struct sk_buff *prp_get_untagged_frame(struct hsr_frame_info *frame, struct sk_buff *prp_get_untagged_frame(struct hsr_frame_info *frame,
struct hsr_port *port); struct hsr_port *port);
bool prp_drop_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, int prp_fill_frame_info(__be16 proto, struct sk_buff *skb,
struct hsr_frame_info *frame); struct hsr_frame_info *frame);
void hsr_fill_frame_info(__be16 proto, struct sk_buff *skb, int hsr_fill_frame_info(__be16 proto, struct sk_buff *skb,
struct hsr_frame_info *frame); struct hsr_frame_info *frame);
#endif /* __HSR_FORWARD_H */ #endif /* __HSR_FORWARD_H */

View File

@ -192,8 +192,8 @@ struct hsr_proto_ops {
struct hsr_port *port); struct hsr_port *port);
struct sk_buff * (*create_tagged_frame)(struct hsr_frame_info *frame, struct sk_buff * (*create_tagged_frame)(struct hsr_frame_info *frame,
struct hsr_port *port); struct hsr_port *port);
void (*fill_frame_info)(__be16 proto, struct sk_buff *skb, int (*fill_frame_info)(__be16 proto, struct sk_buff *skb,
struct hsr_frame_info *frame); struct hsr_frame_info *frame);
bool (*invalid_dan_ingress_frame)(__be16 protocol); bool (*invalid_dan_ingress_frame)(__be16 protocol);
void (*update_san_info)(struct hsr_node *node, bool is_sup); void (*update_san_info)(struct hsr_node *node, bool is_sup);
}; };

View File

@ -58,12 +58,11 @@ static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)
goto finish_pass; goto finish_pass;
skb_push(skb, ETH_HLEN); skb_push(skb, ETH_HLEN);
skb_reset_mac_header(skb);
if (skb_mac_header(skb) != skb->data) { if ((!hsr->prot_version && protocol == htons(ETH_P_PRP)) ||
WARN_ONCE(1, "%s:%d: Malformed frame at source port %s)\n", protocol == htons(ETH_P_HSR))
__func__, __LINE__, port->dev->name); skb_set_network_header(skb, ETH_HLEN + HSR_HLEN);
goto finish_consume; skb_reset_mac_len(skb);
}
hsr_forward_skb(skb, port); hsr_forward_skb(skb, port);