Skip to content

Commit 56b8e23

Browse files
committed
af_packet: Handle outgoing VLAN packets without hardware offloading
JIRA: https://issues.redhat.com/browse/RHEL-84598 Upstream Status: net.git commit 79eecf6 commit 79eecf6 Author: Chengen Du <chengen.du@canonical.com> Date: Sat Jul 13 19:47:35 2024 +0800 af_packet: Handle outgoing VLAN packets without hardware offloading The issue initially stems from libpcap. The ethertype will be overwritten as the VLAN TPID if the network interface lacks hardware VLAN offloading. In the outbound packet path, if hardware VLAN offloading is unavailable, the VLAN tag is inserted into the payload but then cleared from the sk_buff struct. Consequently, this can lead to a false negative when checking for the presence of a VLAN tag, causing the packet sniffing outcome to lack VLAN tag information (i.e., TCI-TPID). As a result, the packet capturing tool may be unable to parse packets as expected. The TCI-TPID is missing because the prb_fill_vlan_info() function does not modify the tp_vlan_tci/tp_vlan_tpid values, as the information is in the payload and not in the sk_buff struct. The skb_vlan_tag_present() function only checks vlan_all in the sk_buff struct. In cooked mode, the L2 header is stripped, preventing the packet capturing tool from determining the correct TCI-TPID value. Additionally, the protocol in SLL is incorrect, which means the packet capturing tool cannot parse the L3 header correctly. Link: the-tcpdump-group/libpcap#1105 Link: https://lore.kernel.org/netdev/20240520070348.26725-1-chengen.du@canonical.com/T/#u Fixes: 393e52e ("packet: deliver VLAN TCI to userspace") Cc: stable@vger.kernel.org Signed-off-by: Chengen Du <chengen.du@canonical.com> Reviewed-by: Willem de Bruijn <willemb@google.com> Link: https://patch.msgid.link/20240713114735.62360-1-chengen.du@canonical.com Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Davide Caratti <dcaratti@redhat.com>
1 parent 818e414 commit 56b8e23

File tree

1 file changed

+84
-2
lines changed

1 file changed

+84
-2
lines changed

net/packet/af_packet.c

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,61 @@ static void *packet_current_frame(struct packet_sock *po,
503503
return packet_lookup_frame(po, rb, rb->head, status);
504504
}
505505

