Commit 48b491a5 authored by George McCollister's avatar George McCollister Committed by David S. Miller
Browse files

net: hsr: fix mac_len checks



Commit 2e9f6093 ("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: 2e9f6093 ("net: hsr: check skb can contain struct hsr_ethhdr in fill_frame_info")
Signed-off-by: default avatarGeorge McCollister <george.mccollister@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a4dd4fc6
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -218,6 +218,7 @@ static netdev_tx_t hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev)
	if (master) {
		skb->dev = master->dev;
		skb_reset_mac_header(skb);
		skb_reset_mac_len(skb);
		hsr_forward_skb(skb, master);
	} else {
		atomic_long_inc(&dev->tx_dropped);
@@ -259,6 +260,7 @@ static struct sk_buff *hsr_init_skb(struct hsr_port *master)
		goto out;

	skb_reset_mac_header(skb);
	skb_reset_mac_len(skb);
	skb_reset_network_header(skb);
	skb_reset_transport_header(skb);

+21 −9
Original line number Diff line number Diff line
@@ -474,7 +474,7 @@ 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_port *port = frame->port_rcv;
@@ -483,19 +483,25 @@ void hsr_fill_frame_info(__be16 proto, struct sk_buff *skb,
	/* 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)) {
		/* Check if skb contains hsr_ethhdr */
		if (skb->mac_len < sizeof(struct hsr_ethhdr))
			return -EINVAL;

		/* HSR tagged frame :- Data or Supervision */
		frame->skb_std = NULL;
		frame->skb_prp = NULL;
		frame->skb_hsr = skb;
		frame->sequence_nr = hsr_get_skb_sequence_nr(skb);
		return;
		return 0;
	}

	/* Standard frame or PRP from master port */
	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)
{
	/* Supervision frame */
@@ -507,9 +513,11 @@ void prp_fill_frame_info(__be16 proto, struct sk_buff *skb,
		frame->skb_std = NULL;
		frame->skb_prp = skb;
		frame->sequence_nr = prp_get_skb_sequence_nr(rct);
		return;
		return 0;
	}
	handle_std_frame(skb, frame);

	return 0;
}

static int fill_frame_info(struct hsr_frame_info *frame,
@@ -519,9 +527,10 @@ static int fill_frame_info(struct hsr_frame_info *frame,
	struct hsr_vlan_ethhdr *vlan_hdr;
	struct ethhdr *ethhdr;
	__be16 proto;
	int ret;

	/* Check if skb contains hsr_ethhdr */
	if (skb->mac_len < sizeof(struct hsr_ethhdr))
	/* Check if skb contains ethhdr */
	if (skb->mac_len < sizeof(struct ethhdr))
		return -EINVAL;

	memset(frame, 0, sizeof(*frame));
@@ -548,7 +557,10 @@ static int fill_frame_info(struct hsr_frame_info *frame,

	frame->is_from_san = false;
	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);

	return 0;
+4 −4
Original line number Diff line number Diff line
@@ -24,8 +24,8 @@ 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);
bool hsr_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);
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);
#endif /* __HSR_FORWARD_H */
+2 −2
Original line number Diff line number Diff line
@@ -186,7 +186,7 @@ struct hsr_proto_ops {
					       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,
	int (*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);
+5 −6
Original line number Diff line number Diff line
@@ -60,12 +60,11 @@ static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)
		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;
	}
	skb_reset_mac_header(skb);
	if ((!hsr->prot_version && protocol == htons(ETH_P_PRP)) ||
	    protocol == htons(ETH_P_HSR))
		skb_set_network_header(skb, ETH_HLEN + HSR_HLEN);
	skb_reset_mac_len(skb);

	hsr_forward_skb(skb, port);