2 * Copyright (c) 2007-2011 Nicira, Inc.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of version 2 of the GNU General Public
6 * License as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 #include <linux/skbuff.h>
23 #include <linux/version.h>
25 #include <net/checksum.h>
27 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) || \
28 (defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID))
29 #define NEED_CSUM_NORMALIZE
32 /* These are the same values as the checksum constants in 2.6.22+. */
35 OVS_CSUM_UNNECESSARY = 1,
36 OVS_CSUM_COMPLETE = 2,
40 #ifdef NEED_CSUM_NORMALIZE
41 int compute_ip_summed(struct sk_buff *skb, bool xmit);
42 void forward_ip_summed(struct sk_buff *skb, bool xmit);
43 u8 get_ip_summed(struct sk_buff *skb);
44 void set_ip_summed(struct sk_buff *skb, u8 ip_summed);
45 void get_skb_csum_pointers(const struct sk_buff *skb, u16 *csum_start,
47 void set_skb_csum_pointers(struct sk_buff *skb, u16 csum_start,
50 static inline int compute_ip_summed(struct sk_buff *skb, bool xmit)
55 static inline void forward_ip_summed(struct sk_buff *skb, bool xmit) { }
57 static inline u8 get_ip_summed(struct sk_buff *skb)
59 return skb->ip_summed;
62 static inline void set_ip_summed(struct sk_buff *skb, u8 ip_summed)
64 skb->ip_summed = ip_summed;
67 static inline void get_skb_csum_pointers(const struct sk_buff *skb,
68 u16 *csum_start, u16 *csum_offset)
70 *csum_start = skb->csum_start;
71 *csum_offset = skb->csum_offset;
74 static inline void set_skb_csum_pointers(struct sk_buff *skb, u16 csum_start,
77 skb->csum_start = csum_start;
78 skb->csum_offset = csum_offset;
82 /* This is really compatibility code that belongs in the compat directory.
83 * However, it needs access to our normalized checksum values, so put it here.
85 #if defined(NEED_CSUM_NORMALIZE) || LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
86 #define inet_proto_csum_replace4 rpl_inet_proto_csum_replace4
87 static inline void inet_proto_csum_replace4(__sum16 *sum, struct sk_buff *skb,
88 __be32 from, __be32 to,
91 __be32 diff[] = { ~from, to };
93 if (get_ip_summed(skb) != OVS_CSUM_PARTIAL) {
94 *sum = csum_fold(csum_partial((char *)diff, sizeof(diff),
96 if (get_ip_summed(skb) == OVS_CSUM_COMPLETE && pseudohdr)
97 skb->csum = ~csum_partial((char *)diff, sizeof(diff),
100 *sum = ~csum_fold(csum_partial((char *)diff, sizeof(diff),
105 #if defined(NEED_CSUM_NORMALIZE) || LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
106 #define inet_proto_csum_replace16 rpl_inet_proto_csum_replace16
107 static inline void inet_proto_csum_replace16(__sum16 *sum,
114 ~from[0], ~from[1], ~from[2], ~from[3],
115 to[0], to[1], to[2], to[3],
117 if (get_ip_summed(skb) != OVS_CSUM_PARTIAL) {
118 *sum = csum_fold(csum_partial(diff, sizeof(diff),
119 ~csum_unfold(*sum)));
120 if (get_ip_summed(skb) == OVS_CSUM_COMPLETE && pseudohdr)
121 skb->csum = ~csum_partial(diff, sizeof(diff),
123 } else if (pseudohdr)
124 *sum = ~csum_fold(csum_partial(diff, sizeof(diff),
129 #ifdef NEED_CSUM_NORMALIZE
130 static inline void update_csum_start(struct sk_buff *skb, int delta)
132 if (get_ip_summed(skb) == OVS_CSUM_PARTIAL) {
133 u16 csum_start, csum_offset;
135 get_skb_csum_pointers(skb, &csum_start, &csum_offset);
136 set_skb_csum_pointers(skb, csum_start + delta, csum_offset);
140 static inline int rpl_pskb_expand_head(struct sk_buff *skb, int nhead,
141 int ntail, gfp_t gfp_mask)
144 int old_headroom = skb_headroom(skb);
146 err = pskb_expand_head(skb, nhead, ntail, gfp_mask);
150 update_csum_start(skb, skb_headroom(skb) - old_headroom);
154 #define pskb_expand_head rpl_pskb_expand_head
156 static inline unsigned char *rpl__pskb_pull_tail(struct sk_buff *skb,
160 int old_headroom = skb_headroom(skb);
162 ret = __pskb_pull_tail(skb, delta);
166 update_csum_start(skb, skb_headroom(skb) - old_headroom);
170 #define __pskb_pull_tail rpl__pskb_pull_tail
173 #endif /* checksum.h */