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");
227*67c20de3SRob Gill MODULE_DESCRIPTION("Foo over UDP (IPv6)");
228