ofp-util: Work on decoding OF1.1 flow_mods.
[openvswitch] / 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 #ifdef NEED_CSUM_NORMALIZE
106 static inline void update_csum_start(struct sk_buff *skb, int delta)
107 {
108         if (get_ip_summed(skb) == OVS_CSUM_PARTIAL) {
109                 u16 csum_start, csum_offset;
110
111                 get_skb_csum_pointers(skb, &csum_start, &csum_offset);
112                 set_skb_csum_pointers(skb, csum_start + delta, csum_offset);
113         }
114 }
115
116 static inline int rpl_pskb_expand_head(struct sk_buff *skb, int nhead,
117                                        int ntail, gfp_t gfp_mask)
118 {
119         int err;
120         int old_headroom = skb_headroom(skb);
121
122         err = pskb_expand_head(skb, nhead, ntail, gfp_mask);
123         if (unlikely(err))
124                 return err;
125
126         update_csum_start(skb, skb_headroom(skb) - old_headroom);
127
128         return 0;
129 }
130 #define pskb_expand_head rpl_pskb_expand_head
131
132 static inline unsigned char *rpl__pskb_pull_tail(struct sk_buff *skb,
133                                                   int delta)
134 {
135         unsigned char *ret;
136         int old_headroom = skb_headroom(skb);
137
138         ret = __pskb_pull_tail(skb, delta);
139         if (unlikely(!ret))
140                 return ret;
141
142         update_csum_start(skb, skb_headroom(skb) - old_headroom);
143
144         return ret;
145 }
146 #define __pskb_pull_tail rpl__pskb_pull_tail
147 #endif
148
149 #endif /* checksum.h */