2 * Copyright (c) 2010 Nicira Networks.
3 * Distributed under the terms of the GNU GPL version 2.
5 * Significant portions of this file may be copied from parts of the Linux
6 * kernel, by Linus Torvalds and others.
12 #include <linux/skbuff.h>
13 #include <linux/version.h>
15 #include <net/checksum.h>
17 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) || \
18 (defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID))
19 #define NEED_CSUM_NORMALIZE
22 /* These are the same values as the checksum constants in 2.6.22+. */
25 OVS_CSUM_UNNECESSARY = 1,
26 OVS_CSUM_COMPLETE = 2,
30 #ifdef NEED_CSUM_NORMALIZE
31 void compute_ip_summed(struct sk_buff *skb, bool xmit);
32 u8 get_ip_summed(struct sk_buff *skb);
34 static inline void compute_ip_summed(struct sk_buff *skb, bool xmit) { }
35 static inline u8 get_ip_summed(struct sk_buff *skb)
37 return skb->ip_summed;
41 /* This function closely resembles skb_forward_csum() used by the bridge. It
42 * is slightly different because we are only concerned with bridging and not
43 * other types of forwarding and can get away with slightly more optimal
46 static inline void forward_ip_summed(struct sk_buff *skb)
49 if (get_ip_summed(skb) == OVS_CSUM_COMPLETE)
50 skb->ip_summed = CHECKSUM_NONE;
54 #if defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID)
55 int vswitch_skb_checksum_setup(struct sk_buff *skb);
57 static inline int vswitch_skb_checksum_setup(struct sk_buff *skb)
63 static inline void set_skb_csum_bits(const struct sk_buff *old_skb,
64 struct sk_buff *new_skb)
66 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
67 /* Before 2.6.24 these fields were not copied when
68 * doing an skb_copy_expand. */
69 new_skb->ip_summed = old_skb->ip_summed;
70 new_skb->csum = old_skb->csum;
72 #if defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID)
73 /* These fields are copied in skb_clone but not in
74 * skb_copy or related functions. We need to manually
75 * copy them over here. */
76 new_skb->proto_data_valid = old_skb->proto_data_valid;
77 new_skb->proto_csum_blank = old_skb->proto_csum_blank;
81 static inline void get_skb_csum_pointers(const struct sk_buff *skb,
82 u16 *csum_start, u16 *csum_offset)
84 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
85 *csum_start = skb->csum_start;
86 *csum_offset = skb->csum_offset;
88 *csum_start = skb_headroom(skb) + skb_transport_offset(skb);
89 *csum_offset = skb->csum;
93 static inline void set_skb_csum_pointers(struct sk_buff *skb, u16 csum_start,
96 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
97 skb->csum_start = csum_start;
98 skb->csum_offset = csum_offset;
100 skb_set_transport_header(skb, csum_start - skb_headroom(skb));
101 skb->csum = csum_offset;
105 #if defined(NEED_CSUM_NORMALIZE) || LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
106 /* This is really compatibility code that belongs in the compat directory.
107 * However, it needs access to our normalized checksum values, so put it here.
109 #define inet_proto_csum_replace4 rpl_inet_proto_csum_replace4
110 static inline void inet_proto_csum_replace4(__sum16 *sum, struct sk_buff *skb,
111 __be32 from, __be32 to,
114 __be32 diff[] = { ~from, to };
116 if (get_ip_summed(skb) != OVS_CSUM_PARTIAL) {
117 *sum = csum_fold(csum_partial((char *)diff, sizeof(diff),
118 ~csum_unfold(*sum)));
119 if (get_ip_summed(skb) == OVS_CSUM_COMPLETE && pseudohdr)
120 skb->csum = ~csum_partial((char *)diff, sizeof(diff),
122 } else if (pseudohdr)
123 *sum = ~csum_fold(csum_partial((char *)diff, sizeof(diff),
128 #endif /* checksum.h */