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