xen tcp checksum for back and front net driver

Test case

two linux guest vm on a same host(physical machine), there is a tcp session
between them.

conclusion:

the checksum in tcp header is only cover the faked tcp header not the whole
tcp packet.

call trace

vm1(netfront) —> host backend(vif_1) —> bridge —> host backend(vif_2) —> vm2(netfrontend)

netfront

before sent to backend, netfront driver set two flags:
XEN_NETTXF_csum_blank | XEN_NETTXF_data_validated;"

1
2
3
4
5
6
575         if (skb->ip_summed == CHECKSUM_PARTIAL)
576 /* local packet? */
577 tx->flags |= XEN_NETTXF_csum_blank | XEN_NETTXF_data_validated;
578 else if (skb->ip_summed == CHECKSUM_UNNECESSARY)
579 /* remote but checksummed. */
580 tx->flags |= XEN_NETTXF_data_validated;
net backend
1
2
3
4
5
6
7
8
9
1409 static int xenvif_tx_submit(struct xenvif_queue *queue)
1410 {
...
1452 if (txp->flags & XEN_NETTXF_csum_blank)
1453 skb->ip_summed = CHECKSUM_PARTIAL;
1454 else if (txp->flags & XEN_NETTXF_data_validated)
1455 skb->ip_summed = CHECKSUM_UNNECESSARY;
...
1515 netif_receive_skb(skb);
bridge

forward this packet to vm2’s backend.

netbackend

after bridge, skb reach peer vm2’s backend’s ndo xmit function.

1
2
3
4
5
6
7
8
9
10
11
12
139 static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)

...
497 static void xenvif_rx_action(struct xenvif_queue *queue)
498 {
...
567
568 if (skb->ip_summed == CHECKSUM_PARTIAL) /* local packet? */
569 flags |= XEN_NETRXF_csum_blank | XEN_NETRXF_data_validated;
570 else if (skb->ip_summed == CHECKSUM_UNNECESSARY)
571 /* remote but checksummed. */
572 flags |= XEN_NETRXF_data_validated;
netfront

xen net front driver will do nothing. just setup the csum offset and len in skb,
even the fake tcp header checksum is not re-calculated.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
876 static int handle_incoming_queue(struct netfront_queue *queue,
877 struct sk_buff_head *rxq)
878 {
...
893 if (checksum_setup(queue->info->netdev, skb)) {
894 kfree_skb(skb);
895 packets_dropped++;
896 queue->info->netdev->stats.rx_errors++;
897 continue;
898 }
...
905 /* Pass it up. */
906 napi_gro_receive(&queue->napi, skb);
907 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
852 static int checksum_setup(struct net_device *dev, struct sk_buff *skb)
853 {
854 bool recalculate_partial_csum = false;
855
856 /*
857 * A GSO SKB must be CHECKSUM_PARTIAL. However some buggy
858 * peers can fail to set NETRXF_csum_blank when sending a GSO
859 * frame. In this case force the SKB to CHECKSUM_PARTIAL and
860 * recalculate the partial checksum.
861 */
862 if (skb->ip_summed != CHECKSUM_PARTIAL && skb_is_gso(skb)) {
863 struct netfront_info *np = netdev_priv(dev);
864 atomic_inc(&np->rx_gso_checksum_fixup);
865 skb->ip_summed = CHECKSUM_PARTIAL;
866 recalculate_partial_csum = true;
867 }
868
869 /* A non-CHECKSUM_PARTIAL SKB does not require setup. */
870 if (skb->ip_summed != CHECKSUM_PARTIAL)
871 return 0;
872
873 return skb_checksum_setup(skb, recalculate_partial_csum);
874 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
4006 /**
4007 * skb_checksum_setup - set up partial checksum offset
4008 * @skb: the skb to set up
4009 * @recalculate: if true the pseudo-header checksum will be recalculated
4010 */
4011 int skb_checksum_setup(struct sk_buff *skb, bool recalculate)
4012 {
4013 int err;
4014
4015 switch (skb->protocol) {
4016 case htons(ETH_P_IP):
4017 err = skb_checksum_setup_ipv4(skb, recalculate);
4018 break;
4019
4020 case htons(ETH_P_IPV6):
4021 err = skb_checksum_setup_ipv6(skb, recalculate);
4022 break;
4023
4024 default:
4025 err = -EPROTO;
4026 break;
4027 }
4028
4029 return err;
4030 }
4031 EXPORT_SYMBOL(skb_checksum_setup);