506+
static u16 vlan_get_tci(struct sk_buff *skb, struct net_device *dev)
507+
{
508+
u8 *skb_orig_data = skb->data;
509+
int skb_orig_len = skb->len;
510+
struct vlan_hdr vhdr, *vh;
511+
unsigned int header_len;
512+
513+
if (!dev)
514+
return 0;
515+
516+
/* In the SOCK_DGRAM scenario, skb data starts at the network
517+
* protocol, which is after the VLAN headers. The outer VLAN
518+
* header is at the hard_header_len offset in non-variable
519+
* length link layer headers. If it's a VLAN device, the
520+
* min_header_len should be used to exclude the VLAN header
521+
* size.
522+
*/
523+
if (dev->min_header_len == dev->hard_header_len)
524+
header_len = dev->hard_header_len;
525+
else if (is_vlan_dev(dev))
526+
header_len = dev->min_header_len;
527+
else
528+
return 0;
529+
530+
skb_push(skb, skb->data - skb_mac_header(skb));
531+
vh = skb_header_pointer(skb, header_len, sizeof(vhdr), &vhdr);
532+
if (skb_orig_data != skb->data) {
533+
skb->data = skb_orig_data;
534+
skb->len = skb_orig_len;
535+
}
536+
if (unlikely(!vh))
537+
return 0;
538+
539+
return ntohs(vh->h_vlan_TCI);
540+
}
541+
542+
static __be16 vlan_get_protocol_dgram(struct sk_buff *skb)
543+
{
544+
__be16 proto = skb->protocol;
545+
546+
if (unlikely(eth_type_vlan(proto))) {
547+
u8 *skb_orig_data = skb->data;
548+
int skb_orig_len = skb->len;
549+
550+
skb_push(skb, skb->data - skb_mac_header(skb));
551+
proto = __vlan_get_protocol(skb, proto, NULL);
552+
if (skb_orig_data != skb->data) {
553+
skb->data = skb_orig_data;
554+
skb->len = skb_orig_len;
555+
}
556+
}
557+
558+
return proto;
559+
}
560+
506561
static void prb_del_retire_blk_timer(struct tpacket_kbdq_core *pkc)
507562
{
508563
del_timer_sync(&pkc->retire_blk_timer);
@@ -972,10 +1027,16 @@ static void prb_clear_rxhash(struct tpacket_kbdq_core *pkc,
9721027
static void prb_fill_vlan_info(struct tpacket_kbdq_core *pkc,
9731028
struct tpacket3_hdr *ppd)
9741029
{
1030+
struct packet_sock *po = container_of(pkc, struct packet_sock, rx_ring.prb_bdqc);
1031+
9751032
if (skb_vlan_tag_present(pkc->skb)) {
9761033
ppd->hv1.tp_vlan_tci = skb_vlan_tag_get(pkc->skb);
9771034
ppd->hv1.tp_vlan_tpid = ntohs(pkc->skb->vlan_proto);
9781035
ppd->tp_status = TP_STATUS_VLAN_VALID | TP_STATUS_VLAN_TPID_VALID;
1036+
} else if (unlikely(po->sk.sk_type == SOCK_DGRAM && eth_type_vlan(pkc->skb->protocol))) {
1037+
ppd->hv1.tp_vlan_tci = vlan_get_tci(pkc->skb, pkc->skb->dev);
1038+
ppd->hv1.tp_vlan_tpid = ntohs(pkc->skb->protocol);
1039+
ppd->tp_status = TP_STATUS_VLAN_VALID | TP_STATUS_VLAN_TPID_VALID;
9791040
} else {
9801041
ppd->hv1.tp_vlan_tci = 0;
9811042
ppd->hv1.tp_vlan_tpid = 0;
@@ -2395,6 +2456,10 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
23952456
h.h2->tp_vlan_tci = skb_vlan_tag_get(skb);
23962457
h.h2->tp_vlan_tpid = ntohs(skb->vlan_proto);
23972458
status |= TP_STATUS_VLAN_VALID | TP_STATUS_VLAN_TPID_VALID;
2459+
} else if (unlikely(sk->sk_type == SOCK_DGRAM && eth_type_vlan(skb->protocol))) {
2460+
h.h2->tp_vlan_tci = vlan_get_tci(skb, skb->dev);
2461+
h.h2->tp_vlan_tpid = ntohs(skb->protocol);
2462+
status |= TP_STATUS_VLAN_VALID | TP_STATUS_VLAN_TPID_VALID;
23982463
} else {
23992464
h.h2->tp_vlan_tci = 0;
24002465
h.h2->tp_vlan_tpid = 0;
@@ -2424,7 +2489,8 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
24242489
sll->sll_halen = dev_parse_header(skb, sll->sll_addr);
24252490
sll->sll_family = AF_PACKET;
24262491
sll->sll_hatype = dev->type;
2427-
sll->sll_protocol = skb->protocol;
2492+
sll->sll_protocol = (sk->sk_type == SOCK_DGRAM) ?
2493+
vlan_get_protocol_dgram(skb) : skb->protocol;
24282494
sll->sll_pkttype = skb->pkt_type;
24292495
if (unlikely(packet_sock_flag(po, PACKET_SOCK_ORIGDEV)))
24302496
sll->sll_ifindex = orig_dev->ifindex;
@@ -3441,7 +3507,8 @@ static int packet_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
34413507
/* Original length was stored in sockaddr_ll fields */
34423508
origlen = PACKET_SKB_CB(skb)->sa.origlen;
34433509
sll->sll_family = AF_PACKET;
3444-
sll->sll_protocol = skb->protocol;
3510+
sll->sll_protocol = (sock->type == SOCK_DGRAM) ?
3511+
vlan_get_protocol_dgram(skb) : skb->protocol;
34453512
}
34463513

34473514
sock_recv_ts_and_drops(msg, sk, skb);
@@ -3498,6 +3565,21 @@ static int packet_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
34983565
aux.tp_vlan_tci = skb_vlan_tag_get(skb);
34993566
aux.tp_vlan_tpid = ntohs(skb->vlan_proto);
35003567
aux.tp_status |= TP_STATUS_VLAN_VALID | TP_STATUS_VLAN_TPID_VALID;
3568+
} else if (unlikely(sock->type == SOCK_DGRAM && eth_type_vlan(skb->protocol))) {
3569+
struct sockaddr_ll *sll = &PACKET_SKB_CB(skb)->sa.ll;
3570+
struct net_device *dev;
3571+
3572+
rcu_read_lock();
3573+
dev = dev_get_by_index_rcu(sock_net(sk), sll->sll_ifindex);
3574+
if (dev) {
3575+
aux.tp_vlan_tci = vlan_get_tci(skb, dev);
3576+
aux.tp_vlan_tpid = ntohs(skb->protocol);
3577+
aux.tp_status |= TP_STATUS_VLAN_VALID | TP_STATUS_VLAN_TPID_VALID;
3578+
} else {
3579+
aux.tp_vlan_tci = 0;
3580+
aux.tp_vlan_tpid = 0;
3581+
}
3582+
rcu_read_unlock();
35013583
} else {
35023584
aux.tp_vlan_tci = 0;
35033585
aux.tp_vlan_tpid = 0;

0 commit comments

Comments
 (0)