Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3c70c132 authored by Daniel Borkmann's avatar Daniel Borkmann Committed by David S. Miller
Browse files

packet: only allow extra vlan len on ethernet devices



Packet sockets can be used by various net devices and are not
really restricted to ARPHRD_ETHER device types. However, when
currently checking for the extra 4 bytes that can be transmitted
in VLAN case, our assumption is that we generally probe on
ARPHRD_ETHER devices. Therefore, before looking into Ethernet
header, check the device type first.

This also fixes the issue where non-ARPHRD_ETHER devices could
have no dev->hard_header_len in TX_RING SOCK_RAW case, and thus
the check would test unfilled linear part of the skb (instead
of non-linear).

Fixes: 57f89bfa ("network: Allow af_packet to transmit +4 bytes for VLAN packets.")
Fixes: 52f1454f ("packet: allow to transmit +4 byte in TX_RING slot for VLAN case")
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Acked-by: default avatarWillem de Bruijn <willemb@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 8fd6c80d
Loading
Loading
Loading
Loading
+25 −35
Original line number Diff line number Diff line
@@ -1741,6 +1741,20 @@ static void fanout_release(struct sock *sk)
		kfree_rcu(po->rollover, rcu);
}

static bool packet_extra_vlan_len_allowed(const struct net_device *dev,
					  struct sk_buff *skb)
{
	/* Earlier code assumed this would be a VLAN pkt, double-check
	 * this now that we have the actual packet in hand. We can only
	 * do this check on Ethernet devices.
	 */
	if (unlikely(dev->type != ARPHRD_ETHER))
		return false;

	skb_reset_mac_header(skb);
	return likely(eth_hdr(skb)->h_proto == htons(ETH_P_8021Q));
}

static const struct proto_ops packet_ops;

static const struct proto_ops packet_ops_spkt;
@@ -1902,19 +1916,11 @@ retry:
		goto retry;
	}

	if (len > (dev->mtu + dev->hard_header_len + extra_len)) {
		/* Earlier code assumed this would be a VLAN pkt,
		 * double-check this now that we have the actual
		 * packet in hand.
		 */
		struct ethhdr *ehdr;
		skb_reset_mac_header(skb);
		ehdr = eth_hdr(skb);
		if (ehdr->h_proto != htons(ETH_P_8021Q)) {
	if (len > (dev->mtu + dev->hard_header_len + extra_len) &&
	    !packet_extra_vlan_len_allowed(dev, skb)) {
		err = -EMSGSIZE;
		goto out_unlock;
	}
	}

	skb->protocol = proto;
	skb->dev = dev;
@@ -2525,18 +2531,10 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
		tp_len = tpacket_fill_skb(po, skb, ph, dev, size_max, proto,
					  addr, hlen);
		if (likely(tp_len >= 0) &&
		    tp_len > dev->mtu + dev->hard_header_len) {
			struct ethhdr *ehdr;
			/* Earlier code assumed this would be a VLAN pkt,
			 * double-check this now that we have the actual
			 * packet in hand.
			 */

			skb_reset_mac_header(skb);
			ehdr = eth_hdr(skb);
			if (ehdr->h_proto != htons(ETH_P_8021Q))
		    tp_len > dev->mtu + dev->hard_header_len &&
		    !packet_extra_vlan_len_allowed(dev, skb))
			tp_len = -EMSGSIZE;
		}

		if (unlikely(tp_len < 0)) {
			if (po->tp_loss) {
				__packet_set_status(po, ph,
@@ -2765,19 +2763,11 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)

	sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags);

	if (!gso_type && (len > dev->mtu + reserve + extra_len)) {
		/* Earlier code assumed this would be a VLAN pkt,
		 * double-check this now that we have the actual
		 * packet in hand.
		 */
		struct ethhdr *ehdr;
		skb_reset_mac_header(skb);
		ehdr = eth_hdr(skb);
		if (ehdr->h_proto != htons(ETH_P_8021Q)) {
	if (!gso_type && (len > dev->mtu + reserve + extra_len) &&
	    !packet_extra_vlan_len_allowed(dev, skb)) {
		err = -EMSGSIZE;
		goto out_free;
	}
	}

	skb->protocol = proto;
	skb->dev = dev;