Prepare Open vSwitch 1.1.2 release.
[sliver-openvswitch.git] / datapath / checksum.h
1 /*
2  * Copyright (c) 2010 Nicira Networks.
3  * Distributed under the terms of the GNU GPL version 2.
4  *
5  * Significant portions of this file may be copied from parts of the Linux
6  * kernel, by Linus Torvalds and others.
7  */
8
9 #ifndef CHECKSUM_H
10 #define CHECKSUM_H 1
11
12 #include <linux/skbuff.h>
13 #include <linux/version.h>
14
15 #include <net/checksum.h>
16
17 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) || \
18         (defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID))
19 #define NEED_CSUM_NORMALIZE
20 #endif
21
22 /* These are the same values as the checksum constants in 2.6.22+. */
23 enum csum_type {
24         OVS_CSUM_NONE = 0,
25         OVS_CSUM_UNNECESSARY = 1,
26         OVS_CSUM_COMPLETE = 2,
27         OVS_CSUM_PARTIAL = 3,
28 };
29
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);
33 #else
34 static inline void compute_ip_summed(struct sk_buff *skb, bool xmit) { }
35 static inline u8 get_ip_summed(struct sk_buff *skb)
36 {
37         return skb->ip_summed;
38 }
39 #endif
40
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
44  * behavior.
45  */
46 static inline void forward_ip_summed(struct sk_buff *skb)
47 {
48 #ifdef CHECKSUM_HW
49         if (get_ip_summed(skb) == OVS_CSUM_COMPLETE)
50                 skb->ip_summed = CHECKSUM_NONE;
51 #endif
52 }
53
54 #if defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID)
55 int vswitch_skb_checksum_setup(struct sk_buff *skb);
56 #else
57 static inline int vswitch_skb_checksum_setup(struct sk_buff *skb)
58 {
59         return 0;
60 }
61 #endif
62
63 static inline void set_skb_csum_bits(const struct sk_buff *old_skb,
64                                      struct sk_buff *new_skb)
65 {
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;
71 #endif
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;
78 #endif
79 }
80
81 static inline void get_skb_csum_pointers(const struct sk_buff *skb,
82                                          u16 *csum_start, u16 *csum_offset)
83 {
84 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
85         *csum_start = skb->csum_start;
86         *csum_offset = skb->csum_offset;
87 #else
88         *csum_start = skb_headroom(skb) + skb_transport_offset(skb);
89         *csum_offset = skb->csum;
90 #endif
91 }
92
93 static inline void set_skb_csum_pointers(struct sk_buff *skb, u16 csum_start,
94                                          u16 csum_offset)
95 {
96 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
97         skb->csum_start = csum_start;
98         skb->csum_offset = csum_offset;
99 #else
100         skb_set_transport_header(skb, csum_start - skb_headroom(skb));
101         skb->csum = csum_offset;
102 #endif
103 }
104
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.
108  */
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,
112                                             int pseudohdr)
113 {
114         __be32 diff[] = { ~from, to };
115
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),
121                                                 ~skb->csum);
122         } else if (pseudohdr)
123                 *sum = ~csum_fold(csum_partial((char *)diff, sizeof(diff),
124                                 csum_unfold(*sum)));
125 }
126 #endif
127
128 #endif /* checksum.h */