]>
git.proxmox.com Git - mirror_ovs.git/blob - datapath/checksum.c
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
19 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
23 #include <linux/tcp.h>
24 #include <linux/udp.h>
29 #ifdef NEED_CSUM_NORMALIZE
31 #if defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID)
32 /* This code is based on skb_checksum_setup() from Xen's net/dev/core.c. We
33 * can't call this function directly because it isn't exported in all
35 static int vswitch_skb_checksum_setup(struct sk_buff
*skb
)
40 __u16 csum_start
, csum_offset
;
42 if (!skb
->proto_csum_blank
)
45 if (skb
->protocol
!= htons(ETH_P_IP
))
48 if (!pskb_may_pull(skb
, skb_network_header(skb
) + sizeof(struct iphdr
) - skb
->data
))
52 th
= skb_network_header(skb
) + 4 * iph
->ihl
;
54 csum_start
= th
- skb
->head
;
55 switch (iph
->protocol
) {
57 csum_offset
= offsetof(struct tcphdr
, check
);
60 csum_offset
= offsetof(struct udphdr
, check
);
64 pr_err("Attempting to checksum a non-TCP/UDP packet, "
65 "dropping a protocol %d packet",
70 if (!pskb_may_pull(skb
, th
+ csum_offset
+ 2 - skb
->data
))
73 skb
->proto_csum_blank
= 0;
74 set_ip_summed(skb
, OVS_CSUM_PARTIAL
);
75 set_skb_csum_pointers(skb
, csum_start
, csum_offset
);
83 static int vswitch_skb_checksum_setup(struct sk_buff
*skb
)
87 #endif /* not Xen old style checksums */
90 * compute_ip_summed - map external checksum state onto OVS representation
92 * @skb: Packet to manipulate.
93 * @xmit: Whether we were on transmit path of network stack. For example,
94 * this is true for the internal dev vport because it receives skbs
95 * that passed through dev_queue_xmit() but false for the netdev vport
96 * because its packets come from netif_receive_skb().
98 * Older kernels (and various versions of Xen) were not explicit enough about
99 * checksum offload parameters and rely on a combination of context and
100 * non standard fields. This deals with all those variations so that we
101 * can internally manipulate checksum offloads without worrying about kernel
104 * Types of checksums that we can receive (these all refer to L4 checksums):
105 * 1. CHECKSUM_NONE: Device that did not compute checksum, contains full
106 * (though not verified) checksum in packet but not in skb->csum. Packets
107 * from the bridge local port will also have this type.
108 * 2. CHECKSUM_COMPLETE (CHECKSUM_HW): Good device that computes checksums,
109 * also the GRE module. This is the same as CHECKSUM_NONE, except it has
110 * a valid skb->csum. Importantly, both contain a full checksum (not
111 * verified) in the packet itself. The only difference is that if the
112 * packet gets to L4 processing on this machine (not in DomU) we won't
113 * have to recompute the checksum to verify. Most hardware devices do not
114 * produce packets with this type, even if they support receive checksum
115 * offloading (they produce type #5).
116 * 3. CHECKSUM_PARTIAL (CHECKSUM_HW): Packet without full checksum and needs to
117 * be computed if it is sent off box. Unfortunately on earlier kernels,
118 * this case is impossible to distinguish from #2, despite having opposite
119 * meanings. Xen adds an extra field on earlier kernels (see #4) in order
120 * to distinguish the different states.
121 * 4. CHECKSUM_UNNECESSARY (with proto_csum_blank true): This packet was
122 * generated locally by a Xen DomU and has a partial checksum. If it is
123 * handled on this machine (Dom0 or DomU), then the checksum will not be
124 * computed. If it goes off box, the checksum in the packet needs to be
125 * completed. Calling skb_checksum_setup converts this to CHECKSUM_HW
126 * (CHECKSUM_PARTIAL) so that the checksum can be completed. In later
127 * kernels, this combination is replaced with CHECKSUM_PARTIAL.
128 * 5. CHECKSUM_UNNECESSARY (with proto_csum_blank false): Packet with a correct
129 * full checksum or using a protocol without a checksum. skb->csum is
130 * undefined. This is common from devices with receive checksum
131 * offloading. This is somewhat similar to CHECKSUM_NONE, except that
132 * nobody will try to verify the checksum with CHECKSUM_UNNECESSARY.
134 * Note that on earlier kernels, CHECKSUM_COMPLETE and CHECKSUM_PARTIAL are
135 * both defined as CHECKSUM_HW. Normally the meaning of CHECKSUM_HW is clear
136 * based on whether it is on the transmit or receive path. After the datapath
137 * it will be intepreted as CHECKSUM_PARTIAL. If the packet already has a
138 * checksum, we will panic. Since we can receive packets with checksums, we
139 * assume that all CHECKSUM_HW packets have checksums and map them to
140 * CHECKSUM_NONE, which has a similar meaning (the it is only different if the
141 * packet is processed by the local IP stack, in which case it will need to
142 * be reverified). If we receive a packet with CHECKSUM_HW that really means
143 * CHECKSUM_PARTIAL, it will be sent with the wrong checksum. However, there
144 * shouldn't be any devices that do this with bridging.
146 int compute_ip_summed(struct sk_buff
*skb
, bool xmit
)
148 /* For our convenience these defines change repeatedly between kernel
149 * versions, so we can't just copy them over...
151 switch (skb
->ip_summed
) {
153 set_ip_summed(skb
, OVS_CSUM_NONE
);
155 case CHECKSUM_UNNECESSARY
:
156 set_ip_summed(skb
, OVS_CSUM_UNNECESSARY
);
159 /* In theory this could be either CHECKSUM_PARTIAL or CHECKSUM_COMPLETE.
160 * However, on the receive side we should only get CHECKSUM_PARTIAL
161 * packets from Xen, which uses some special fields to represent this
162 * (see vswitch_skb_checksum_setup()). Since we can only make one type
163 * work, pick the one that actually happens in practice.
165 * On the transmit side (basically after skb_checksum_setup()
166 * has been run or on internal dev transmit), packets with
167 * CHECKSUM_COMPLETE aren't generated, so assume CHECKSUM_PARTIAL.
171 set_ip_summed(skb
, OVS_CSUM_COMPLETE
);
173 set_ip_summed(skb
, OVS_CSUM_PARTIAL
);
176 case CHECKSUM_COMPLETE
:
177 set_ip_summed(skb
, OVS_CSUM_COMPLETE
);
179 case CHECKSUM_PARTIAL
:
180 set_ip_summed(skb
, OVS_CSUM_PARTIAL
);
185 OVS_CB(skb
)->csum_start
= skb_headroom(skb
) + skb_transport_offset(skb
);
187 return vswitch_skb_checksum_setup(skb
);
191 * forward_ip_summed - map internal checksum state back onto native
194 * @skb: Packet to manipulate.
195 * @xmit: Whether we are about send on the transmit path the network stack.
196 * This follows the same logic as the @xmit field in compute_ip_summed().
197 * Generally, a given vport will have opposite values for @xmit passed to
198 * these two functions.
200 * When a packet is about to egress from OVS take our internal fields (including
201 * any modifications we have made) and recreate the correct representation for
202 * this kernel. This may do things like change the transport header offset.
204 void forward_ip_summed(struct sk_buff
*skb
, bool xmit
)
206 switch (get_ip_summed(skb
)) {
208 skb
->ip_summed
= CHECKSUM_NONE
;
210 case OVS_CSUM_UNNECESSARY
:
211 skb
->ip_summed
= CHECKSUM_UNNECESSARY
;
212 #if defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID)
213 skb
->proto_data_valid
= 1;
217 case OVS_CSUM_COMPLETE
:
219 skb
->ip_summed
= CHECKSUM_HW
;
221 skb
->ip_summed
= CHECKSUM_NONE
;
223 case OVS_CSUM_PARTIAL
:
225 skb
->ip_summed
= CHECKSUM_UNNECESSARY
;
226 #if defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID)
227 skb
->proto_csum_blank
= 1;
230 skb
->ip_summed
= CHECKSUM_HW
;
234 case OVS_CSUM_COMPLETE
:
235 skb
->ip_summed
= CHECKSUM_COMPLETE
;
237 case OVS_CSUM_PARTIAL
:
238 skb
->ip_summed
= CHECKSUM_PARTIAL
;
243 if (get_ip_summed(skb
) == OVS_CSUM_PARTIAL
)
244 skb_set_transport_header(skb
, OVS_CB(skb
)->csum_start
-
248 u8
get_ip_summed(struct sk_buff
*skb
)
250 return OVS_CB(skb
)->ip_summed
;
253 void set_ip_summed(struct sk_buff
*skb
, u8 ip_summed
)
255 OVS_CB(skb
)->ip_summed
= ip_summed
;
258 void get_skb_csum_pointers(const struct sk_buff
*skb
, u16
*csum_start
,
261 *csum_start
= OVS_CB(skb
)->csum_start
;
262 *csum_offset
= skb
->csum
;
265 void set_skb_csum_pointers(struct sk_buff
*skb
, u16 csum_start
,
268 OVS_CB(skb
)->csum_start
= csum_start
;
269 skb
->csum
= csum_offset
;
271 #endif /* NEED_CSUM_NORMALIZE */