Skip to content

Commit

Permalink
packet: only allow extra vlan len on ethernet devices
Browse files Browse the repository at this point in the history
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: 57f89bf ("network: Allow af_packet to transmit +4 bytes for VLAN packets.")
Fixes: 52f1454 ("packet: allow to transmit +4 byte in TX_RING slot for VLAN case")
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Daniel Borkmann authored and David S. Miller committed Nov 15, 2015
1 parent 8fd6c80 commit 3c70c13
Showing 1 changed file with 25 additions and 35 deletions.
60 changes: 25 additions & 35 deletions net/packet/af_packet.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1902,18 +1916,10 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg,
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)) {
err = -EMSGSIZE;
goto out_unlock;
}
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;
Expand Down Expand Up @@ -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.
*/
tp_len > dev->mtu + dev->hard_header_len &&
!packet_extra_vlan_len_allowed(dev, skb))
tp_len = -EMSGSIZE;

skb_reset_mac_header(skb);
ehdr = eth_hdr(skb);
if (ehdr->h_proto != htons(ETH_P_8021Q))
tp_len = -EMSGSIZE;
}
if (unlikely(tp_len < 0)) {
if (po->tp_loss) {
__packet_set_status(po, ph,
Expand Down Expand Up @@ -2765,18 +2763,10 @@ 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)) {
err = -EMSGSIZE;
goto out_free;
}
if (!gso_type && (len > dev->mtu + reserve + extra_len) &&
!packet_extra_vlan_len_allowed(dev, skb)) {
err = -EMSGSIZE;
goto out_free;
}

skb->protocol = proto;
Expand Down

0 comments on commit 3c70c13

Please sign in to comment.