162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci#include <linux/module.h>
362306a36Sopenharmony_ci#include <linux/errno.h>
462306a36Sopenharmony_ci#include <linux/socket.h>
562306a36Sopenharmony_ci#include <linux/udp.h>
662306a36Sopenharmony_ci#include <linux/types.h>
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci#include <linux/in6.h>
962306a36Sopenharmony_ci#include <net/udp.h>
1062306a36Sopenharmony_ci#include <net/udp_tunnel.h>
1162306a36Sopenharmony_ci#include <net/net_namespace.h>
1262306a36Sopenharmony_ci#include <net/netns/generic.h>
1362306a36Sopenharmony_ci#include <net/ip6_tunnel.h>
1462306a36Sopenharmony_ci#include <net/ip6_checksum.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ciint udp_sock_create6(struct net *net, struct udp_port_cfg *cfg,
1762306a36Sopenharmony_ci		     struct socket **sockp)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	struct sockaddr_in6 udp6_addr = {};
2062306a36Sopenharmony_ci	int err;
2162306a36Sopenharmony_ci	struct socket *sock = NULL;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	err = sock_create_kern(net, AF_INET6, SOCK_DGRAM, 0, &sock);
2462306a36Sopenharmony_ci	if (err < 0)
2562306a36Sopenharmony_ci		goto error;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	if (cfg->ipv6_v6only) {
2862306a36Sopenharmony_ci		err = ip6_sock_set_v6only(sock->sk);
2962306a36Sopenharmony_ci		if (err < 0)
3062306a36Sopenharmony_ci			goto error;
3162306a36Sopenharmony_ci	}
3262306a36Sopenharmony_ci	if (cfg->bind_ifindex) {
3362306a36Sopenharmony_ci		err = sock_bindtoindex(sock->sk, cfg->bind_ifindex, true);
3462306a36Sopenharmony_ci		if (err < 0)
3562306a36Sopenharmony_ci			goto error;
3662306a36Sopenharmony_ci	}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	udp6_addr.sin6_family = AF_INET6;
3962306a36Sopenharmony_ci	memcpy(&udp6_addr.sin6_addr, &cfg->local_ip6,
4062306a36Sopenharmony_ci	       sizeof(udp6_addr.sin6_addr));
4162306a36Sopenharmony_ci	udp6_addr.sin6_port = cfg->local_udp_port;
4262306a36Sopenharmony_ci	err = kernel_bind(sock, (struct sockaddr *)&udp6_addr,
4362306a36Sopenharmony_ci			  sizeof(udp6_addr));
4462306a36Sopenharmony_ci	if (err < 0)
4562306a36Sopenharmony_ci		goto error;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	if (cfg->peer_udp_port) {
4862306a36Sopenharmony_ci		memset(&udp6_addr, 0, sizeof(udp6_addr));
4962306a36Sopenharmony_ci		udp6_addr.sin6_family = AF_INET6;
5062306a36Sopenharmony_ci		memcpy(&udp6_addr.sin6_addr, &cfg->peer_ip6,
5162306a36Sopenharmony_ci		       sizeof(udp6_addr.sin6_addr));
5262306a36Sopenharmony_ci		udp6_addr.sin6_port = cfg->peer_udp_port;
5362306a36Sopenharmony_ci		err = kernel_connect(sock,
5462306a36Sopenharmony_ci				     (struct sockaddr *)&udp6_addr,
5562306a36Sopenharmony_ci				     sizeof(udp6_addr), 0);
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci	if (err < 0)
5862306a36Sopenharmony_ci		goto error;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	udp_set_no_check6_tx(sock->sk, !cfg->use_udp6_tx_checksums);
6162306a36Sopenharmony_ci	udp_set_no_check6_rx(sock->sk, !cfg->use_udp6_rx_checksums);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	*sockp = sock;
6462306a36Sopenharmony_ci	return 0;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cierror:
6762306a36Sopenharmony_ci	if (sock) {
6862306a36Sopenharmony_ci		kernel_sock_shutdown(sock, SHUT_RDWR);
6962306a36Sopenharmony_ci		sock_release(sock);
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci	*sockp = NULL;
7262306a36Sopenharmony_ci	return err;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(udp_sock_create6);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ciint udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sock *sk,
7762306a36Sopenharmony_ci			 struct sk_buff *skb,
7862306a36Sopenharmony_ci			 struct net_device *dev, struct in6_addr *saddr,
7962306a36Sopenharmony_ci			 struct in6_addr *daddr,
8062306a36Sopenharmony_ci			 __u8 prio, __u8 ttl, __be32 label,
8162306a36Sopenharmony_ci			 __be16 src_port, __be16 dst_port, bool nocheck)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	struct udphdr *uh;
8462306a36Sopenharmony_ci	struct ipv6hdr *ip6h;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	__skb_push(skb, sizeof(*uh));
8762306a36Sopenharmony_ci	skb_reset_transport_header(skb);
8862306a36Sopenharmony_ci	uh = udp_hdr(skb);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	uh->dest = dst_port;
9162306a36Sopenharmony_ci	uh->source = src_port;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	uh->len = htons(skb->len);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	skb_dst_set(skb, dst);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	udp6_set_csum(nocheck, skb, saddr, daddr, skb->len);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	__skb_push(skb, sizeof(*ip6h));
10062306a36Sopenharmony_ci	skb_reset_network_header(skb);
10162306a36Sopenharmony_ci	ip6h		  = ipv6_hdr(skb);
10262306a36Sopenharmony_ci	ip6_flow_hdr(ip6h, prio, label);
10362306a36Sopenharmony_ci	ip6h->payload_len = htons(skb->len);
10462306a36Sopenharmony_ci	ip6h->nexthdr     = IPPROTO_UDP;
10562306a36Sopenharmony_ci	ip6h->hop_limit   = ttl;
10662306a36Sopenharmony_ci	ip6h->daddr	  = *daddr;
10762306a36Sopenharmony_ci	ip6h->saddr	  = *saddr;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	ip6tunnel_xmit(sk, skb, dev);
11062306a36Sopenharmony_ci	return 0;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(udp_tunnel6_xmit_skb);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
115