.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | #include <linux/module.h> |
---|
2 | 3 | #include <linux/errno.h> |
---|
3 | 4 | #include <linux/socket.h> |
---|
4 | 5 | #include <linux/skbuff.h> |
---|
5 | 6 | #include <linux/ip.h> |
---|
6 | 7 | #include <linux/udp.h> |
---|
| 8 | +#include <linux/icmpv6.h> |
---|
7 | 9 | #include <linux/types.h> |
---|
8 | 10 | #include <linux/kernel.h> |
---|
9 | 11 | #include <net/fou.h> |
---|
.. | .. |
---|
69 | 71 | return 0; |
---|
70 | 72 | } |
---|
71 | 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 | + |
---|
72 | 158 | static const struct ip6_tnl_encap_ops fou_ip6tun_ops = { |
---|
73 | 159 | .encap_hlen = fou_encap_hlen, |
---|
74 | 160 | .build_header = fou6_build_header, |
---|
| 161 | + .err_handler = gue6_err, |
---|
75 | 162 | }; |
---|
76 | 163 | |
---|
77 | 164 | static const struct ip6_tnl_encap_ops gue_ip6tun_ops = { |
---|
78 | 165 | .encap_hlen = gue_encap_hlen, |
---|
79 | 166 | .build_header = gue6_build_header, |
---|
| 167 | + .err_handler = gue6_err, |
---|
80 | 168 | }; |
---|
81 | 169 | |
---|
82 | 170 | static int ip6_tnl_encap_add_fou_ops(void) |
---|
.. | .. |
---|
136 | 224 | module_exit(fou6_fini); |
---|
137 | 225 | MODULE_AUTHOR("Tom Herbert <therbert@google.com>"); |
---|
138 | 226 | MODULE_LICENSE("GPL"); |
---|
| 227 | +MODULE_DESCRIPTION("Foo over UDP (IPv6)"); |
---|