+ return htons((((uint64_t) hash * (tnl_udp_port_max - tnl_udp_port_min)) >> 32) +
+ tnl_udp_port_min);
+}
+
+static void
+push_udp_header(struct dp_packet *packet,
+ const struct ovs_action_push_tnl *data)
+{
+ struct udp_header *udp;
+ int ip_tot_size;
+
+ udp = push_ip_header(packet, data->header, data->header_len, &ip_tot_size);
+
+ /* set udp src port */
+ udp->udp_src = get_src_port(packet);
+ udp->udp_len = htons(ip_tot_size);
+
+ if (udp->udp_csum) {
+ uint32_t csum;
+ if (is_header_ipv6(dp_packet_data(packet))) {
+ csum = packet_csum_pseudoheader6(ipv6_hdr(dp_packet_data(packet)));
+ } else {
+ csum = packet_csum_pseudoheader(ip_hdr(dp_packet_data(packet)));
+ }
+
+ csum = csum_continue(csum, udp, ip_tot_size);
+ udp->udp_csum = csum_finish(csum);
+
+ if (!udp->udp_csum) {
+ udp->udp_csum = htons(0xffff);
+ }
+ }
+}
+
+static void *
+udp_build_header(struct netdev_tunnel_config *tnl_cfg,
+ const struct flow *tnl_flow,
+ struct ovs_action_push_tnl *data,
+ unsigned int *hlen)
+{
+ struct ip_header *ip;
+ struct ovs_16aligned_ip6_hdr *ip6;
+ struct udp_header *udp;
+ bool is_ipv6;
+
+ *hlen = sizeof(struct eth_header);
+
+ is_ipv6 = is_header_ipv6(data->header);
+
+ if (is_ipv6) {
+ ip6 = ipv6_hdr(data->header);
+ ip6->ip6_nxt = IPPROTO_UDP;
+ udp = (struct udp_header *) (ip6 + 1);
+ *hlen += IPV6_HEADER_LEN;
+ } else {
+ ip = ip_hdr(data->header);
+ ip->ip_proto = IPPROTO_UDP;
+ udp = (struct udp_header *) (ip + 1);
+ *hlen += IP_HEADER_LEN;
+ }
+
+ udp->udp_dst = tnl_cfg->dst_port;
+
+ if (is_ipv6 || 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;