2 * Copyright (c) 2010, 2011 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 int compute_ip_summed(struct sk_buff *skb, bool xmit);
32 void forward_ip_summed(struct sk_buff *skb, bool xmit);
33 u8 get_ip_summed(struct sk_buff *skb);
34 void set_ip_summed(struct sk_buff *skb, u8 ip_summed);
35 void get_skb_csum_pointers(const struct sk_buff *skb, u16 *csum_start,
37 void set_skb_csum_pointers(struct sk_buff *skb, u16 csum_start,
40 static inline int compute_ip_summed(struct sk_buff *skb, bool xmit)
45 static inline void forward_ip_summed(struct sk_buff *skb, bool xmit) { }
47 static inline u8 get_ip_summed(struct sk_buff *skb)
49 return skb->ip_summed;
52 static inline void set_ip_summed(struct sk_buff *skb, u8 ip_summed)
54 skb->ip_summed = ip_summed;
57 static inline void get_skb_csum_pointers(const struct sk_buff *skb,
58 u16 *csum_start, u16 *csum_offset)
60 *csum_start = skb->csum_start;
61 *csum_offset = skb->csum_offset;
64 static inline void set_skb_csum_pointers(struct sk_buff *skb, u16 csum_start,
67 skb->csum_start = csum_start;
68 skb->csum_offset = csum_offset;
72 /* This is really compatibility code that belongs in the compat directory.
73 * However, it needs access to our normalized checksum values, so put it here.
75 #if defined(NEED_CSUM_NORMALIZE) || LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
76 #define inet_proto_csum_replace4 rpl_inet_proto_csum_replace4
77 static inline void inet_proto_csum_replace4(__sum16 *sum, struct sk_buff *skb,
78 __be32 from, __be32 to,
81 __be32 diff[] = { ~from, to };
83 if (get_ip_summed(skb) != OVS_CSUM_PARTIAL) {
84 *sum = csum_fold(csum_partial((char *)diff, sizeof(diff),
86 if (get_ip_summed(skb) == OVS_CSUM_COMPLETE && pseudohdr)
87 skb->csum = ~csum_partial((char *)diff, sizeof(diff),
90 *sum = ~csum_fold(csum_partial((char *)diff, sizeof(diff),
95 #ifdef NEED_CSUM_NORMALIZE
96 static inline void update_csum_start(struct sk_buff *skb, int delta)
98 if (get_ip_summed(skb) == OVS_CSUM_PARTIAL) {
99 u16 csum_start, csum_offset;
101 get_skb_csum_pointers(skb, &csum_start, &csum_offset);
102 set_skb_csum_pointers(skb, csum_start + delta, csum_offset);
106 static inline int rpl_pskb_expand_head(struct sk_buff *skb, int nhead,
107 int ntail, gfp_t gfp_mask)
110 int old_headroom = skb_headroom(skb);
112 err = pskb_expand_head(skb, nhead, ntail, gfp_mask);
116 update_csum_start(skb, skb_headroom(skb) - old_headroom);
120 #define pskb_expand_head rpl_pskb_expand_head
122 static inline unsigned char *rpl__pskb_pull_tail(struct sk_buff *skb,
126 int old_headroom = skb_headroom(skb);
128 ret = __pskb_pull_tail(skb, delta);
132 update_csum_start(skb, skb_headroom(skb) - old_headroom);
136 #define __pskb_pull_tail rpl__pskb_pull_tail
139 #endif /* checksum.h */