ovs-ofctl: Use vconn_open() instead of vconn_open_block() in open_vconn__().
[sliver-openvswitch.git] / datapath / checksum.h
1 /*
2  * Copyright (c) 2007-2011 Nicira, Inc.
3  *
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.
7  *
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.
12  *
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
16  * 02110-1301, USA
17  */
18
19 #ifndef CHECKSUM_H
20 #define CHECKSUM_H 1
21
22 #include <linux/skbuff.h>
23 #include <linux/version.h>
24
25 #include <net/checksum.h>
26
27 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) || \
28         (defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID))
29 #define NEED_CSUM_NORMALIZE
30 #endif
31
32 /* These are the same values as the checksum constants in 2.6.22+. */
33 enum csum_type {
34         OVS_CSUM_NONE = 0,
35         OVS_CSUM_UNNECESSARY = 1,
36         OVS_CSUM_COMPLETE = 2,
37         OVS_CSUM_PARTIAL = 3,
38 };
39
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,
46                            u16 *csum_offset);
47 void set_skb_csum_pointers(struct sk_buff *skb, u16 csum_start,
48                            u16 csum_offset);
49 #else
50 static inline int compute_ip_summed(struct sk_buff *skb, bool xmit)
51 {
52         return 0;
53 }
54
55 static inline void forward_ip_summed(struct sk_buff *skb, bool xmit) { }
56
57 static inline u8 get_ip_summed(struct sk_buff *skb)
58 {
59         return skb->ip_summed;
60 }
61
62 static inline void set_ip_summed(struct sk_buff *skb, u8 ip_summed)
63 {
64         skb->ip_summed = ip_summed;
65 }
66
67 static inline void get_skb_csum_pointers(const struct sk_buff *skb,
68                                          u16 *csum_start, u16 *csum_offset)
69 {
70         *csum_start = skb->csum_start;
71         *csum_offset = skb->csum_offset;
72 }
73
74 static inline void set_skb_csum_pointers(struct sk_buff *skb, u16 csum_start,
75                                          u16 csum_offset)
76 {
77         skb->csum_start = csum_start;
78         skb->csum_offset = csum_offset;
79 }
80 #endif
81
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.
84  */
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,
89                                             int pseudohdr)
90 {
91         __be32 diff[] = { ~from, to };
92
93         if (get_ip_summed(skb) != OVS_CSUM_PARTIAL) {
94                 *sum = csum_fold(csum_partial((char *)diff, sizeof(diff),
95                                 ~csum_unfold(*sum)));
96                 if (get_ip_summed(skb) == OVS_CSUM_COMPLETE && pseudohdr)
97                         skb->csum = ~csum_partial((char *)diff, sizeof(diff),
98                                                 ~skb->csum);
99         } else if (pseudohdr)
100                 *sum = ~csum_fold(csum_partial((char *)diff, sizeof(diff),
101                                 csum_unfold(*sum)));
102 }
103 #endif
104
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,
108                                              struct sk_buff *skb,
109                                              const __be32 *from,
110                                              const __be32 *to,
111                                              int pseudohdr)
112 {
113         __be32 diff[] = {
114                 ~from[0], ~from[1], ~from[2], ~from[3],
115                 to[0], to[1], to[2], to[3],
116         };
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),
122                                                   ~skb->csum);
123         } else if (pseudohdr)
124                 *sum = ~csum_fold(csum_partial(diff, sizeof(diff),
125                                   csum_unfold(*sum)));
126 }
127 #endif
128
129 #ifdef NEED_CSUM_NORMALIZE
130 static inline void update_csum_start(struct sk_buff *skb, int delta)
131 {
132         if (get_ip_summed(skb) == OVS_CSUM_PARTIAL) {
133                 u16 csum_start, csum_offset;
134
135                 get_skb_csum_pointers(skb, &csum_start, &csum_offset);
136                 set_skb_csum_pointers(skb, csum_start + delta, csum_offset);
137         }
138 }
139
140 static inline int rpl_pskb_expand_head(struct sk_buff *skb, int nhead,
141                                        int ntail, gfp_t gfp_mask)
142 {
143         int err;
144         int old_headroom = skb_headroom(skb);
145
146         err = pskb_expand_head(skb, nhead, ntail, gfp_mask);
147         if (unlikely(err))
148                 return err;
149
150         update_csum_start(skb, skb_headroom(skb) - old_headroom);
151
152         return 0;
153 }
154 #define pskb_expand_head rpl_pskb_expand_head
155
156 static inline unsigned char *rpl__pskb_pull_tail(struct sk_buff *skb,
157                                                   int delta)
158 {
159         unsigned char *ret;
160         int old_headroom = skb_headroom(skb);
161
162         ret = __pskb_pull_tail(skb, delta);
163         if (unlikely(!ret))
164                 return ret;
165
166         update_csum_start(skb, skb_headroom(skb) - old_headroom);
167
168         return ret;
169 }
170 #define __pskb_pull_tail rpl__pskb_pull_tail
171 #endif
172
173 #endif /* checksum.h */