18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci#include <linux/module.h>
38c2ecf20Sopenharmony_ci#include <linux/errno.h>
48c2ecf20Sopenharmony_ci#include <linux/socket.h>
58c2ecf20Sopenharmony_ci#include <linux/udp.h>
68c2ecf20Sopenharmony_ci#include <linux/types.h>
78c2ecf20Sopenharmony_ci#include <linux/kernel.h>
88c2ecf20Sopenharmony_ci#include <linux/in6.h>
98c2ecf20Sopenharmony_ci#include <net/udp.h>
108c2ecf20Sopenharmony_ci#include <net/udp_tunnel.h>
118c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
128c2ecf20Sopenharmony_ci#include <net/netns/generic.h>
138c2ecf20Sopenharmony_ci#include <net/ip6_tunnel.h>
148c2ecf20Sopenharmony_ci#include <net/ip6_checksum.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ciint udp_sock_create6(struct net *net, struct udp_port_cfg *cfg,
178c2ecf20Sopenharmony_ci		     struct socket **sockp)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	struct sockaddr_in6 udp6_addr = {};
208c2ecf20Sopenharmony_ci	int err;
218c2ecf20Sopenharmony_ci	struct socket *sock = NULL;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci	err = sock_create_kern(net, AF_INET6, SOCK_DGRAM, 0, &sock);
248c2ecf20Sopenharmony_ci	if (err < 0)
258c2ecf20Sopenharmony_ci		goto error;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	if (cfg->ipv6_v6only) {
288c2ecf20Sopenharmony_ci		err = ip6_sock_set_v6only(sock->sk);
298c2ecf20Sopenharmony_ci		if (err < 0)
308c2ecf20Sopenharmony_ci			goto error;
318c2ecf20Sopenharmony_ci	}
328c2ecf20Sopenharmony_ci	if (cfg->bind_ifindex) {
338c2ecf20Sopenharmony_ci		err = sock_bindtoindex(sock->sk, cfg->bind_ifindex, true);
348c2ecf20Sopenharmony_ci		if (err < 0)
358c2ecf20Sopenharmony_ci			goto error;
368c2ecf20Sopenharmony_ci	}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	udp6_addr.sin6_family = AF_INET6;
398c2ecf20Sopenharmony_ci	memcpy(&udp6_addr.sin6_addr, &cfg->local_ip6,
408c2ecf20Sopenharmony_ci	       sizeof(udp6_addr.sin6_addr));
418c2ecf20Sopenharmony_ci	udp6_addr.sin6_port = cfg->local_udp_port;
428c2ecf20Sopenharmony_ci	err = kernel_bind(sock, (struct sockaddr *)&udp6_addr,
438c2ecf20Sopenharmony_ci			  sizeof(udp6_addr));
448c2ecf20Sopenharmony_ci	if (err < 0)
458c2ecf20Sopenharmony_ci		goto error;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	if (cfg->peer_udp_port) {
488c2ecf20Sopenharmony_ci		memset(&udp6_addr, 0, sizeof(udp6_addr));
498c2ecf20Sopenharmony_ci		udp6_addr.sin6_family = AF_INET6;
508c2ecf20Sopenharmony_ci		memcpy(&udp6_addr.sin6_addr, &cfg->peer_ip6,
518c2ecf20Sopenharmony_ci		       sizeof(udp6_addr.sin6_addr));
528c2ecf20Sopenharmony_ci		udp6_addr.sin6_port = cfg->peer_udp_port;
538c2ecf20Sopenharmony_ci		err = kernel_connect(sock,
548c2ecf20Sopenharmony_ci				     (struct sockaddr *)&udp6_addr,
558c2ecf20Sopenharmony_ci				     sizeof(udp6_addr), 0);
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci	if (err < 0)
588c2ecf20Sopenharmony_ci		goto error;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	udp_set_no_check6_tx(sock->sk, !cfg->use_udp6_tx_checksums);
618c2ecf20Sopenharmony_ci	udp_set_no_check6_rx(sock->sk, !cfg->use_udp6_rx_checksums);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	*sockp = sock;
648c2ecf20Sopenharmony_ci	return 0;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cierror:
678c2ecf20Sopenharmony_ci	if (sock) {
688c2ecf20Sopenharmony_ci		kernel_sock_shutdown(sock, SHUT_RDWR);
698c2ecf20Sopenharmony_ci		sock_release(sock);
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci	*sockp = NULL;
728c2ecf20Sopenharmony_ci	return err;
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(udp_sock_create6);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ciint udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sock *sk,
778c2ecf20Sopenharmony_ci			 struct sk_buff *skb,
788c2ecf20Sopenharmony_ci			 struct net_device *dev, struct in6_addr *saddr,
798c2ecf20Sopenharmony_ci			 struct in6_addr *daddr,
808c2ecf20Sopenharmony_ci			 __u8 prio, __u8 ttl, __be32 label,
818c2ecf20Sopenharmony_ci			 __be16 src_port, __be16 dst_port, bool nocheck)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct udphdr *uh;
848c2ecf20Sopenharmony_ci	struct ipv6hdr *ip6h;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	__skb_push(skb, sizeof(*uh));
878c2ecf20Sopenharmony_ci	skb_reset_transport_header(skb);
888c2ecf20Sopenharmony_ci	uh = udp_hdr(skb);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	uh->dest = dst_port;
918c2ecf20Sopenharmony_ci	uh->source = src_port;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	uh->len = htons(skb->len);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	skb_dst_set(skb, dst);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	udp6_set_csum(nocheck, skb, saddr, daddr, skb->len);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	__skb_push(skb, sizeof(*ip6h));
1008c2ecf20Sopenharmony_ci	skb_reset_network_header(skb);
1018c2ecf20Sopenharmony_ci	ip6h		  = ipv6_hdr(skb);
1028c2ecf20Sopenharmony_ci	ip6_flow_hdr(ip6h, prio, label);
1038c2ecf20Sopenharmony_ci	ip6h->payload_len = htons(skb->len);
1048c2ecf20Sopenharmony_ci	ip6h->nexthdr     = IPPROTO_UDP;
1058c2ecf20Sopenharmony_ci	ip6h->hop_limit   = ttl;
1068c2ecf20Sopenharmony_ci	ip6h->daddr	  = *daddr;
1078c2ecf20Sopenharmony_ci	ip6h->saddr	  = *saddr;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	ip6tunnel_xmit(sk, skb, dev);
1108c2ecf20Sopenharmony_ci	return 0;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(udp_tunnel6_xmit_skb);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
115