Commit c51e297a authored by Daniel Borkmann's avatar Daniel Borkmann Committed by Luis Henriques

packet: only allow extra vlan len on ethernet devices

commit 3c70c132 upstream.

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>
Signed-off-by: default avatarLuis Henriques <luis.henriques@canonical.com>
parent 48d4a63a
...@@ -1517,6 +1517,20 @@ static void fanout_release(struct sock *sk) ...@@ -1517,6 +1517,20 @@ static void fanout_release(struct sock *sk)
mutex_unlock(&fanout_mutex); mutex_unlock(&fanout_mutex);
} }
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;
static const struct proto_ops packet_ops_spkt; static const struct proto_ops packet_ops_spkt;
...@@ -1678,19 +1692,11 @@ static int packet_sendmsg_spkt(struct kiocb *iocb, struct socket *sock, ...@@ -1678,19 +1692,11 @@ static int packet_sendmsg_spkt(struct kiocb *iocb, struct socket *sock,
goto retry; goto retry;
} }
if (len > (dev->mtu + dev->hard_header_len + extra_len)) { if (len > (dev->mtu + dev->hard_header_len + extra_len) &&
/* Earlier code assumed this would be a VLAN pkt, !packet_extra_vlan_len_allowed(dev, skb)) {
* 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)) {
err = -EMSGSIZE; err = -EMSGSIZE;
goto out_unlock; goto out_unlock;
} }
}
skb->protocol = proto; skb->protocol = proto;
skb->dev = dev; skb->dev = dev;
...@@ -2286,18 +2292,10 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) ...@@ -2286,18 +2292,10 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
tp_len = tpacket_fill_skb(po, skb, ph, dev, size_max, proto, tp_len = tpacket_fill_skb(po, skb, ph, dev, size_max, proto,
addr, hlen); addr, hlen);
if (likely(tp_len >= 0) && if (likely(tp_len >= 0) &&
tp_len > dev->mtu + dev->hard_header_len) { tp_len > dev->mtu + dev->hard_header_len &&
struct ethhdr *ehdr; !packet_extra_vlan_len_allowed(dev, skb))
/* 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 = -EMSGSIZE; tp_len = -EMSGSIZE;
}
if (unlikely(tp_len < 0)) { if (unlikely(tp_len < 0)) {
if (po->tp_loss) { if (po->tp_loss) {
__packet_set_status(po, ph, __packet_set_status(po, ph,
...@@ -2509,19 +2507,11 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) ...@@ -2509,19 +2507,11 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags); sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags);
if (!gso_type && (len > dev->mtu + reserve + extra_len)) { if (!gso_type && (len > dev->mtu + reserve + extra_len) &&
/* Earlier code assumed this would be a VLAN pkt, !packet_extra_vlan_len_allowed(dev, skb)) {
* 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)) {
err = -EMSGSIZE; err = -EMSGSIZE;
goto out_free; goto out_free;
} }
}
skb->protocol = proto; skb->protocol = proto;
skb->dev = dev; skb->dev = dev;
......
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