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 <je...@nicira.com> --- lib/netdev-vport.c | 37 ++++++++++++++++++++++++++++++++++--- lib/odp-util.c | 15 +++++++-------- tests/odp.at | 5 +++-- tests/tunnel-push-pop.at | 10 +++++----- 4 files changed, 49 insertions(+), 18 deletions(-) diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c index ef96862..d39d449 100644 --- a/lib/netdev-vport.c +++ b/lib/netdev-vport.c @@ -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; } @@ -1182,7 +1213,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)); @@ -1286,7 +1317,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); diff --git a/lib/odp-util.c b/lib/odp-util.c index 47dfc92..3e6a7bd 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -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; } diff --git a/tests/odp.at b/tests/odp.at index 7fda449..d615891 100644 --- a/tests/odp.at +++ b/tests/odp.at @@ -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` diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at index effe543..1e530ba 100644 --- a/tests/tunnel-push-pop.at +++ b/tests/tunnel-push-pop.at @@ -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 -- 1.9.1 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev