|
@@ -47,11 +47,29 @@
|
|
|
*
|
|
|
* The hardware you're dealing with doesn't calculate the full checksum
|
|
|
* (as in CHECKSUM_COMPLETE), but it does parse headers and verify checksums
|
|
|
- * for specific protocols e.g. TCP/UDP/SCTP, then, for such packets it will
|
|
|
- * set CHECKSUM_UNNECESSARY if their checksums are okay. skb->csum is still
|
|
|
- * undefined in this case though. It is a bad option, but, unfortunately,
|
|
|
- * nowadays most vendors do this. Apparently with the secret goal to sell
|
|
|
- * you new devices, when you will add new protocol to your host, f.e. IPv6 8)
|
|
|
+ * for specific protocols. For such packets it will set CHECKSUM_UNNECESSARY
|
|
|
+ * if their checksums are okay. skb->csum is still undefined in this case
|
|
|
+ * though. It is a bad option, but, unfortunately, nowadays most vendors do
|
|
|
+ * this. Apparently with the secret goal to sell you new devices, when you
|
|
|
+ * will add new protocol to your host, f.e. IPv6 8)
|
|
|
+ *
|
|
|
+ * CHECKSUM_UNNECESSARY is applicable to following protocols:
|
|
|
+ * TCP: IPv6 and IPv4.
|
|
|
+ * UDP: IPv4 and IPv6. A device may apply CHECKSUM_UNNECESSARY to a
|
|
|
+ * zero UDP checksum for either IPv4 or IPv6, the networking stack
|
|
|
+ * may perform further validation in this case.
|
|
|
+ * GRE: only if the checksum is present in the header.
|
|
|
+ * SCTP: indicates the CRC in SCTP header has been validated.
|
|
|
+ *
|
|
|
+ * skb->csum_level indicates the number of consecutive checksums found in
|
|
|
+ * the packet minus one that have been verified as CHECKSUM_UNNECESSARY.
|
|
|
+ * For instance if a device receives an IPv6->UDP->GRE->IPv4->TCP packet
|
|
|
+ * and a device is able to verify the checksums for UDP (possibly zero),
|
|
|
+ * GRE (checksum flag is set), and TCP-- skb->csum_level would be set to
|
|
|
+ * two. If the device were only able to verify the UDP checksum and not
|
|
|
+ * GRE, either because it doesn't support GRE checksum of because GRE
|
|
|
+ * checksum is bad, skb->csum_level would be set to zero (TCP checksum is
|
|
|
+ * not considered in this case).
|
|
|
*
|
|
|
* CHECKSUM_COMPLETE:
|
|
|
*
|
|
@@ -112,6 +130,9 @@
|
|
|
#define CHECKSUM_COMPLETE 2
|
|
|
#define CHECKSUM_PARTIAL 3
|
|
|
|
|
|
+/* Maximum value in skb->csum_level */
|
|
|
+#define SKB_MAX_CSUM_LEVEL 3
|
|
|
+
|
|
|
#define SKB_DATA_ALIGN(X) ALIGN(X, SMP_CACHE_BYTES)
|
|
|
#define SKB_WITH_OVERHEAD(X) \
|
|
|
((X) - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
|
|
@@ -571,11 +592,7 @@ struct sk_buff {
|
|
|
__u8 wifi_acked:1;
|
|
|
__u8 no_fcs:1;
|
|
|
__u8 head_frag:1;
|
|
|
- /* Encapsulation protocol and NIC drivers should use
|
|
|
- * this flag to indicate to each other if the skb contains
|
|
|
- * encapsulated packet or not and maybe use the inner packet
|
|
|
- * headers if needed
|
|
|
- */
|
|
|
+ /* Indicates the inner headers are valid in the skbuff. */
|
|
|
__u8 encapsulation:1;
|
|
|
__u8 encap_hdr_csum:1;
|
|
|
__u8 csum_valid:1;
|
|
@@ -598,6 +615,11 @@ struct sk_buff {
|
|
|
__u32 reserved_tailroom;
|
|
|
};
|
|
|
|
|
|
+ kmemcheck_bitfield_begin(flags3);
|
|
|
+ __u8 csum_level:2;
|
|
|
+ /* 14 bit hole */
|
|
|
+ kmemcheck_bitfield_end(flags3);
|
|
|
+
|
|
|
__be16 inner_protocol;
|
|
|
__u16 inner_transport_header;
|
|
|
__u16 inner_network_header;
|
|
@@ -1862,18 +1884,6 @@ static inline int pskb_network_may_pull(struct sk_buff *skb, unsigned int len)
|
|
|
return pskb_may_pull(skb, skb_network_offset(skb) + len);
|
|
|
}
|
|
|
|
|
|
-static inline void skb_pop_rcv_encapsulation(struct sk_buff *skb)
|
|
|
-{
|
|
|
- /* Only continue with checksum unnecessary if device indicated
|
|
|
- * it is valid across encapsulation (skb->encapsulation was set).
|
|
|
- */
|
|
|
- if (skb->ip_summed == CHECKSUM_UNNECESSARY && !skb->encapsulation)
|
|
|
- skb->ip_summed = CHECKSUM_NONE;
|
|
|
-
|
|
|
- skb->encapsulation = 0;
|
|
|
- skb->csum_valid = 0;
|
|
|
-}
|
|
|
-
|
|
|
/*
|
|
|
* CPUs often take a performance hit when accessing unaligned memory
|
|
|
* locations. The actual performance hit varies, it can be small if the
|
|
@@ -2794,6 +2804,27 @@ static inline __sum16 skb_checksum_complete(struct sk_buff *skb)
|
|
|
0 : __skb_checksum_complete(skb);
|
|
|
}
|
|
|
|
|
|
+static inline void __skb_decr_checksum_unnecessary(struct sk_buff *skb)
|
|
|
+{
|
|
|
+ if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
|
|
|
+ if (skb->csum_level == 0)
|
|
|
+ skb->ip_summed = CHECKSUM_NONE;
|
|
|
+ else
|
|
|
+ skb->csum_level--;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static inline void __skb_incr_checksum_unnecessary(struct sk_buff *skb)
|
|
|
+{
|
|
|
+ if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
|
|
|
+ if (skb->csum_level < SKB_MAX_CSUM_LEVEL)
|
|
|
+ skb->csum_level++;
|
|
|
+ } else if (skb->ip_summed == CHECKSUM_NONE) {
|
|
|
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
|
|
|
+ skb->csum_level = 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/* Check if we need to perform checksum complete validation.
|
|
|
*
|
|
|
* Returns true if checksum complete is needed, false otherwise
|
|
@@ -2805,6 +2836,7 @@ static inline bool __skb_checksum_validate_needed(struct sk_buff *skb,
|
|
|
{
|
|
|
if (skb_csum_unnecessary(skb) || (zero_okay && !check)) {
|
|
|
skb->csum_valid = 1;
|
|
|
+ __skb_decr_checksum_unnecessary(skb);
|
|
|
return false;
|
|
|
}
|
|
|
|