aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/fou6.c
diff options
context:
space:
mode:
authorStefano Brivio <sbrivio@redhat.com>2018-11-08 06:19:23 -0500
committerDavid S. Miller <davem@davemloft.net>2018-11-08 20:13:08 -0500
commitb8a51b38e4d4dec3e379d52c0fe1a66827f7cf1e (patch)
tree03a60c4c9b5988ff6283467e31bf8ad8bdd49eea /net/ipv6/fou6.c
parente7cc082455cb49ea937a3ec4ab3d001b0b5f137b (diff)
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>
Diffstat (limited to 'net/ipv6/fou6.c')
-rw-r--r--net/ipv6/fou6.c74
1 files changed, 74 insertions, 0 deletions
diff --git a/net/ipv6/fou6.c b/net/ipv6/fou6.c
index 6de3c04b0f30..bd675c61deb1 100644
--- a/net/ipv6/fou6.c
+++ b/net/ipv6/fou6.c
@@ -4,6 +4,7 @@
4#include <linux/skbuff.h> 4#include <linux/skbuff.h>
5#include <linux/ip.h> 5#include <linux/ip.h>
6#include <linux/udp.h> 6#include <linux/udp.h>
7#include <linux/icmpv6.h>
7#include <linux/types.h> 8#include <linux/types.h>
8#include <linux/kernel.h> 9#include <linux/kernel.h>
9#include <net/fou.h> 10#include <net/fou.h>
@@ -69,14 +70,87 @@ static int gue6_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
69 return 0; 70 return 0;
70} 71}
71 72
73static 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
88static 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
138out:
139 skb_set_transport_header(skb, transport_offset);
140 return ret;
141}
142
143
72static const struct ip6_tnl_encap_ops fou_ip6tun_ops = { 144static const struct ip6_tnl_encap_ops fou_ip6tun_ops = {
73 .encap_hlen = fou_encap_hlen, 145 .encap_hlen = fou_encap_hlen,
74 .build_header = fou6_build_header, 146 .build_header = fou6_build_header,
147 .err_handler = gue6_err,
75}; 148};
76 149
77static const struct ip6_tnl_encap_ops gue_ip6tun_ops = { 150static const struct ip6_tnl_encap_ops gue_ip6tun_ops = {
78 .encap_hlen = gue_encap_hlen, 151 .encap_hlen = gue_encap_hlen,
79 .build_header = gue6_build_header, 152 .build_header = gue6_build_header,
153 .err_handler = gue6_err,
80}; 154};
81 155
82static int ip6_tnl_encap_add_fou_ops(void) 156static int ip6_tnl_encap_add_fou_ops(void)