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