xref: /openbmc/linux/net/ipv6/fou6.c (revision 67c20de3)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2aa3463d6STom Herbert #include <linux/module.h>
3aa3463d6STom Herbert #include <linux/errno.h>
4aa3463d6STom Herbert #include <linux/socket.h>
5aa3463d6STom Herbert #include <linux/skbuff.h>
6aa3463d6STom Herbert #include <linux/ip.h>
7aa3463d6STom Herbert #include <linux/udp.h>
8b8a51b38SStefano Brivio #include <linux/icmpv6.h>
9aa3463d6STom Herbert #include <linux/types.h>
10aa3463d6STom Herbert #include <linux/kernel.h>
11aa3463d6STom Herbert #include <net/fou.h>
12aa3463d6STom Herbert #include <net/ip.h>
13aa3463d6STom Herbert #include <net/ip6_tunnel.h>
14aa3463d6STom Herbert #include <net/ip6_checksum.h>
15aa3463d6STom Herbert #include <net/protocol.h>
16aa3463d6STom Herbert #include <net/udp.h>
17aa3463d6STom Herbert #include <net/udp_tunnel.h>
18aa3463d6STom Herbert 
199dc621afSstephen hemminger #if IS_ENABLED(CONFIG_IPV6_FOU_TUNNEL)
209dc621afSstephen hemminger 
fou6_build_udp(struct sk_buff * skb,struct ip_tunnel_encap * e,struct flowi6 * fl6,u8 * protocol,__be16 sport)21aa3463d6STom Herbert static void fou6_build_udp(struct sk_buff *skb, struct ip_tunnel_encap *e,
22aa3463d6STom Herbert 			   struct flowi6 *fl6, u8 *protocol, __be16 sport)
23aa3463d6STom Herbert {
24aa3463d6STom Herbert 	struct udphdr *uh;
25aa3463d6STom Herbert 
26aa3463d6STom Herbert 	skb_push(skb, sizeof(struct udphdr));
27aa3463d6STom Herbert 	skb_reset_transport_header(skb);
28aa3463d6STom Herbert 
29aa3463d6STom Herbert 	uh = udp_hdr(skb);
30aa3463d6STom Herbert 
31aa3463d6STom Herbert 	uh->dest = e->dport;
32aa3463d6STom Herbert 	uh->source = sport;
33aa3463d6STom Herbert 	uh->len = htons(skb->len);
34aa3463d6STom Herbert 	udp6_set_csum(!(e->flags & TUNNEL_ENCAP_FLAG_CSUM6), skb,
35aa3463d6STom Herbert 		      &fl6->saddr, &fl6->daddr, skb->len);
36aa3463d6STom Herbert 
37aa3463d6STom Herbert 	*protocol = IPPROTO_UDP;
38aa3463d6STom Herbert }
39aa3463d6STom Herbert 
fou6_build_header(struct sk_buff * skb,struct ip_tunnel_encap * e,u8 * protocol,struct flowi6 * fl6)409dc621afSstephen hemminger static int fou6_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
41aa3463d6STom Herbert 			     u8 *protocol, struct flowi6 *fl6)
42aa3463d6STom Herbert {
43aa3463d6STom Herbert 	__be16 sport;
44aa3463d6STom Herbert 	int err;
45aa3463d6STom Herbert 	int type = e->flags & TUNNEL_ENCAP_FLAG_CSUM6 ?
46aa3463d6STom Herbert 		SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
47aa3463d6STom Herbert 
48aa3463d6STom Herbert 	err = __fou_build_header(skb, e, protocol, &sport, type);
49aa3463d6STom Herbert 	if (err)
50aa3463d6STom Herbert 		return err;
51aa3463d6STom Herbert 
52aa3463d6STom Herbert 	fou6_build_udp(skb, e, fl6, protocol, sport);
53aa3463d6STom Herbert 
54aa3463d6STom Herbert 	return 0;
55aa3463d6STom Herbert }
56aa3463d6STom Herbert 
gue6_build_header(struct sk_buff * skb,struct ip_tunnel_encap * e,u8 * protocol,struct flowi6 * fl6)579dc621afSstephen hemminger static int gue6_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
58aa3463d6STom Herbert 			     u8 *protocol, struct flowi6 *fl6)
59aa3463d6STom Herbert {
60aa3463d6STom Herbert 	__be16 sport;
61aa3463d6STom Herbert 	int err;
62aa3463d6STom Herbert 	int type = e->flags & TUNNEL_ENCAP_FLAG_CSUM6 ?
63aa3463d6STom Herbert 		SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
64aa3463d6STom Herbert 
65aa3463d6STom Herbert 	err = __gue_build_header(skb, e, protocol, &sport, type);
66aa3463d6STom Herbert 	if (err)
67aa3463d6STom Herbert 		return err;
68aa3463d6STom Herbert 
69aa3463d6STom Herbert 	fou6_build_udp(skb, e, fl6, protocol, sport);
70aa3463d6STom Herbert 
71aa3463d6STom Herbert 	return 0;
72aa3463d6STom Herbert }
73aa3463d6STom Herbert 
gue6_err_proto_handler(int proto,struct sk_buff * skb,struct inet6_skb_parm * opt,u8 type,u8 code,int offset,__be32 info)74b8a51b38SStefano Brivio static int gue6_err_proto_handler(int proto, struct sk_buff *skb,
75b8a51b38SStefano Brivio 				  struct inet6_skb_parm *opt,
765de362dfSPaolo Abeni 				  u8 type, u8 code, int offset, __be32 info)
77b8a51b38SStefano Brivio {
78b8a51b38SStefano Brivio 	const struct inet6_protocol *ipprot;
79b8a51b38SStefano Brivio 
80b8a51b38SStefano Brivio 	ipprot = rcu_dereference(inet6_protos[proto]);
81b8a51b38SStefano Brivio 	if (ipprot && ipprot->err_handler) {
82b8a51b38SStefano Brivio 		if (!ipprot->err_handler(skb, opt, type, code, offset, info))
83b8a51b38SStefano Brivio 			return 0;
84b8a51b38SStefano Brivio 	}
85b8a51b38SStefano Brivio 
86b8a51b38SStefano Brivio 	return -ENOENT;
87b8a51b38SStefano Brivio }
88b8a51b38SStefano Brivio 
gue6_err(struct sk_buff * skb,struct inet6_skb_parm * opt,u8 type,u8 code,int offset,__be32 info)89b8a51b38SStefano Brivio static int gue6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
90b8a51b38SStefano Brivio 		    u8 type, u8 code, int offset, __be32 info)
91b8a51b38SStefano Brivio {
92b8a51b38SStefano Brivio 	int transport_offset = skb_transport_offset(skb);
93b8a51b38SStefano Brivio 	struct guehdr *guehdr;
9426fc181eSEric Dumazet 	size_t len, optlen;
95b8a51b38SStefano Brivio 	int ret;
96b8a51b38SStefano Brivio 
9726fc181eSEric Dumazet 	len = sizeof(struct udphdr) + sizeof(struct guehdr);
985355ed63SEric Dumazet 	if (!pskb_may_pull(skb, transport_offset + len))
99b8a51b38SStefano Brivio 		return -EINVAL;
100b8a51b38SStefano Brivio 
101b8a51b38SStefano Brivio 	guehdr = (struct guehdr *)&udp_hdr(skb)[1];
102b8a51b38SStefano Brivio 
103b8a51b38SStefano Brivio 	switch (guehdr->version) {
104b8a51b38SStefano Brivio 	case 0: /* Full GUE header present */
105b8a51b38SStefano Brivio 		break;
106b8a51b38SStefano Brivio 	case 1: {
107b8a51b38SStefano Brivio 		/* Direct encasulation of IPv4 or IPv6 */
108b8a51b38SStefano Brivio 		skb_set_transport_header(skb, -(int)sizeof(struct icmp6hdr));
109b8a51b38SStefano Brivio 
110b8a51b38SStefano Brivio 		switch (((struct iphdr *)guehdr)->version) {
111b8a51b38SStefano Brivio 		case 4:
112b8a51b38SStefano Brivio 			ret = gue6_err_proto_handler(IPPROTO_IPIP, skb, opt,
113b8a51b38SStefano Brivio 						     type, code, offset, info);
114b8a51b38SStefano Brivio 			goto out;
115b8a51b38SStefano Brivio 		case 6:
116b8a51b38SStefano Brivio 			ret = gue6_err_proto_handler(IPPROTO_IPV6, skb, opt,
117b8a51b38SStefano Brivio 						     type, code, offset, info);
118b8a51b38SStefano Brivio 			goto out;
119b8a51b38SStefano Brivio 		default:
120b8a51b38SStefano Brivio 			ret = -EOPNOTSUPP;
121b8a51b38SStefano Brivio 			goto out;
122b8a51b38SStefano Brivio 		}
123b8a51b38SStefano Brivio 	}
124b8a51b38SStefano Brivio 	default: /* Undefined version */
125b8a51b38SStefano Brivio 		return -EOPNOTSUPP;
126b8a51b38SStefano Brivio 	}
127b8a51b38SStefano Brivio 
128b8a51b38SStefano Brivio 	if (guehdr->control)
129b8a51b38SStefano Brivio 		return -ENOENT;
130b8a51b38SStefano Brivio 
131b8a51b38SStefano Brivio 	optlen = guehdr->hlen << 2;
132b8a51b38SStefano Brivio 
1335355ed63SEric Dumazet 	if (!pskb_may_pull(skb, transport_offset + len + optlen))
13426fc181eSEric Dumazet 		return -EINVAL;
13526fc181eSEric Dumazet 
13626fc181eSEric Dumazet 	guehdr = (struct guehdr *)&udp_hdr(skb)[1];
137b8a51b38SStefano Brivio 	if (validate_gue_flags(guehdr, optlen))
138b8a51b38SStefano Brivio 		return -EINVAL;
139b8a51b38SStefano Brivio 
14044039e00SStefano Brivio 	/* Handling exceptions for direct UDP encapsulation in GUE would lead to
14144039e00SStefano Brivio 	 * recursion. Besides, this kind of encapsulation can't even be
14244039e00SStefano Brivio 	 * configured currently. Discard this.
14344039e00SStefano Brivio 	 */
14444039e00SStefano Brivio 	if (guehdr->proto_ctype == IPPROTO_UDP ||
14544039e00SStefano Brivio 	    guehdr->proto_ctype == IPPROTO_UDPLITE)
14644039e00SStefano Brivio 		return -EOPNOTSUPP;
14744039e00SStefano Brivio 
148b8a51b38SStefano Brivio 	skb_set_transport_header(skb, -(int)sizeof(struct icmp6hdr));
149b8a51b38SStefano Brivio 	ret = gue6_err_proto_handler(guehdr->proto_ctype, skb,
150b8a51b38SStefano Brivio 				     opt, type, code, offset, info);
151b8a51b38SStefano Brivio 
152b8a51b38SStefano Brivio out:
153b8a51b38SStefano Brivio 	skb_set_transport_header(skb, transport_offset);
154b8a51b38SStefano Brivio 	return ret;
155b8a51b38SStefano Brivio }
156b8a51b38SStefano Brivio 
157b8a51b38SStefano Brivio 
158aa3463d6STom Herbert static const struct ip6_tnl_encap_ops fou_ip6tun_ops = {
159aa3463d6STom Herbert 	.encap_hlen = fou_encap_hlen,
160aa3463d6STom Herbert 	.build_header = fou6_build_header,
161b8a51b38SStefano Brivio 	.err_handler = gue6_err,
162aa3463d6STom Herbert };
163aa3463d6STom Herbert 
164aa3463d6STom Herbert static const struct ip6_tnl_encap_ops gue_ip6tun_ops = {
165aa3463d6STom Herbert 	.encap_hlen = gue_encap_hlen,
166aa3463d6STom Herbert 	.build_header = gue6_build_header,
167b8a51b38SStefano Brivio 	.err_handler = gue6_err,
168aa3463d6STom Herbert };
169aa3463d6STom Herbert 
ip6_tnl_encap_add_fou_ops(void)170aa3463d6STom Herbert static int ip6_tnl_encap_add_fou_ops(void)
171aa3463d6STom Herbert {
172aa3463d6STom Herbert 	int ret;
173aa3463d6STom Herbert 
174aa3463d6STom Herbert 	ret = ip6_tnl_encap_add_ops(&fou_ip6tun_ops, TUNNEL_ENCAP_FOU);
175aa3463d6STom Herbert 	if (ret < 0) {
176aa3463d6STom Herbert 		pr_err("can't add fou6 ops\n");
177aa3463d6STom Herbert 		return ret;
178aa3463d6STom Herbert 	}
179aa3463d6STom Herbert 
180aa3463d6STom Herbert 	ret = ip6_tnl_encap_add_ops(&gue_ip6tun_ops, TUNNEL_ENCAP_GUE);
181aa3463d6STom Herbert 	if (ret < 0) {
182aa3463d6STom Herbert 		pr_err("can't add gue6 ops\n");
183aa3463d6STom Herbert 		ip6_tnl_encap_del_ops(&fou_ip6tun_ops, TUNNEL_ENCAP_FOU);
184aa3463d6STom Herbert 		return ret;
185aa3463d6STom Herbert 	}
186aa3463d6STom Herbert 
187aa3463d6STom Herbert 	return 0;
188aa3463d6STom Herbert }
189aa3463d6STom Herbert 
ip6_tnl_encap_del_fou_ops(void)190aa3463d6STom Herbert static void ip6_tnl_encap_del_fou_ops(void)
191aa3463d6STom Herbert {
192aa3463d6STom Herbert 	ip6_tnl_encap_del_ops(&fou_ip6tun_ops, TUNNEL_ENCAP_FOU);
193aa3463d6STom Herbert 	ip6_tnl_encap_del_ops(&gue_ip6tun_ops, TUNNEL_ENCAP_GUE);
194aa3463d6STom Herbert }
195aa3463d6STom Herbert 
196aa3463d6STom Herbert #else
197aa3463d6STom Herbert 
ip6_tnl_encap_add_fou_ops(void)198aa3463d6STom Herbert static int ip6_tnl_encap_add_fou_ops(void)
199aa3463d6STom Herbert {
200aa3463d6STom Herbert 	return 0;
201aa3463d6STom Herbert }
202aa3463d6STom Herbert 
ip6_tnl_encap_del_fou_ops(void)203aa3463d6STom Herbert static void ip6_tnl_encap_del_fou_ops(void)
204aa3463d6STom Herbert {
205aa3463d6STom Herbert }
206aa3463d6STom Herbert 
207aa3463d6STom Herbert #endif
208aa3463d6STom Herbert 
fou6_init(void)209aa3463d6STom Herbert static int __init fou6_init(void)
210aa3463d6STom Herbert {
211aa3463d6STom Herbert 	int ret;
212aa3463d6STom Herbert 
213aa3463d6STom Herbert 	ret = ip6_tnl_encap_add_fou_ops();
214aa3463d6STom Herbert 
215aa3463d6STom Herbert 	return ret;
216aa3463d6STom Herbert }
217aa3463d6STom Herbert 
fou6_fini(void)218aa3463d6STom Herbert static void __exit fou6_fini(void)
219aa3463d6STom Herbert {
220aa3463d6STom Herbert 	ip6_tnl_encap_del_fou_ops();
221aa3463d6STom Herbert }
222aa3463d6STom Herbert 
223aa3463d6STom Herbert module_init(fou6_init);
224aa3463d6STom Herbert module_exit(fou6_fini);
225aa3463d6STom Herbert MODULE_AUTHOR("Tom Herbert <therbert@google.com>");
226aa3463d6STom Herbert MODULE_LICENSE("GPL");
22767c20de3SRob Gill MODULE_DESCRIPTION("Foo over UDP (IPv6)");
228