X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=datapath%2Fdatapath.c;h=539859a40c908a2698d12fa9970e70f484809ac6;hb=8cdaca99b5742489c35b1cac6e39791a47451952;hp=4c397670899ee32beae5b86bb8a9eb7f1e3e7a2e;hpb=de3f65ea52d60d70b104d852dbc752cb48ccf8f1;p=openvswitch diff --git a/datapath/datapath.c b/datapath/datapath.c index 4c397670..539859a4 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2008, 2009 Nicira Networks. + * Copyright (c) 2007, 2008, 2009, 2010 Nicira Networks. * Distributed under the terms of the GNU GPL version 2. * * Significant portions of this file may be copied from parts of the Linux @@ -574,10 +574,11 @@ static int dp_frame_hook(struct net_bridge_port *p, struct sk_buff **pskb) #error #endif -#if defined(CONFIG_XEN) && LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18) -/* This code is copied verbatim from net/dev/core.c in Xen's - * linux-2.6.18-92.1.10.el5.xs5.0.0.394.644. We can't call those functions - * directly because they aren't exported. */ +#if defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID) +/* This code is based on a skb_checksum_setup from net/dev/core.c from a + * combination of Lenny's 2.6.26 Xen kernel and Xen's + * linux-2.6.18-92.1.10.el5.xs5.0.0.394.644. We can't call this function + * directly because it isn't exported in all versions. */ static int skb_pull_up_to(struct sk_buff *skb, void *ptr) { if (ptr < (void *)skb->tail) @@ -592,39 +593,118 @@ static int skb_pull_up_to(struct sk_buff *skb, void *ptr) int vswitch_skb_checksum_setup(struct sk_buff *skb) { - if (skb->proto_csum_blank) { - if (skb->protocol != htons(ETH_P_IP)) - goto out; - if (!skb_pull_up_to(skb, skb->nh.iph + 1)) - goto out; - skb->h.raw = (unsigned char *)skb->nh.iph + 4*skb->nh.iph->ihl; - switch (skb->nh.iph->protocol) { - case IPPROTO_TCP: - skb->csum = offsetof(struct tcphdr, check); - break; - case IPPROTO_UDP: - skb->csum = offsetof(struct udphdr, check); - break; - default: - if (net_ratelimit()) - printk(KERN_ERR "Attempting to checksum a non-" - "TCP/UDP packet, dropping a protocol" - " %d packet", skb->nh.iph->protocol); - goto out; - } - if (!skb_pull_up_to(skb, skb->h.raw + skb->csum + 2)) - goto out; - skb->ip_summed = CHECKSUM_HW; - skb->proto_csum_blank = 0; + struct iphdr *iph; + unsigned char *th; + int err = -EPROTO; + __u16 csum_start, csum_offset; + + if (!skb->proto_csum_blank) + return 0; + + if (skb->protocol != htons(ETH_P_IP)) + goto out; + + if (!skb_pull_up_to(skb, skb_network_header(skb) + 1)) + goto out; + + iph = ip_hdr(skb); + th = skb_network_header(skb) + 4 * iph->ihl; + + csum_start = th - skb->head; + switch (iph->protocol) { + case IPPROTO_TCP: + csum_offset = offsetof(struct tcphdr, check); + break; + case IPPROTO_UDP: + csum_offset = offsetof(struct udphdr, check); + break; + default: + if (net_ratelimit()) + printk(KERN_ERR "Attempting to checksum a non-" + "TCP/UDP packet, dropping a protocol" + " %d packet", iph->protocol); + goto out; } - return 0; + + if (!skb_pull_up_to(skb, th + csum_offset + 2)) + goto out; + + skb->ip_summed = CHECKSUM_PARTIAL; + skb->proto_csum_blank = 0; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) + skb->csum_start = csum_start; + skb->csum_offset = csum_offset; +#else + skb_set_transport_header(skb, csum_start - skb_headroom(skb)); + skb->csum = csum_offset; +#endif + + err = 0; + out: - return -EPROTO; + return err; } #else int vswitch_skb_checksum_setup(struct sk_buff *skb) { return 0; } #endif /* CONFIG_XEN && linux == 2.6.18 */ + /* Types of checksums that we can receive (these all refer to L4 checksums): + * 1. CHECKSUM_NONE: Device that did not compute checksum, contains full + * (though not verified) checksum in packet but not in skb->csum. Packets + * from the bridge local port will also have this type. + * 2. CHECKSUM_COMPLETE (CHECKSUM_HW): Good device that computes checksums, + * also the GRE module. This is the same as CHECKSUM_NONE, except it has + * a valid skb->csum. Importantly, both contain a full checksum (not + * verified) in the packet itself. The only difference is that if the + * packet gets to L4 processing on this machine (not in DomU) we won't + * have to recompute the checksum to verify. Most hardware devices do not + * produce packets with this type, even if they support receive checksum + * offloading (they produce type #5). + * 3. CHECKSUM_PARTIAL (CHECKSUM_HW): Packet without full checksum and needs to + * be computed if it is sent off box. Unfortunately on earlier kernels, + * this case is impossible to distinguish from #2, despite having opposite + * meanings. Xen adds an extra field on earlier kernels (see #4) in order + * to distinguish the different states. The only real user of this type + * with bridging is Xen (on later kernels). + * 4. CHECKSUM_UNNECESSARY (with proto_csum_blank true): This packet was + * generated locally by a Xen DomU and has a partial checksum. If it is + * handled on this machine (Dom0 or DomU), then the checksum will not be + * computed. If it goes off box, the checksum in the packet needs to + * completed. Calling skb_checksum_setup converts this to CHECKSUM_HW + * (CHECKSUM_PARTIAL) so that the checksum can be completed. In later + * kernels, this combination is replaced with CHECKSUM_PARTIAL. + * 5. CHECKSUM_UNNECESSARY (with proto_csum_blank false): Packet with a correct + * full checksum or using a protocol without a checksum. skb->csum is + * undefined. This is common from devices with receive checksum + * offloading. This is somewhat similar to CHECKSUM_NONE, except that + * nobody will try to verify the checksum with CHECKSUM_UNNECESSARY. + * + * Note that on earlier kernels, CHECKSUM_COMPLETE and CHECKSUM_PARTIAL are + * both defined as CHECKSUM_HW. Normally the meaning of CHECKSUM_HW is clear + * based on whether it is on the transmit or receive path. After the datapath + * it will be intepreted as CHECKSUM_PARTIAL. If the packet already has a + * checksum, we will panic. Since we can receive packets with checksums, we + * assume that all CHECKSUM_HW packets have checksums and map them to + * CHECKSUM_NONE, which has a similar meaning (the it is only different if the + * packet is processed by the local IP stack, in which case it will need to + * be reverified). If we receive a packet with CHECKSUM_HW that really means + * CHECKSUM_PARTIAL, it will be sent with the wrong checksum. However, there + * shouldn't be any devices that do this with bridging. + * + * The bridge has similar behavior and this function closely resembles + * skb_forward_csum(). It is slightly different because we are only concerned + * with bridging and not other types of forwarding and can get away with + * slightly more optimal behavior.*/ +void +forward_ip_summed(struct sk_buff *skb) +{ +#ifdef CHECKSUM_HW + if (skb->ip_summed == CHECKSUM_HW) + skb->ip_summed = CHECKSUM_NONE; +#endif +} + /* Append each packet in 'skb' list to 'queue'. There will be only one packet * unless we broke up a GSO packet. */ static int @@ -722,6 +802,8 @@ dp_output_control(struct datapath *dp, struct sk_buff *skb, int queue_no, if (skb_queue_len(queue) >= DP_MAX_QUEUE_LEN) goto err_kfree_skb; + forward_ip_summed(skb); + /* Break apart GSO packets into their component pieces. Otherwise * userspace may try to stuff a 64kB packet into a 1500-byte MTU. */ if (skb_is_gso(skb)) {