Skip to content

Commit b8a51b3

Browse files
sbrivio-rhdavem330
authored andcommitted
fou, fou6: ICMP error handlers for FoU and GUE
As the destination port in FoU and GUE receiving sockets doesn't necessarily match the remote destination port, we can't associate errors to the encapsulating tunnels with a socket lookup -- we need to blindly try them instead. This means we don't even know if we are handling errors for FoU or GUE without digging into the packets. Hence, implement a single handler for both, one for IPv4 and one for IPv6, that will check whether the packet that generated the ICMP error used a direct IP encapsulation or if it had a GUE header, and send the error to the matching protocol handler, if any. Signed-off-by: Stefano Brivio <sbrivio@redhat.com> Reviewed-by: Sabrina Dubroca <sd@queasysnail.net> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent e7cc082 commit b8a51b3

File tree

3 files changed

+143
-0
lines changed

3 files changed

+143
-0
lines changed

net/ipv4/fou.c

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <linux/socket.h>
44
#include <linux/skbuff.h>
55
#include <linux/ip.h>
6+
#include <linux/icmp.h>
67
#include <linux/udp.h>
78
#include <linux/types.h>
89
#include <linux/kernel.h>
@@ -1003,15 +1004,82 @@ static int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
10031004
return 0;
10041005
}
10051006

1007+
static int gue_err_proto_handler(int proto, struct sk_buff *skb, u32 info)
1008+
{
1009+
const struct net_protocol *ipprot = rcu_dereference(inet_protos[proto]);
1010+
1011+
if (ipprot && ipprot->err_handler) {
1012+
if (!ipprot->err_handler(skb, info))
1013+
return 0;
1014+
}
1015+
1016+
return -ENOENT;
1017+
}
1018+
1019+
static int gue_err(struct sk_buff *skb, u32 info)
1020+
{
1021+
int transport_offset = skb_transport_offset(skb);
1022+
struct guehdr *guehdr;
1023+
size_t optlen;
1024+
int ret;
1025+
1026+
if (skb->len < sizeof(struct udphdr) + sizeof(struct guehdr))
1027+
return -EINVAL;
1028+
1029+
guehdr = (struct guehdr *)&udp_hdr(skb)[1];
1030+
1031+
switch (guehdr->version) {
1032+
case 0: /* Full GUE header present */
1033+
break;
1034+
case 1: {
1035+
/* Direct encasulation of IPv4 or IPv6 */
1036+
skb_set_transport_header(skb, -(int)sizeof(struct icmphdr));
1037+
1038+
switch (((struct iphdr *)guehdr)->version) {
1039+
case 4:
1040+
ret = gue_err_proto_handler(IPPROTO_IPIP, skb, info);
1041+
goto out;
1042+
#if IS_ENABLED(CONFIG_IPV6)
1043+
case 6:
1044+
ret = gue_err_proto_handler(IPPROTO_IPV6, skb, info);
1045+
goto out;
1046+
#endif
1047+
default:
1048+
ret = -EOPNOTSUPP;
1049+
goto out;
1050+
}
1051+
}
1052+
default: /* Undefined version */
1053+
return -EOPNOTSUPP;
1054+
}
1055+
1056+
if (guehdr->control)
1057+
return -ENOENT;
1058+
1059+
optlen = guehdr->hlen << 2;
1060+
1061+
if (validate_gue_flags(guehdr, optlen))
1062+
return -EINVAL;
1063+
1064+
skb_set_transport_header(skb, -(int)sizeof(struct icmphdr));
1065+
ret = gue_err_proto_handler(guehdr->proto_ctype, skb, info);
1066+
1067+
out:
1068+
skb_set_transport_header(skb, transport_offset);
1069+
return ret;
1070+
}
1071+
10061072

10071073
static const struct ip_tunnel_encap_ops fou_iptun_ops = {
10081074
.encap_hlen = fou_encap_hlen,
10091075
.build_header = fou_build_header,
1076+
.err_handler = gue_err,
10101077
};
10111078

