- __be32 diff[] = { ~from, to };
-
-/* On older kernels, CHECKSUM_PARTIAL and CHECKSUM_COMPLETE are both defined
- * as CHECKSUM_HW. However, we can make some inferences so that we can update
- * the checksums appropriately. */
- enum {
- CSUM_PARTIAL, /* Partial checksum, skb->csum undefined. */
- CSUM_PACKET, /* In-packet checksum, skb->csum undefined. */
- CSUM_COMPLETE, /* In-packet checksum, skb->csum valid. */
- } csum_type;
-
- csum_type = CSUM_PACKET;
-#ifndef CHECKSUM_HW
- /* Newer kernel, just map between kernel types and ours. */
- if (skb->ip_summed == CHECKSUM_PARTIAL)
- csum_type = CSUM_PARTIAL;
- else if (skb->ip_summed == CHECKSUM_COMPLETE)
- csum_type = CSUM_COMPLETE;
-#else
- /* In theory this could be either CHECKSUM_PARTIAL or CHECKSUM_COMPLETE.
- * However, we should only get CHECKSUM_PARTIAL packets from Xen, which
- * uses some special fields to represent this (see below). Since we
- * can only make one type work, pick the one that actually happens in
- * practice. */
- if (skb->ip_summed == CHECKSUM_HW)
- csum_type = CSUM_COMPLETE;
-#endif
-#if defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID)
- /* Xen has a special way of representing CHECKSUM_PARTIAL on older
- * kernels. */
- if (skb->proto_csum_blank)
- csum_type = CSUM_PARTIAL;
-#endif
-
- if (csum_type != CSUM_PARTIAL) {
- *sum = csum_fold(csum_partial((char *)diff, sizeof(diff),
- ~csum_unfold(*sum)));
- if (csum_type == CSUM_COMPLETE && pseudohdr)
- skb->csum = ~csum_partial((char *)diff, sizeof(diff),
- ~skb->csum);
- } else if (pseudohdr)
- *sum = ~csum_fold(csum_partial((char *)diff, sizeof(diff),
- csum_unfold(*sum)));
+ struct iphdr *nh;
+ int err;
+
+ err = make_writable(skb, skb_network_offset(skb) +
+ sizeof(struct iphdr));
+ if (unlikely(err))
+ return err;
+
+ nh = ip_hdr(skb);
+
+ if (ipv4_key->ipv4_src != nh->saddr)
+ set_ip_addr(skb, nh, &nh->saddr, ipv4_key->ipv4_src);
+
+ if (ipv4_key->ipv4_dst != nh->daddr)
+ set_ip_addr(skb, nh, &nh->daddr, ipv4_key->ipv4_dst);
+
+ if (ipv4_key->ipv4_tos != nh->tos)
+ ipv4_change_dsfield(nh, 0, ipv4_key->ipv4_tos);
+
+ if (ipv4_key->ipv4_ttl != nh->ttl)
+ set_ip_ttl(skb, nh, ipv4_key->ipv4_ttl);
+
+ return 0;