|
|
@@ -1098,6 +1098,36 @@ static int ovs_ct_commit(struct net *net, struct sw_flow_key *key,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/* Trim the skb to the length specified by the IP/IPv6 header,
|
|
|
+ * removing any trailing lower-layer padding. This prepares the skb
|
|
|
+ * for higher-layer processing that assumes skb->len excludes padding
|
|
|
+ * (such as nf_ip_checksum). The caller needs to pull the skb to the
|
|
|
+ * network header, and ensure ip_hdr/ipv6_hdr points to valid data.
|
|
|
+ */
|
|
|
+static int ovs_skb_network_trim(struct sk_buff *skb)
|
|
|
+{
|
|
|
+ unsigned int len;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ switch (skb->protocol) {
|
|
|
+ case htons(ETH_P_IP):
|
|
|
+ len = ntohs(ip_hdr(skb)->tot_len);
|
|
|
+ break;
|
|
|
+ case htons(ETH_P_IPV6):
|
|
|
+ len = sizeof(struct ipv6hdr)
|
|
|
+ + ntohs(ipv6_hdr(skb)->payload_len);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ len = skb->len;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = pskb_trim_rcsum(skb, len);
|
|
|
+ if (err)
|
|
|
+ kfree_skb(skb);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
/* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero
|
|
|
* value if 'skb' is freed.
|
|
|
*/
|
|
|
@@ -1112,6 +1142,10 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb,
|
|
|
nh_ofs = skb_network_offset(skb);
|
|
|
skb_pull_rcsum(skb, nh_ofs);
|
|
|
|
|
|
+ err = ovs_skb_network_trim(skb);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
if (key->ip.frag != OVS_FRAG_TYPE_NONE) {
|
|
|
err = handle_fragments(net, key, info->zone.id, skb);
|
|
|
if (err)
|