1d21c4ed5d488052a592d2f15c45aebe91d6590c
[openvswitch] / datapath / checksum.c
1 /*
2  * Copyright (c) 2010, 2011 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 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10
11 #include <linux/in.h>
12 #include <linux/ip.h>
13 #include <linux/tcp.h>
14 #include <linux/udp.h>
15
16 #include "checksum.h"
17 #include "datapath.h"
18
19 #ifdef NEED_CSUM_NORMALIZE
20
21 #if defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID)
22 /* This code is based on skb_checksum_setup() from Xen's net/dev/core.c.  We
23  * can't call this function directly because it isn't exported in all
24  * versions. */
25 static int vswitch_skb_checksum_setup(struct sk_buff *skb)
26 {
27         struct iphdr *iph;
28         unsigned char *th;
29         int err = -EPROTO;
30         __u16 csum_start, csum_offset;
31
32         if (!skb->proto_csum_blank)
33                 return 0;
34
35         if (skb->protocol != htons(ETH_P_IP))
36                 goto out;
37
38         if (!pskb_may_pull(skb, skb_network_header(skb) + sizeof(struct iphdr) - skb->data))
39                 goto out;
40
41         iph = ip_hdr(skb);
42         th = skb_network_header(skb) + 4 * iph->ihl;
43
44         csum_start = th - skb->head;
45         switch (iph->protocol) {
46         case IPPROTO_TCP:
47                 csum_offset = offsetof(struct tcphdr, check);
48                 break;
49         case IPPROTO_UDP:
50                 csum_offset = offsetof(struct udphdr, check);
51                 break;
52         default:
53                 if (net_ratelimit())
54                         pr_err("Attempting to checksum a non-TCP/UDP packet, "
55                                "dropping a protocol %d packet",
56                                iph->protocol);
57                 goto out;
58         }
59
60         if (!pskb_may_pull(skb, th + csum_offset + 2 - skb->data))
61                 goto out;
62
63         skb->proto_csum_blank = 0;
64         set_ip_summed(skb, OVS_CSUM_PARTIAL);
65         set_skb_csum_pointers(skb, csum_start, csum_offset);
66
67         err = 0;
68
69 out:
70         return err;
71 }
72 #else
73 static int vswitch_skb_checksum_setup(struct sk_buff *skb)
74 {
75         return 0;
76 }
77 #endif /* not Xen old style checksums */
78
79 /*
80  *      compute_ip_summed - map external checksum state onto OVS representation
81  *
82  * @skb: Packet to manipulate.
83  * @xmit: Whether we were on transmit path of network stack.  For example,
84  *        this is true for the internal dev vport because it receives skbs
85  *        that passed through dev_queue_xmit() but false for the netdev vport
86  *        because its packets come from netif_receive_skb().
87  *
88  * Older kernels (and various versions of Xen) were not explicit enough about
89  * checksum offload parameters and rely on a combination of context and
90  * non standard fields.  This deals with all those variations so that we
91  * can internally manipulate checksum offloads without worrying about kernel
92  * version.
93  *
94  * Types of checksums that we can receive (these all refer to L4 checksums):
95  * 1. CHECKSUM_NONE: Device that did not compute checksum, contains full
96  *      (though not verified) checksum in packet but not in skb->csum.  Packets
97  *      from the bridge local port will also have this type.
98  * 2. CHECKSUM_COMPLETE (CHECKSUM_HW): Good device that computes checksums,
99  *      also the GRE module.  This is the same as CHECKSUM_NONE, except it has
100  *      a valid skb->csum.  Importantly, both contain a full checksum (not
101  *      verified) in the packet itself.  The only difference is that if the
102  *      packet gets to L4 processing on this machine (not in DomU) we won't
103  *      have to recompute the checksum to verify.  Most hardware devices do not
104  *      produce packets with this type, even if they support receive checksum
105  *      offloading (they produce type #5).
106  * 3. CHECKSUM_PARTIAL (CHECKSUM_HW): Packet without full checksum and needs to
107  *      be computed if it is sent off box.  Unfortunately on earlier kernels,
108  *      this case is impossible to distinguish from #2, despite having opposite
109  *      meanings.  Xen adds an extra field on earlier kernels (see #4) in order
110  *      to distinguish the different states.
111  * 4. CHECKSUM_UNNECESSARY (with proto_csum_blank true): This packet was
112  *      generated locally by a Xen DomU and has a partial checksum.  If it is
113  *      handled on this machine (Dom0 or DomU), then the checksum will not be
114  *      computed.  If it goes off box, the checksum in the packet needs to be
115  *      completed.  Calling skb_checksum_setup converts this to CHECKSUM_HW
116  *      (CHECKSUM_PARTIAL) so that the checksum can be completed.  In later
117  *      kernels, this combination is replaced with CHECKSUM_PARTIAL.
118  * 5. CHECKSUM_UNNECESSARY (with proto_csum_blank false): Packet with a correct
119  *      full checksum or using a protocol without a checksum.  skb->csum is
120  *      undefined.  This is common from devices with receive checksum
121  *      offloading.  This is somewhat similar to CHECKSUM_NONE, except that
122  *      nobody will try to verify the checksum with CHECKSUM_UNNECESSARY.
123  *
124  * Note that on earlier kernels, CHECKSUM_COMPLETE and CHECKSUM_PARTIAL are
125  * both defined as CHECKSUM_HW.  Normally the meaning of CHECKSUM_HW is clear
126  * based on whether it is on the transmit or receive path.  After the datapath
127  * it will be intepreted as CHECKSUM_PARTIAL.  If the packet already has a
128  * checksum, we will panic.  Since we can receive packets with checksums, we
129  * assume that all CHECKSUM_HW packets have checksums and map them to
130  * CHECKSUM_NONE, which has a similar meaning (the it is only different if the
131  * packet is processed by the local IP stack, in which case it will need to
132  * be reverified).  If we receive a packet with CHECKSUM_HW that really means
133  * CHECKSUM_PARTIAL, it will be sent with the wrong checksum.  However, there
134  * shouldn't be any devices that do this with bridging.
135  */
136 int compute_ip_summed(struct sk_buff *skb, bool xmit)
137 {
138         /* For our convenience these defines change repeatedly between kernel
139          * versions, so we can't just copy them over...
140          */
141         switch (skb->ip_summed) {
142         case CHECKSUM_NONE:
143                 set_ip_summed(skb, OVS_CSUM_NONE);
144                 break;
145         case CHECKSUM_UNNECESSARY:
146                 set_ip_summed(skb, OVS_CSUM_UNNECESSARY);
147                 break;
148 #ifdef CHECKSUM_HW
149         /* In theory this could be either CHECKSUM_PARTIAL or CHECKSUM_COMPLETE.
150          * However, on the receive side we should only get CHECKSUM_PARTIAL
151          * packets from Xen, which uses some special fields to represent this
152          * (see vswitch_skb_checksum_setup()).  Since we can only make one type
153          * work, pick the one that actually happens in practice.
154          *
155          * On the transmit side (basically after skb_checksum_setup()
156          * has been run or on internal dev transmit), packets with
157          * CHECKSUM_COMPLETE aren't generated, so assume CHECKSUM_PARTIAL.
158          */
159         case CHECKSUM_HW:
160                 if (!xmit)
161                         set_ip_summed(skb, OVS_CSUM_COMPLETE);
162                 else
163                         set_ip_summed(skb, OVS_CSUM_PARTIAL);
164                 break;
165 #else
166         case CHECKSUM_COMPLETE:
167                 set_ip_summed(skb, OVS_CSUM_COMPLETE);
168                 break;
169         case CHECKSUM_PARTIAL:
170                 set_ip_summed(skb, OVS_CSUM_PARTIAL);
171                 break;
172 #endif
173         }
174
175         OVS_CB(skb)->csum_start = skb_headroom(skb) + skb_transport_offset(skb);
176
177         return vswitch_skb_checksum_setup(skb);
178 }
179
180 /*
181  *     forward_ip_summed - map internal checksum state back onto native
182  *                         kernel fields.
183  *
184  * @skb: Packet to manipulate.
185  * @xmit: Whether we are about send on the transmit path the network stack.
186  *        This follows the same logic as the @xmit field in compute_ip_summed().
187  *        Generally, a given vport will have opposite values for @xmit passed to
188  *        these two functions.
189  *
190  * When a packet is about to egress from OVS take our internal fields (including
191  * any modifications we have made) and recreate the correct representation for
192  * this kernel.  This may do things like change the transport header offset.
193  */
194 void forward_ip_summed(struct sk_buff *skb, bool xmit)
195 {
196         switch (get_ip_summed(skb)) {
197         case OVS_CSUM_NONE:
198                 skb->ip_summed = CHECKSUM_NONE;
199                 break;
200         case OVS_CSUM_UNNECESSARY:
201                 skb->ip_summed = CHECKSUM_UNNECESSARY;
202 #if defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID)
203                 skb->proto_data_valid = 1;
204 #endif
205                 break;
206 #ifdef CHECKSUM_HW
207         case OVS_CSUM_COMPLETE:
208                 if (!xmit)
209                         skb->ip_summed = CHECKSUM_HW;
210                 else
211                         skb->ip_summed = CHECKSUM_NONE;
212                 break;
213         case OVS_CSUM_PARTIAL:
214                 if (!xmit) {
215                         skb->ip_summed = CHECKSUM_UNNECESSARY;
216 #if defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID)
217                         skb->proto_csum_blank = 1;
218 #endif
219                 } else {
220                         skb->ip_summed = CHECKSUM_HW;
221                 }
222                 break;
223 #else
224         case OVS_CSUM_COMPLETE:
225                 skb->ip_summed = CHECKSUM_COMPLETE;
226                 break;
227         case OVS_CSUM_PARTIAL:
228                 skb->ip_summed = CHECKSUM_PARTIAL;
229                 break;
230 #endif
231         }
232
233         if (get_ip_summed(skb) == OVS_CSUM_PARTIAL)
234                 skb_set_transport_header(skb, OVS_CB(skb)->csum_start -
235                                               skb_headroom(skb));
236 }
237
238 u8 get_ip_summed(struct sk_buff *skb)
239 {
240         return OVS_CB(skb)->ip_summed;
241 }
242
243 void set_ip_summed(struct sk_buff *skb, u8 ip_summed)
244 {
245         OVS_CB(skb)->ip_summed = ip_summed;
246 }
247
248 void get_skb_csum_pointers(const struct sk_buff *skb, u16 *csum_start,
249                            u16 *csum_offset)
250 {
251         *csum_start = OVS_CB(skb)->csum_start;
252         *csum_offset = skb->csum;
253 }
254
255 void set_skb_csum_pointers(struct sk_buff *skb, u16 csum_start,
256                            u16 csum_offset)
257 {
258         OVS_CB(skb)->csum_start = csum_start;
259         skb->csum = csum_offset;
260 }
261 #endif /* NEED_CSUM_NORMALIZE */