10121079
static const struct ip_tunnel_encap_ops gue_iptun_ops = {
10131080
.encap_hlen = gue_encap_hlen,
10141081
.build_header = gue_build_header,
1082+
.err_handler = gue_err,
10151083
};
10161084

10171085
static int ip_tunnel_encap_add_fou_ops(void)

net/ipv4/protocol.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <net/protocol.h>
3030

3131
struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS] __read_mostly;
32+
EXPORT_SYMBOL(inet_protos);
3233
const struct net_offload __rcu *inet_offloads[MAX_INET_PROTOS] __read_mostly;
3334
EXPORT_SYMBOL(inet_offloads);
3435

net/ipv6/fou6.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <linux/skbuff.h>
55
#include <linux/ip.h>
66
#include <linux/udp.h>
7+
#include <linux/icmpv6.h>
78
#include <linux/types.h>
89
#include <linux/kernel.h>
910
#include <net/fou.h>
@@ -69,14 +70,87 @@ static int gue6_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
6970
return 0;
7071
}
7172

73+
static int gue6_err_proto_handler(int proto, struct sk_buff *skb,
74+
struct inet6_skb_parm *opt,
75+
u8 type, u8 code, int offset, u32 info)
76+
{
77+
const struct inet6_protocol *ipprot;
78+
79+
ipprot = rcu_dereference(inet6_protos[proto]);
80+
if (ipprot && ipprot->err_handler) {
81+
if (!ipprot->err_handler(skb, opt, type, code, offset, info))
82+
return 0;
83+
}
84+
85+
return -ENOENT;
86+
}
87+
88+
static int gue6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
89+
u8 type, u8 code, int offset, __be32 info)
90+
{
91+
int transport_offset = skb_transport_offset(skb);
92+
struct guehdr *guehdr;
93+
size_t optlen;
94+
int ret;
95+
96+
if (skb->len < sizeof(struct udphdr) + sizeof(struct guehdr))
97+
return -EINVAL;
98+
99+
guehdr = (struct guehdr *)&udp_hdr(skb)[1];
100+
101+
switch (guehdr->version) {
102+
case 0: /* Full GUE header present */
103+
break;
104+
case 1: {
105+
/* Direct encasulation of IPv4 or IPv6 */
106+
skb_set_transport_header(skb, -(int)sizeof(struct icmp6hdr));
107+
108+
switch (((struct iphdr *)guehdr)->version) {
109+
case 4:
110+
ret = gue6_err_proto_handler(IPPROTO_IPIP, skb, opt,
111+
type, code, offset, info);
112+
goto out;
113+
case 6:
114+
ret = gue6_err_proto_handler(IPPROTO_IPV6, skb, opt,
115+
type, code, offset, info);
116+
goto out;
117+
default:
118+
ret = -EOPNOTSUPP;
119+
goto out;
120+
}
121+
}
122+
default: /* Undefined version */
123+
return -EOPNOTSUPP;
124+
}
125+
126+
if (guehdr->control)
127+
return -ENOENT;
128+
129+
optlen = guehdr->hlen << 2;
130+
131+
if (validate_gue_flags(guehdr, optlen))
132+
return -EINVAL;
133+
134+
skb_set_transport_header(skb, -(int)sizeof(struct icmp6hdr));
135+
ret = gue6_err_proto_handler(guehdr->proto_ctype, skb,
136+
opt, type, code, offset, info);
137+
138+
out:
139+
skb_set_transport_header(skb, transport_offset);
140+
return ret;
141+
}
142+
143+
72144
static const struct ip6_tnl_encap_ops fou_ip6tun_ops = {
73145
.encap_hlen = fou_encap_hlen,
74146
.build_header = fou6_build_header,
147+
.err_handler = gue6_err,
75148
};
76149

77150
static const struct ip6_tnl_encap_ops gue_ip6tun_ops = {
78151
.encap_hlen = gue_encap_hlen,
79152
.build_header = gue6_build_header,
153+
.err_handler = gue6_err,
80154
};
81155

82156
static int ip6_tnl_encap_add_fou_ops(void)

0 commit comments

Comments
 (0)