tunneling: Add UDP checksum support for userspace tunnels.
authorJesse Gross <jesse@nicira.com>
Thu, 26 Mar 2015 21:27:19 +0000 (14:27 -0700)
committerJesse Gross <jesse@nicira.com>
Tue, 7 Apr 2015 23:51:57 +0000 (16:51 -0700)
Kernel based OVS recently added the ability to support checksums
for UDP based tunnels (Geneve and VXLAN). This adds similar support
for the userspace datapath to bring feature parity.

Signed-off-by: Jesse Gross <jesse@nicira.com>
Acked-by: Pravin B Shelar <pshelar@nicira.com>
lib/netdev-vport.c
lib/odp-util.c
tests/odp.at
tests/tunnel-push-pop.at

index ed407dc..23483e3 100644 (file)
@@ -891,6 +891,18 @@ udp_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl)
         return NULL;
     }
 
+    if (udp->udp_csum) {
+        uint32_t csum = packet_csum_pseudoheader(dp_packet_l3(packet));
+
+        csum = csum_continue(csum, udp, dp_packet_size(packet) -
+                             ((const unsigned char *)udp -
+                              (const unsigned char *)dp_packet_l2(packet)));
+        if (csum_finish(csum)) {
+            return NULL;
+        }
+        tnl->flags |= FLOW_TNL_F_CSUM;
+    }
+
     tnl->tp_src = udp->udp_src;
     tnl->tp_dst = udp->udp_dst;
 
@@ -919,13 +931,25 @@ push_udp_header(struct dp_packet *packet, const void *header, int size)
     /* set udp src port */
     udp->udp_src = get_src_port(packet);
     udp->udp_len = htons(ip_tot_size - sizeof (struct ip_header));
-    /* udp_csum is zero */
+
+    if (udp->udp_csum) {
+        uint32_t csum = packet_csum_pseudoheader(ip_hdr(dp_packet_data(packet)));
+
+        csum = csum_continue(csum, udp,
+                             ip_tot_size - sizeof (struct ip_header));
+        udp->udp_csum = csum_finish(csum);
+
+        if (!udp->udp_csum) {
+            udp->udp_csum = htons(0xffff);
+        }
+    }
 
     return udp + 1;
 }
 
 static void *
 udp_build_header(struct netdev_tunnel_config *tnl_cfg,
+                 const struct flow *tnl_flow,
                  struct ovs_action_push_tnl *data)
 {
     struct ip_header *ip;
@@ -937,6 +961,13 @@ udp_build_header(struct netdev_tunnel_config *tnl_cfg,
     udp = (struct udp_header *) (ip + 1);
     udp->udp_dst = tnl_cfg->dst_port;
 
+    if (tnl_flow->tunnel.flags & FLOW_TNL_F_CSUM) {
+        /* Write a value in now to mark that we should compute the checksum
+         * later. 0xffff is handy because it is transparent to the
+         * calculation. */
+        udp->udp_csum = htons(0xffff);
+    }
+
     return udp + 1;
 }
 
@@ -1183,7 +1214,7 @@ netdev_vxlan_build_header(const struct netdev *netdev,
     ovs_mutex_lock(&dev->mutex);
     tnl_cfg = &dev->tnl_cfg;
 
-    vxh = udp_build_header(tnl_cfg, data);
+    vxh = udp_build_header(tnl_cfg, tnl_flow, data);
 
     put_16aligned_be32(&vxh->vx_flags, htonl(VXLAN_FLAGS));
     put_16aligned_be32(&vxh->vx_vni, htonl(ntohll(tnl_flow->tunnel.tun_id) << 8));
@@ -1288,7 +1319,7 @@ netdev_geneve_build_header(const struct netdev *netdev,
     ovs_mutex_lock(&dev->mutex);
     tnl_cfg = &dev->tnl_cfg;
 
-    gnh = udp_build_header(tnl_cfg, data);
+    gnh = udp_build_header(tnl_cfg, tnl_flow, data);
 
     gnh->oam = !!(tnl_flow->tunnel.flags & FLOW_TNL_F_OAM);
     gnh->proto_type = htons(ETH_TYPE_TEB);
index 4e6a06d..7725a09 100644 (file)
@@ -516,8 +516,9 @@ format_udp_tnl_push_header(struct ds *ds, const struct ip_header *ip)
     const struct udp_header *udp;
 
     udp = (const struct udp_header *) (ip + 1);
-    ds_put_format(ds, "udp(src=%"PRIu16",dst=%"PRIu16"),",
-                  ntohs(udp->udp_src), ntohs(udp->udp_dst));
+    ds_put_format(ds, "udp(src=%"PRIu16",dst=%"PRIu16",csum=0x%"PRIx16"),",
+                  ntohs(udp->udp_src), ntohs(udp->udp_dst),
+                  ntohs(udp->udp_csum));
 
     return udp + 1;
 }
@@ -854,7 +855,7 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
     struct ip_header *ip;
     struct udp_header *udp;
     struct gre_base_hdr *greh;
-    uint16_t gre_proto, dl_type, udp_src, udp_dst;
+    uint16_t gre_proto, dl_type, udp_src, udp_dst, csum;
     ovs_be32 sip, dip;
     uint32_t tnl_type = 0, header_len = 0;
     void *l3, *l4;
@@ -899,14 +900,14 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
     /* Tunnel header */
     udp = (struct udp_header *) l4;
     greh = (struct gre_base_hdr *) l4;
-    if (ovs_scan_len(s, &n, "udp(src=%"SCNi16",dst=%"SCNi16"),",
-                         &udp_src, &udp_dst)) {
+    if (ovs_scan_len(s, &n, "udp(src=%"SCNi16",dst=%"SCNi16",csum=0x%"SCNx16"),",
+                         &udp_src, &udp_dst, &csum)) {
         uint32_t vx_flags, vni;
 
         udp->udp_src = htons(udp_src);
         udp->udp_dst = htons(udp_dst);
         udp->udp_len = 0;
-        udp->udp_csum = 0;
+        udp->udp_csum = htons(csum);
 
         if (ovs_scan_len(s, &n, "vxlan(flags=0x%"SCNx32",vni=0x%"SCNx32"))",
                             &vx_flags, &vni)) {
@@ -942,8 +943,6 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
         ovs_16aligned_be32 *options = (ovs_16aligned_be32 *) (greh + 1);
 
         if (greh->flags & htons(GRE_CSUM)) {
-            uint16_t csum;
-
             if (!ovs_scan_len(s, &n, ",csum=0x%"SCNx16, &csum)) {
                 return -EINVAL;
             }
index 7fda449..d615891 100644 (file)
@@ -281,8 +281,9 @@ set(tunnel(tun_id=0xabcdef1234567890,src=1.1.1.1,dst=2.2.2.2,tos=0,ttl=64,tp_src
 tnl_pop(4)
 tnl_push(tnl_port(4),header(size=42,type=3,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=47,tos=0,ttl=64,frag=0x40),gre((flags=0x20,proto=0x6558),key=0x1e241)),out_port(1))
 tnl_push(tnl_port(4),header(size=46,type=3,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=47,tos=0,ttl=64,frag=0x40),gre((flags=0xa0,proto=0x6558),csum=0x0,key=0x1e241)),out_port(1))
-tnl_push(tnl_port(6),header(size=50,type=4,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4789),vxlan(flags=0x8000000,vni=0x1c7)),out_port(1))
-tnl_push(tnl_port(6),header(size=50,type=5,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=6081),geneve(oam,vni=0x1c7)),out_port(1))
+tnl_push(tnl_port(6),header(size=50,type=4,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0x8000000,vni=0x1c7)),out_port(1))
+tnl_push(tnl_port(6),header(size=50,type=5,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=6081,csum=0x0),geneve(oam,vni=0x1c7)),out_port(1))
+tnl_push(tnl_port(6),header(size=50,type=5,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=6081,csum=0xffff),geneve(vni=0x1c7)),out_port(1))
 ])
 AT_CHECK_UNQUOTED([ovstest test-odp parse-actions < actions.txt], [0],
   [`cat actions.txt`
index 77dde69..72216ef 100644 (file)
@@ -9,7 +9,7 @@ AT_CHECK([ovs-vsctl add-port int-br t2 -- set Interface t2 type=vxlan \
                     -- add-port int-br t1 -- set Interface t1 type=gre \
                        options:remote_ip=1.1.2.92 options:key=456 ofport_request=3\
                     -- add-port int-br t3 -- set Interface t3 type=vxlan \
-                       options:remote_ip=1.1.2.93 options:out_key=flow ofport_request=4\
+                       options:remote_ip=1.1.2.93 options:out_key=flow options:csum=true ofport_request=4\
                     -- add-port int-br t4 -- set Interface t4 type=geneve \
                        options:remote_ip=1.1.2.92 options:key=123 ofport_request=5\
                        ], [0])
@@ -23,7 +23,7 @@ dummy@ovs-dummy: hit:0 missed:0
                int-br 65534/2: (dummy)
                t1 3/3: (gre: key=456, remote_ip=1.1.2.92)
                t2 2/4789: (vxlan: key=123, remote_ip=1.1.2.92)
-               t3 4/4789: (vxlan: out_key=flow, remote_ip=1.1.2.93)
+               t3 4/4789: (vxlan: csum=true, out_key=flow, remote_ip=1.1.2.93)
                t4 5/6081: (geneve: key=123, remote_ip=1.1.2.92)
 ])
 
@@ -67,14 +67,14 @@ dnl Check VXLAN tunnel push
 AT_CHECK([ovs-ofctl add-flow int-br action=2])
 AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
-  [Datapath actions: tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4789),vxlan(flags=0x8000000,vni=0x7b)),out_port(100))
+  [Datapath actions: tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0x8000000,vni=0x7b)),out_port(100))
 ])
 
-dnl Check VXLAN tunnel push set tunnel id by flow
+dnl Check VXLAN tunnel push set tunnel id by flow and checksum
 AT_CHECK([ovs-ofctl add-flow int-br "actions=set_tunnel:124,4"])
 AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
-  [Datapath actions: tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=f8:bc:12:44:34:b7,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.93,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4789),vxlan(flags=0x8000000,vni=0x7c)),out_port(100))
+  [Datapath actions: tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=f8:bc:12:44:34:b7,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.93,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4789,csum=0xffff),vxlan(flags=0x8000000,vni=0x7c)),out_port(100))
 ])
 
 dnl Check GRE tunnel push