.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (c) 2013 Nicira, Inc. |
---|
3 | | - * |
---|
4 | | - * This program is free software; you can redistribute it and/or |
---|
5 | | - * modify it under the terms of version 2 of the GNU General Public |
---|
6 | | - * License as published by the Free Software Foundation. |
---|
7 | | - * |
---|
8 | | - * This program is distributed in the hope that it will be useful, but |
---|
9 | | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
10 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
11 | | - * General Public License for more details. |
---|
12 | | - * |
---|
13 | | - * You should have received a copy of the GNU General Public License |
---|
14 | | - * along with this program; if not, write to the Free Software |
---|
15 | | - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
---|
16 | | - * 02110-1301, USA |
---|
17 | 4 | */ |
---|
18 | 5 | |
---|
19 | 6 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
---|
.. | .. |
---|
38 | 25 | #include <net/protocol.h> |
---|
39 | 26 | #include <net/ip_tunnels.h> |
---|
40 | 27 | #include <net/ip6_tunnel.h> |
---|
| 28 | +#include <net/ip6_checksum.h> |
---|
41 | 29 | #include <net/arp.h> |
---|
42 | 30 | #include <net/checksum.h> |
---|
43 | 31 | #include <net/dsfield.h> |
---|
.. | .. |
---|
47 | 35 | #include <net/netns/generic.h> |
---|
48 | 36 | #include <net/rtnetlink.h> |
---|
49 | 37 | #include <net/dst_metadata.h> |
---|
| 38 | +#include <net/geneve.h> |
---|
| 39 | +#include <net/vxlan.h> |
---|
| 40 | +#include <net/erspan.h> |
---|
50 | 41 | |
---|
51 | 42 | const struct ip_tunnel_encap_ops __rcu * |
---|
52 | 43 | iptun_encaps[MAX_IPTUN_ENCAP_OPS] __read_mostly; |
---|
.. | .. |
---|
123 | 114 | } |
---|
124 | 115 | |
---|
125 | 116 | skb_clear_hash_if_not_l4(skb); |
---|
126 | | - skb->vlan_tci = 0; |
---|
| 117 | + __vlan_hwaccel_clear_tag(skb); |
---|
127 | 118 | skb_set_queue_mapping(skb, 0); |
---|
128 | 119 | skb_scrub_packet(skb, xnet); |
---|
129 | 120 | |
---|
.. | .. |
---|
139 | 130 | |
---|
140 | 131 | if (!md || md->type != METADATA_IP_TUNNEL || |
---|
141 | 132 | md->u.tun_info.mode & IP_TUNNEL_INFO_TX) |
---|
142 | | - |
---|
143 | 133 | return NULL; |
---|
144 | 134 | |
---|
145 | | - res = metadata_dst_alloc(0, METADATA_IP_TUNNEL, flags); |
---|
| 135 | + src = &md->u.tun_info; |
---|
| 136 | + res = metadata_dst_alloc(src->options_len, METADATA_IP_TUNNEL, flags); |
---|
146 | 137 | if (!res) |
---|
147 | 138 | return NULL; |
---|
148 | 139 | |
---|
149 | 140 | dst = &res->u.tun_info; |
---|
150 | | - src = &md->u.tun_info; |
---|
151 | 141 | dst->key.tun_id = src->key.tun_id; |
---|
152 | 142 | if (src->mode & IP_TUNNEL_INFO_IPV6) |
---|
153 | 143 | memcpy(&dst->key.u.ipv6.dst, &src->key.u.ipv6.src, |
---|
154 | 144 | sizeof(struct in6_addr)); |
---|
155 | 145 | else |
---|
156 | 146 | dst->key.u.ipv4.dst = src->key.u.ipv4.src; |
---|
| 147 | + dst->key.tun_flags = src->key.tun_flags; |
---|
157 | 148 | dst->mode = src->mode | IP_TUNNEL_INFO_TX; |
---|
| 149 | + ip_tunnel_info_opts_set(dst, ip_tunnel_info_opts(src), |
---|
| 150 | + src->options_len, 0); |
---|
158 | 151 | |
---|
159 | 152 | return res; |
---|
160 | 153 | } |
---|
.. | .. |
---|
192 | 185 | } |
---|
193 | 186 | EXPORT_SYMBOL_GPL(iptunnel_handle_offloads); |
---|
194 | 187 | |
---|
| 188 | +/** |
---|
| 189 | + * iptunnel_pmtud_build_icmp() - Build ICMP error message for PMTUD |
---|
| 190 | + * @skb: Original packet with L2 header |
---|
| 191 | + * @mtu: MTU value for ICMP error |
---|
| 192 | + * |
---|
| 193 | + * Return: length on success, negative error code if message couldn't be built. |
---|
| 194 | + */ |
---|
| 195 | +static int iptunnel_pmtud_build_icmp(struct sk_buff *skb, int mtu) |
---|
| 196 | +{ |
---|
| 197 | + const struct iphdr *iph = ip_hdr(skb); |
---|
| 198 | + struct icmphdr *icmph; |
---|
| 199 | + struct iphdr *niph; |
---|
| 200 | + struct ethhdr eh; |
---|
| 201 | + int len, err; |
---|
| 202 | + |
---|
| 203 | + if (!pskb_may_pull(skb, ETH_HLEN + sizeof(struct iphdr))) |
---|
| 204 | + return -EINVAL; |
---|
| 205 | + |
---|
| 206 | + skb_copy_bits(skb, skb_mac_offset(skb), &eh, ETH_HLEN); |
---|
| 207 | + pskb_pull(skb, ETH_HLEN); |
---|
| 208 | + skb_reset_network_header(skb); |
---|
| 209 | + |
---|
| 210 | + err = pskb_trim(skb, 576 - sizeof(*niph) - sizeof(*icmph)); |
---|
| 211 | + if (err) |
---|
| 212 | + return err; |
---|
| 213 | + |
---|
| 214 | + len = skb->len + sizeof(*icmph); |
---|
| 215 | + err = skb_cow(skb, sizeof(*niph) + sizeof(*icmph) + ETH_HLEN); |
---|
| 216 | + if (err) |
---|
| 217 | + return err; |
---|
| 218 | + |
---|
| 219 | + icmph = skb_push(skb, sizeof(*icmph)); |
---|
| 220 | + *icmph = (struct icmphdr) { |
---|
| 221 | + .type = ICMP_DEST_UNREACH, |
---|
| 222 | + .code = ICMP_FRAG_NEEDED, |
---|
| 223 | + .checksum = 0, |
---|
| 224 | + .un.frag.__unused = 0, |
---|
| 225 | + .un.frag.mtu = ntohs(mtu), |
---|
| 226 | + }; |
---|
| 227 | + icmph->checksum = csum_fold(skb_checksum(skb, 0, len, 0)); |
---|
| 228 | + skb_reset_transport_header(skb); |
---|
| 229 | + |
---|
| 230 | + niph = skb_push(skb, sizeof(*niph)); |
---|
| 231 | + *niph = (struct iphdr) { |
---|
| 232 | + .ihl = sizeof(*niph) / 4u, |
---|
| 233 | + .version = 4, |
---|
| 234 | + .tos = 0, |
---|
| 235 | + .tot_len = htons(len + sizeof(*niph)), |
---|
| 236 | + .id = 0, |
---|
| 237 | + .frag_off = htons(IP_DF), |
---|
| 238 | + .ttl = iph->ttl, |
---|
| 239 | + .protocol = IPPROTO_ICMP, |
---|
| 240 | + .saddr = iph->daddr, |
---|
| 241 | + .daddr = iph->saddr, |
---|
| 242 | + }; |
---|
| 243 | + ip_send_check(niph); |
---|
| 244 | + skb_reset_network_header(skb); |
---|
| 245 | + |
---|
| 246 | + skb->ip_summed = CHECKSUM_NONE; |
---|
| 247 | + |
---|
| 248 | + eth_header(skb, skb->dev, htons(eh.h_proto), eh.h_source, eh.h_dest, 0); |
---|
| 249 | + skb_reset_mac_header(skb); |
---|
| 250 | + |
---|
| 251 | + return skb->len; |
---|
| 252 | +} |
---|
| 253 | + |
---|
| 254 | +/** |
---|
| 255 | + * iptunnel_pmtud_check_icmp() - Trigger ICMP reply if needed and allowed |
---|
| 256 | + * @skb: Buffer being sent by encapsulation, L2 headers expected |
---|
| 257 | + * @mtu: Network MTU for path |
---|
| 258 | + * |
---|
| 259 | + * Return: 0 for no ICMP reply, length if built, negative value on error. |
---|
| 260 | + */ |
---|
| 261 | +static int iptunnel_pmtud_check_icmp(struct sk_buff *skb, int mtu) |
---|
| 262 | +{ |
---|
| 263 | + const struct icmphdr *icmph = icmp_hdr(skb); |
---|
| 264 | + const struct iphdr *iph = ip_hdr(skb); |
---|
| 265 | + |
---|
| 266 | + if (mtu < 576 || iph->frag_off != htons(IP_DF)) |
---|
| 267 | + return 0; |
---|
| 268 | + |
---|
| 269 | + if (ipv4_is_lbcast(iph->daddr) || ipv4_is_multicast(iph->daddr) || |
---|
| 270 | + ipv4_is_zeronet(iph->saddr) || ipv4_is_loopback(iph->saddr) || |
---|
| 271 | + ipv4_is_lbcast(iph->saddr) || ipv4_is_multicast(iph->saddr)) |
---|
| 272 | + return 0; |
---|
| 273 | + |
---|
| 274 | + if (iph->protocol == IPPROTO_ICMP && icmp_is_err(icmph->type)) |
---|
| 275 | + return 0; |
---|
| 276 | + |
---|
| 277 | + return iptunnel_pmtud_build_icmp(skb, mtu); |
---|
| 278 | +} |
---|
| 279 | + |
---|
| 280 | +#if IS_ENABLED(CONFIG_IPV6) |
---|
| 281 | +/** |
---|
| 282 | + * iptunnel_pmtud_build_icmpv6() - Build ICMPv6 error message for PMTUD |
---|
| 283 | + * @skb: Original packet with L2 header |
---|
| 284 | + * @mtu: MTU value for ICMPv6 error |
---|
| 285 | + * |
---|
| 286 | + * Return: length on success, negative error code if message couldn't be built. |
---|
| 287 | + */ |
---|
| 288 | +static int iptunnel_pmtud_build_icmpv6(struct sk_buff *skb, int mtu) |
---|
| 289 | +{ |
---|
| 290 | + const struct ipv6hdr *ip6h = ipv6_hdr(skb); |
---|
| 291 | + struct icmp6hdr *icmp6h; |
---|
| 292 | + struct ipv6hdr *nip6h; |
---|
| 293 | + struct ethhdr eh; |
---|
| 294 | + int len, err; |
---|
| 295 | + __wsum csum; |
---|
| 296 | + |
---|
| 297 | + if (!pskb_may_pull(skb, ETH_HLEN + sizeof(struct ipv6hdr))) |
---|
| 298 | + return -EINVAL; |
---|
| 299 | + |
---|
| 300 | + skb_copy_bits(skb, skb_mac_offset(skb), &eh, ETH_HLEN); |
---|
| 301 | + pskb_pull(skb, ETH_HLEN); |
---|
| 302 | + skb_reset_network_header(skb); |
---|
| 303 | + |
---|
| 304 | + err = pskb_trim(skb, IPV6_MIN_MTU - sizeof(*nip6h) - sizeof(*icmp6h)); |
---|
| 305 | + if (err) |
---|
| 306 | + return err; |
---|
| 307 | + |
---|
| 308 | + len = skb->len + sizeof(*icmp6h); |
---|
| 309 | + err = skb_cow(skb, sizeof(*nip6h) + sizeof(*icmp6h) + ETH_HLEN); |
---|
| 310 | + if (err) |
---|
| 311 | + return err; |
---|
| 312 | + |
---|
| 313 | + icmp6h = skb_push(skb, sizeof(*icmp6h)); |
---|
| 314 | + *icmp6h = (struct icmp6hdr) { |
---|
| 315 | + .icmp6_type = ICMPV6_PKT_TOOBIG, |
---|
| 316 | + .icmp6_code = 0, |
---|
| 317 | + .icmp6_cksum = 0, |
---|
| 318 | + .icmp6_mtu = htonl(mtu), |
---|
| 319 | + }; |
---|
| 320 | + skb_reset_transport_header(skb); |
---|
| 321 | + |
---|
| 322 | + nip6h = skb_push(skb, sizeof(*nip6h)); |
---|
| 323 | + *nip6h = (struct ipv6hdr) { |
---|
| 324 | + .priority = 0, |
---|
| 325 | + .version = 6, |
---|
| 326 | + .flow_lbl = { 0 }, |
---|
| 327 | + .payload_len = htons(len), |
---|
| 328 | + .nexthdr = IPPROTO_ICMPV6, |
---|
| 329 | + .hop_limit = ip6h->hop_limit, |
---|
| 330 | + .saddr = ip6h->daddr, |
---|
| 331 | + .daddr = ip6h->saddr, |
---|
| 332 | + }; |
---|
| 333 | + skb_reset_network_header(skb); |
---|
| 334 | + |
---|
| 335 | + csum = csum_partial(icmp6h, len, 0); |
---|
| 336 | + icmp6h->icmp6_cksum = csum_ipv6_magic(&nip6h->saddr, &nip6h->daddr, len, |
---|
| 337 | + IPPROTO_ICMPV6, csum); |
---|
| 338 | + |
---|
| 339 | + skb->ip_summed = CHECKSUM_NONE; |
---|
| 340 | + |
---|
| 341 | + eth_header(skb, skb->dev, htons(eh.h_proto), eh.h_source, eh.h_dest, 0); |
---|
| 342 | + skb_reset_mac_header(skb); |
---|
| 343 | + |
---|
| 344 | + return skb->len; |
---|
| 345 | +} |
---|
| 346 | + |
---|
| 347 | +/** |
---|
| 348 | + * iptunnel_pmtud_check_icmpv6() - Trigger ICMPv6 reply if needed and allowed |
---|
| 349 | + * @skb: Buffer being sent by encapsulation, L2 headers expected |
---|
| 350 | + * @mtu: Network MTU for path |
---|
| 351 | + * |
---|
| 352 | + * Return: 0 for no ICMPv6 reply, length if built, negative value on error. |
---|
| 353 | + */ |
---|
| 354 | +static int iptunnel_pmtud_check_icmpv6(struct sk_buff *skb, int mtu) |
---|
| 355 | +{ |
---|
| 356 | + const struct ipv6hdr *ip6h = ipv6_hdr(skb); |
---|
| 357 | + int stype = ipv6_addr_type(&ip6h->saddr); |
---|
| 358 | + u8 proto = ip6h->nexthdr; |
---|
| 359 | + __be16 frag_off; |
---|
| 360 | + int offset; |
---|
| 361 | + |
---|
| 362 | + if (mtu < IPV6_MIN_MTU) |
---|
| 363 | + return 0; |
---|
| 364 | + |
---|
| 365 | + if (stype == IPV6_ADDR_ANY || stype == IPV6_ADDR_MULTICAST || |
---|
| 366 | + stype == IPV6_ADDR_LOOPBACK) |
---|
| 367 | + return 0; |
---|
| 368 | + |
---|
| 369 | + offset = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &proto, |
---|
| 370 | + &frag_off); |
---|
| 371 | + if (offset < 0 || (frag_off & htons(~0x7))) |
---|
| 372 | + return 0; |
---|
| 373 | + |
---|
| 374 | + if (proto == IPPROTO_ICMPV6) { |
---|
| 375 | + struct icmp6hdr *icmp6h; |
---|
| 376 | + |
---|
| 377 | + if (!pskb_may_pull(skb, skb_network_header(skb) + |
---|
| 378 | + offset + 1 - skb->data)) |
---|
| 379 | + return 0; |
---|
| 380 | + |
---|
| 381 | + icmp6h = (struct icmp6hdr *)(skb_network_header(skb) + offset); |
---|
| 382 | + if (icmpv6_is_err(icmp6h->icmp6_type) || |
---|
| 383 | + icmp6h->icmp6_type == NDISC_REDIRECT) |
---|
| 384 | + return 0; |
---|
| 385 | + } |
---|
| 386 | + |
---|
| 387 | + return iptunnel_pmtud_build_icmpv6(skb, mtu); |
---|
| 388 | +} |
---|
| 389 | +#endif /* IS_ENABLED(CONFIG_IPV6) */ |
---|
| 390 | + |
---|
| 391 | +/** |
---|
| 392 | + * skb_tunnel_check_pmtu() - Check, update PMTU and trigger ICMP reply as needed |
---|
| 393 | + * @skb: Buffer being sent by encapsulation, L2 headers expected |
---|
| 394 | + * @encap_dst: Destination for tunnel encapsulation (outer IP) |
---|
| 395 | + * @headroom: Encapsulation header size, bytes |
---|
| 396 | + * @reply: Build matching ICMP or ICMPv6 message as a result |
---|
| 397 | + * |
---|
| 398 | + * L2 tunnel implementations that can carry IP and can be directly bridged |
---|
| 399 | + * (currently UDP tunnels) can't always rely on IP forwarding paths to handle |
---|
| 400 | + * PMTU discovery. In the bridged case, ICMP or ICMPv6 messages need to be built |
---|
| 401 | + * based on payload and sent back by the encapsulation itself. |
---|
| 402 | + * |
---|
| 403 | + * For routable interfaces, we just need to update the PMTU for the destination. |
---|
| 404 | + * |
---|
| 405 | + * Return: 0 if ICMP error not needed, length if built, negative value on error |
---|
| 406 | + */ |
---|
| 407 | +int skb_tunnel_check_pmtu(struct sk_buff *skb, struct dst_entry *encap_dst, |
---|
| 408 | + int headroom, bool reply) |
---|
| 409 | +{ |
---|
| 410 | + u32 mtu = dst_mtu(encap_dst) - headroom; |
---|
| 411 | + |
---|
| 412 | + if ((skb_is_gso(skb) && skb_gso_validate_network_len(skb, mtu)) || |
---|
| 413 | + (!skb_is_gso(skb) && (skb->len - skb_network_offset(skb)) <= mtu)) |
---|
| 414 | + return 0; |
---|
| 415 | + |
---|
| 416 | + skb_dst_update_pmtu_no_confirm(skb, mtu); |
---|
| 417 | + |
---|
| 418 | + if (!reply || skb->pkt_type == PACKET_HOST) |
---|
| 419 | + return 0; |
---|
| 420 | + |
---|
| 421 | + if (skb->protocol == htons(ETH_P_IP)) |
---|
| 422 | + return iptunnel_pmtud_check_icmp(skb, mtu); |
---|
| 423 | + |
---|
| 424 | +#if IS_ENABLED(CONFIG_IPV6) |
---|
| 425 | + if (skb->protocol == htons(ETH_P_IPV6)) |
---|
| 426 | + return iptunnel_pmtud_check_icmpv6(skb, mtu); |
---|
| 427 | +#endif |
---|
| 428 | + return 0; |
---|
| 429 | +} |
---|
| 430 | +EXPORT_SYMBOL(skb_tunnel_check_pmtu); |
---|
| 431 | + |
---|
195 | 432 | /* Often modified stats are per cpu, other are shared (netdev->stats) */ |
---|
196 | 433 | void ip_tunnel_get_stats64(struct net_device *dev, |
---|
197 | 434 | struct rtnl_link_stats64 *tot) |
---|
198 | 435 | { |
---|
199 | | - int i; |
---|
200 | | - |
---|
201 | 436 | netdev_stats_to_stats64(tot, &dev->stats); |
---|
202 | | - |
---|
203 | | - for_each_possible_cpu(i) { |
---|
204 | | - const struct pcpu_sw_netstats *tstats = |
---|
205 | | - per_cpu_ptr(dev->tstats, i); |
---|
206 | | - u64 rx_packets, rx_bytes, tx_packets, tx_bytes; |
---|
207 | | - unsigned int start; |
---|
208 | | - |
---|
209 | | - do { |
---|
210 | | - start = u64_stats_fetch_begin_irq(&tstats->syncp); |
---|
211 | | - rx_packets = tstats->rx_packets; |
---|
212 | | - tx_packets = tstats->tx_packets; |
---|
213 | | - rx_bytes = tstats->rx_bytes; |
---|
214 | | - tx_bytes = tstats->tx_bytes; |
---|
215 | | - } while (u64_stats_fetch_retry_irq(&tstats->syncp, start)); |
---|
216 | | - |
---|
217 | | - tot->rx_packets += rx_packets; |
---|
218 | | - tot->tx_packets += tx_packets; |
---|
219 | | - tot->rx_bytes += rx_bytes; |
---|
220 | | - tot->tx_bytes += tx_bytes; |
---|
221 | | - } |
---|
| 437 | + dev_fetch_sw_netstats(tot, dev->tstats); |
---|
222 | 438 | } |
---|
223 | 439 | EXPORT_SYMBOL_GPL(ip_tunnel_get_stats64); |
---|
224 | 440 | |
---|
225 | 441 | static const struct nla_policy ip_tun_policy[LWTUNNEL_IP_MAX + 1] = { |
---|
| 442 | + [LWTUNNEL_IP_UNSPEC] = { .strict_start_type = LWTUNNEL_IP_OPTS }, |
---|
226 | 443 | [LWTUNNEL_IP_ID] = { .type = NLA_U64 }, |
---|
227 | 444 | [LWTUNNEL_IP_DST] = { .type = NLA_U32 }, |
---|
228 | 445 | [LWTUNNEL_IP_SRC] = { .type = NLA_U32 }, |
---|
229 | 446 | [LWTUNNEL_IP_TTL] = { .type = NLA_U8 }, |
---|
230 | 447 | [LWTUNNEL_IP_TOS] = { .type = NLA_U8 }, |
---|
231 | 448 | [LWTUNNEL_IP_FLAGS] = { .type = NLA_U16 }, |
---|
| 449 | + [LWTUNNEL_IP_OPTS] = { .type = NLA_NESTED }, |
---|
232 | 450 | }; |
---|
233 | 451 | |
---|
234 | | -static int ip_tun_build_state(struct nlattr *attr, |
---|
| 452 | +static const struct nla_policy ip_opts_policy[LWTUNNEL_IP_OPTS_MAX + 1] = { |
---|
| 453 | + [LWTUNNEL_IP_OPTS_GENEVE] = { .type = NLA_NESTED }, |
---|
| 454 | + [LWTUNNEL_IP_OPTS_VXLAN] = { .type = NLA_NESTED }, |
---|
| 455 | + [LWTUNNEL_IP_OPTS_ERSPAN] = { .type = NLA_NESTED }, |
---|
| 456 | +}; |
---|
| 457 | + |
---|
| 458 | +static const struct nla_policy |
---|
| 459 | +geneve_opt_policy[LWTUNNEL_IP_OPT_GENEVE_MAX + 1] = { |
---|
| 460 | + [LWTUNNEL_IP_OPT_GENEVE_CLASS] = { .type = NLA_U16 }, |
---|
| 461 | + [LWTUNNEL_IP_OPT_GENEVE_TYPE] = { .type = NLA_U8 }, |
---|
| 462 | + [LWTUNNEL_IP_OPT_GENEVE_DATA] = { .type = NLA_BINARY, .len = 128 }, |
---|
| 463 | +}; |
---|
| 464 | + |
---|
| 465 | +static const struct nla_policy |
---|
| 466 | +vxlan_opt_policy[LWTUNNEL_IP_OPT_VXLAN_MAX + 1] = { |
---|
| 467 | + [LWTUNNEL_IP_OPT_VXLAN_GBP] = { .type = NLA_U32 }, |
---|
| 468 | +}; |
---|
| 469 | + |
---|
| 470 | +static const struct nla_policy |
---|
| 471 | +erspan_opt_policy[LWTUNNEL_IP_OPT_ERSPAN_MAX + 1] = { |
---|
| 472 | + [LWTUNNEL_IP_OPT_ERSPAN_VER] = { .type = NLA_U8 }, |
---|
| 473 | + [LWTUNNEL_IP_OPT_ERSPAN_INDEX] = { .type = NLA_U32 }, |
---|
| 474 | + [LWTUNNEL_IP_OPT_ERSPAN_DIR] = { .type = NLA_U8 }, |
---|
| 475 | + [LWTUNNEL_IP_OPT_ERSPAN_HWID] = { .type = NLA_U8 }, |
---|
| 476 | +}; |
---|
| 477 | + |
---|
| 478 | +static int ip_tun_parse_opts_geneve(struct nlattr *attr, |
---|
| 479 | + struct ip_tunnel_info *info, int opts_len, |
---|
| 480 | + struct netlink_ext_ack *extack) |
---|
| 481 | +{ |
---|
| 482 | + struct nlattr *tb[LWTUNNEL_IP_OPT_GENEVE_MAX + 1]; |
---|
| 483 | + int data_len, err; |
---|
| 484 | + |
---|
| 485 | + err = nla_parse_nested(tb, LWTUNNEL_IP_OPT_GENEVE_MAX, attr, |
---|
| 486 | + geneve_opt_policy, extack); |
---|
| 487 | + if (err) |
---|
| 488 | + return err; |
---|
| 489 | + |
---|
| 490 | + if (!tb[LWTUNNEL_IP_OPT_GENEVE_CLASS] || |
---|
| 491 | + !tb[LWTUNNEL_IP_OPT_GENEVE_TYPE] || |
---|
| 492 | + !tb[LWTUNNEL_IP_OPT_GENEVE_DATA]) |
---|
| 493 | + return -EINVAL; |
---|
| 494 | + |
---|
| 495 | + attr = tb[LWTUNNEL_IP_OPT_GENEVE_DATA]; |
---|
| 496 | + data_len = nla_len(attr); |
---|
| 497 | + if (data_len % 4) |
---|
| 498 | + return -EINVAL; |
---|
| 499 | + |
---|
| 500 | + if (info) { |
---|
| 501 | + struct geneve_opt *opt = ip_tunnel_info_opts(info) + opts_len; |
---|
| 502 | + |
---|
| 503 | + memcpy(opt->opt_data, nla_data(attr), data_len); |
---|
| 504 | + opt->length = data_len / 4; |
---|
| 505 | + attr = tb[LWTUNNEL_IP_OPT_GENEVE_CLASS]; |
---|
| 506 | + opt->opt_class = nla_get_be16(attr); |
---|
| 507 | + attr = tb[LWTUNNEL_IP_OPT_GENEVE_TYPE]; |
---|
| 508 | + opt->type = nla_get_u8(attr); |
---|
| 509 | + info->key.tun_flags |= TUNNEL_GENEVE_OPT; |
---|
| 510 | + } |
---|
| 511 | + |
---|
| 512 | + return sizeof(struct geneve_opt) + data_len; |
---|
| 513 | +} |
---|
| 514 | + |
---|
| 515 | +static int ip_tun_parse_opts_vxlan(struct nlattr *attr, |
---|
| 516 | + struct ip_tunnel_info *info, int opts_len, |
---|
| 517 | + struct netlink_ext_ack *extack) |
---|
| 518 | +{ |
---|
| 519 | + struct nlattr *tb[LWTUNNEL_IP_OPT_VXLAN_MAX + 1]; |
---|
| 520 | + int err; |
---|
| 521 | + |
---|
| 522 | + err = nla_parse_nested(tb, LWTUNNEL_IP_OPT_VXLAN_MAX, attr, |
---|
| 523 | + vxlan_opt_policy, extack); |
---|
| 524 | + if (err) |
---|
| 525 | + return err; |
---|
| 526 | + |
---|
| 527 | + if (!tb[LWTUNNEL_IP_OPT_VXLAN_GBP]) |
---|
| 528 | + return -EINVAL; |
---|
| 529 | + |
---|
| 530 | + if (info) { |
---|
| 531 | + struct vxlan_metadata *md = |
---|
| 532 | + ip_tunnel_info_opts(info) + opts_len; |
---|
| 533 | + |
---|
| 534 | + attr = tb[LWTUNNEL_IP_OPT_VXLAN_GBP]; |
---|
| 535 | + md->gbp = nla_get_u32(attr); |
---|
| 536 | + md->gbp &= VXLAN_GBP_MASK; |
---|
| 537 | + info->key.tun_flags |= TUNNEL_VXLAN_OPT; |
---|
| 538 | + } |
---|
| 539 | + |
---|
| 540 | + return sizeof(struct vxlan_metadata); |
---|
| 541 | +} |
---|
| 542 | + |
---|
| 543 | +static int ip_tun_parse_opts_erspan(struct nlattr *attr, |
---|
| 544 | + struct ip_tunnel_info *info, int opts_len, |
---|
| 545 | + struct netlink_ext_ack *extack) |
---|
| 546 | +{ |
---|
| 547 | + struct nlattr *tb[LWTUNNEL_IP_OPT_ERSPAN_MAX + 1]; |
---|
| 548 | + int err; |
---|
| 549 | + u8 ver; |
---|
| 550 | + |
---|
| 551 | + err = nla_parse_nested(tb, LWTUNNEL_IP_OPT_ERSPAN_MAX, attr, |
---|
| 552 | + erspan_opt_policy, extack); |
---|
| 553 | + if (err) |
---|
| 554 | + return err; |
---|
| 555 | + |
---|
| 556 | + if (!tb[LWTUNNEL_IP_OPT_ERSPAN_VER]) |
---|
| 557 | + return -EINVAL; |
---|
| 558 | + |
---|
| 559 | + ver = nla_get_u8(tb[LWTUNNEL_IP_OPT_ERSPAN_VER]); |
---|
| 560 | + if (ver == 1) { |
---|
| 561 | + if (!tb[LWTUNNEL_IP_OPT_ERSPAN_INDEX]) |
---|
| 562 | + return -EINVAL; |
---|
| 563 | + } else if (ver == 2) { |
---|
| 564 | + if (!tb[LWTUNNEL_IP_OPT_ERSPAN_DIR] || |
---|
| 565 | + !tb[LWTUNNEL_IP_OPT_ERSPAN_HWID]) |
---|
| 566 | + return -EINVAL; |
---|
| 567 | + } else { |
---|
| 568 | + return -EINVAL; |
---|
| 569 | + } |
---|
| 570 | + |
---|
| 571 | + if (info) { |
---|
| 572 | + struct erspan_metadata *md = |
---|
| 573 | + ip_tunnel_info_opts(info) + opts_len; |
---|
| 574 | + |
---|
| 575 | + md->version = ver; |
---|
| 576 | + if (ver == 1) { |
---|
| 577 | + attr = tb[LWTUNNEL_IP_OPT_ERSPAN_INDEX]; |
---|
| 578 | + md->u.index = nla_get_be32(attr); |
---|
| 579 | + } else { |
---|
| 580 | + attr = tb[LWTUNNEL_IP_OPT_ERSPAN_DIR]; |
---|
| 581 | + md->u.md2.dir = nla_get_u8(attr); |
---|
| 582 | + attr = tb[LWTUNNEL_IP_OPT_ERSPAN_HWID]; |
---|
| 583 | + set_hwid(&md->u.md2, nla_get_u8(attr)); |
---|
| 584 | + } |
---|
| 585 | + |
---|
| 586 | + info->key.tun_flags |= TUNNEL_ERSPAN_OPT; |
---|
| 587 | + } |
---|
| 588 | + |
---|
| 589 | + return sizeof(struct erspan_metadata); |
---|
| 590 | +} |
---|
| 591 | + |
---|
| 592 | +static int ip_tun_parse_opts(struct nlattr *attr, struct ip_tunnel_info *info, |
---|
| 593 | + struct netlink_ext_ack *extack) |
---|
| 594 | +{ |
---|
| 595 | + int err, rem, opt_len, opts_len = 0, type = 0; |
---|
| 596 | + struct nlattr *nla; |
---|
| 597 | + |
---|
| 598 | + if (!attr) |
---|
| 599 | + return 0; |
---|
| 600 | + |
---|
| 601 | + err = nla_validate(nla_data(attr), nla_len(attr), LWTUNNEL_IP_OPTS_MAX, |
---|
| 602 | + ip_opts_policy, extack); |
---|
| 603 | + if (err) |
---|
| 604 | + return err; |
---|
| 605 | + |
---|
| 606 | + nla_for_each_attr(nla, nla_data(attr), nla_len(attr), rem) { |
---|
| 607 | + switch (nla_type(nla)) { |
---|
| 608 | + case LWTUNNEL_IP_OPTS_GENEVE: |
---|
| 609 | + if (type && type != TUNNEL_GENEVE_OPT) |
---|
| 610 | + return -EINVAL; |
---|
| 611 | + opt_len = ip_tun_parse_opts_geneve(nla, info, opts_len, |
---|
| 612 | + extack); |
---|
| 613 | + if (opt_len < 0) |
---|
| 614 | + return opt_len; |
---|
| 615 | + opts_len += opt_len; |
---|
| 616 | + if (opts_len > IP_TUNNEL_OPTS_MAX) |
---|
| 617 | + return -EINVAL; |
---|
| 618 | + type = TUNNEL_GENEVE_OPT; |
---|
| 619 | + break; |
---|
| 620 | + case LWTUNNEL_IP_OPTS_VXLAN: |
---|
| 621 | + if (type) |
---|
| 622 | + return -EINVAL; |
---|
| 623 | + opt_len = ip_tun_parse_opts_vxlan(nla, info, opts_len, |
---|
| 624 | + extack); |
---|
| 625 | + if (opt_len < 0) |
---|
| 626 | + return opt_len; |
---|
| 627 | + opts_len += opt_len; |
---|
| 628 | + type = TUNNEL_VXLAN_OPT; |
---|
| 629 | + break; |
---|
| 630 | + case LWTUNNEL_IP_OPTS_ERSPAN: |
---|
| 631 | + if (type) |
---|
| 632 | + return -EINVAL; |
---|
| 633 | + opt_len = ip_tun_parse_opts_erspan(nla, info, opts_len, |
---|
| 634 | + extack); |
---|
| 635 | + if (opt_len < 0) |
---|
| 636 | + return opt_len; |
---|
| 637 | + opts_len += opt_len; |
---|
| 638 | + type = TUNNEL_ERSPAN_OPT; |
---|
| 639 | + break; |
---|
| 640 | + default: |
---|
| 641 | + return -EINVAL; |
---|
| 642 | + } |
---|
| 643 | + } |
---|
| 644 | + |
---|
| 645 | + return opts_len; |
---|
| 646 | +} |
---|
| 647 | + |
---|
| 648 | +static int ip_tun_get_optlen(struct nlattr *attr, |
---|
| 649 | + struct netlink_ext_ack *extack) |
---|
| 650 | +{ |
---|
| 651 | + return ip_tun_parse_opts(attr, NULL, extack); |
---|
| 652 | +} |
---|
| 653 | + |
---|
| 654 | +static int ip_tun_set_opts(struct nlattr *attr, struct ip_tunnel_info *info, |
---|
| 655 | + struct netlink_ext_ack *extack) |
---|
| 656 | +{ |
---|
| 657 | + return ip_tun_parse_opts(attr, info, extack); |
---|
| 658 | +} |
---|
| 659 | + |
---|
| 660 | +static int ip_tun_build_state(struct net *net, struct nlattr *attr, |
---|
235 | 661 | unsigned int family, const void *cfg, |
---|
236 | 662 | struct lwtunnel_state **ts, |
---|
237 | 663 | struct netlink_ext_ack *extack) |
---|
238 | 664 | { |
---|
239 | | - struct ip_tunnel_info *tun_info; |
---|
240 | | - struct lwtunnel_state *new_state; |
---|
241 | 665 | struct nlattr *tb[LWTUNNEL_IP_MAX + 1]; |
---|
242 | | - int err; |
---|
| 666 | + struct lwtunnel_state *new_state; |
---|
| 667 | + struct ip_tunnel_info *tun_info; |
---|
| 668 | + int err, opt_len; |
---|
243 | 669 | |
---|
244 | | - err = nla_parse_nested(tb, LWTUNNEL_IP_MAX, attr, ip_tun_policy, |
---|
245 | | - extack); |
---|
| 670 | + err = nla_parse_nested_deprecated(tb, LWTUNNEL_IP_MAX, attr, |
---|
| 671 | + ip_tun_policy, extack); |
---|
246 | 672 | if (err < 0) |
---|
247 | 673 | return err; |
---|
248 | 674 | |
---|
249 | | - new_state = lwtunnel_state_alloc(sizeof(*tun_info)); |
---|
| 675 | + opt_len = ip_tun_get_optlen(tb[LWTUNNEL_IP_OPTS], extack); |
---|
| 676 | + if (opt_len < 0) |
---|
| 677 | + return opt_len; |
---|
| 678 | + |
---|
| 679 | + new_state = lwtunnel_state_alloc(sizeof(*tun_info) + opt_len); |
---|
250 | 680 | if (!new_state) |
---|
251 | 681 | return -ENOMEM; |
---|
252 | 682 | |
---|
253 | 683 | new_state->type = LWTUNNEL_ENCAP_IP; |
---|
254 | 684 | |
---|
255 | 685 | tun_info = lwt_tun_info(new_state); |
---|
| 686 | + |
---|
| 687 | + err = ip_tun_set_opts(tb[LWTUNNEL_IP_OPTS], tun_info, extack); |
---|
| 688 | + if (err < 0) { |
---|
| 689 | + lwtstate_free(new_state); |
---|
| 690 | + return err; |
---|
| 691 | + } |
---|
| 692 | + |
---|
| 693 | +#ifdef CONFIG_DST_CACHE |
---|
| 694 | + err = dst_cache_init(&tun_info->dst_cache, GFP_KERNEL); |
---|
| 695 | + if (err) { |
---|
| 696 | + lwtstate_free(new_state); |
---|
| 697 | + return err; |
---|
| 698 | + } |
---|
| 699 | +#endif |
---|
256 | 700 | |
---|
257 | 701 | if (tb[LWTUNNEL_IP_ID]) |
---|
258 | 702 | tun_info->key.tun_id = nla_get_be64(tb[LWTUNNEL_IP_ID]); |
---|
.. | .. |
---|
270 | 714 | tun_info->key.tos = nla_get_u8(tb[LWTUNNEL_IP_TOS]); |
---|
271 | 715 | |
---|
272 | 716 | if (tb[LWTUNNEL_IP_FLAGS]) |
---|
273 | | - tun_info->key.tun_flags = nla_get_be16(tb[LWTUNNEL_IP_FLAGS]); |
---|
| 717 | + tun_info->key.tun_flags |= |
---|
| 718 | + (nla_get_be16(tb[LWTUNNEL_IP_FLAGS]) & |
---|
| 719 | + ~TUNNEL_OPTIONS_PRESENT); |
---|
274 | 720 | |
---|
275 | 721 | tun_info->mode = IP_TUNNEL_INFO_TX; |
---|
276 | | - tun_info->options_len = 0; |
---|
| 722 | + tun_info->options_len = opt_len; |
---|
277 | 723 | |
---|
278 | 724 | *ts = new_state; |
---|
279 | 725 | |
---|
| 726 | + return 0; |
---|
| 727 | +} |
---|
| 728 | + |
---|
| 729 | +static void ip_tun_destroy_state(struct lwtunnel_state *lwtstate) |
---|
| 730 | +{ |
---|
| 731 | +#ifdef CONFIG_DST_CACHE |
---|
| 732 | + struct ip_tunnel_info *tun_info = lwt_tun_info(lwtstate); |
---|
| 733 | + |
---|
| 734 | + dst_cache_destroy(&tun_info->dst_cache); |
---|
| 735 | +#endif |
---|
| 736 | +} |
---|
| 737 | + |
---|
| 738 | +static int ip_tun_fill_encap_opts_geneve(struct sk_buff *skb, |
---|
| 739 | + struct ip_tunnel_info *tun_info) |
---|
| 740 | +{ |
---|
| 741 | + struct geneve_opt *opt; |
---|
| 742 | + struct nlattr *nest; |
---|
| 743 | + int offset = 0; |
---|
| 744 | + |
---|
| 745 | + nest = nla_nest_start_noflag(skb, LWTUNNEL_IP_OPTS_GENEVE); |
---|
| 746 | + if (!nest) |
---|
| 747 | + return -ENOMEM; |
---|
| 748 | + |
---|
| 749 | + while (tun_info->options_len > offset) { |
---|
| 750 | + opt = ip_tunnel_info_opts(tun_info) + offset; |
---|
| 751 | + if (nla_put_be16(skb, LWTUNNEL_IP_OPT_GENEVE_CLASS, |
---|
| 752 | + opt->opt_class) || |
---|
| 753 | + nla_put_u8(skb, LWTUNNEL_IP_OPT_GENEVE_TYPE, opt->type) || |
---|
| 754 | + nla_put(skb, LWTUNNEL_IP_OPT_GENEVE_DATA, opt->length * 4, |
---|
| 755 | + opt->opt_data)) { |
---|
| 756 | + nla_nest_cancel(skb, nest); |
---|
| 757 | + return -ENOMEM; |
---|
| 758 | + } |
---|
| 759 | + offset += sizeof(*opt) + opt->length * 4; |
---|
| 760 | + } |
---|
| 761 | + |
---|
| 762 | + nla_nest_end(skb, nest); |
---|
| 763 | + return 0; |
---|
| 764 | +} |
---|
| 765 | + |
---|
| 766 | +static int ip_tun_fill_encap_opts_vxlan(struct sk_buff *skb, |
---|
| 767 | + struct ip_tunnel_info *tun_info) |
---|
| 768 | +{ |
---|
| 769 | + struct vxlan_metadata *md; |
---|
| 770 | + struct nlattr *nest; |
---|
| 771 | + |
---|
| 772 | + nest = nla_nest_start_noflag(skb, LWTUNNEL_IP_OPTS_VXLAN); |
---|
| 773 | + if (!nest) |
---|
| 774 | + return -ENOMEM; |
---|
| 775 | + |
---|
| 776 | + md = ip_tunnel_info_opts(tun_info); |
---|
| 777 | + if (nla_put_u32(skb, LWTUNNEL_IP_OPT_VXLAN_GBP, md->gbp)) { |
---|
| 778 | + nla_nest_cancel(skb, nest); |
---|
| 779 | + return -ENOMEM; |
---|
| 780 | + } |
---|
| 781 | + |
---|
| 782 | + nla_nest_end(skb, nest); |
---|
| 783 | + return 0; |
---|
| 784 | +} |
---|
| 785 | + |
---|
| 786 | +static int ip_tun_fill_encap_opts_erspan(struct sk_buff *skb, |
---|
| 787 | + struct ip_tunnel_info *tun_info) |
---|
| 788 | +{ |
---|
| 789 | + struct erspan_metadata *md; |
---|
| 790 | + struct nlattr *nest; |
---|
| 791 | + |
---|
| 792 | + nest = nla_nest_start_noflag(skb, LWTUNNEL_IP_OPTS_ERSPAN); |
---|
| 793 | + if (!nest) |
---|
| 794 | + return -ENOMEM; |
---|
| 795 | + |
---|
| 796 | + md = ip_tunnel_info_opts(tun_info); |
---|
| 797 | + if (nla_put_u8(skb, LWTUNNEL_IP_OPT_ERSPAN_VER, md->version)) |
---|
| 798 | + goto err; |
---|
| 799 | + |
---|
| 800 | + if (md->version == 1 && |
---|
| 801 | + nla_put_be32(skb, LWTUNNEL_IP_OPT_ERSPAN_INDEX, md->u.index)) |
---|
| 802 | + goto err; |
---|
| 803 | + |
---|
| 804 | + if (md->version == 2 && |
---|
| 805 | + (nla_put_u8(skb, LWTUNNEL_IP_OPT_ERSPAN_DIR, md->u.md2.dir) || |
---|
| 806 | + nla_put_u8(skb, LWTUNNEL_IP_OPT_ERSPAN_HWID, |
---|
| 807 | + get_hwid(&md->u.md2)))) |
---|
| 808 | + goto err; |
---|
| 809 | + |
---|
| 810 | + nla_nest_end(skb, nest); |
---|
| 811 | + return 0; |
---|
| 812 | +err: |
---|
| 813 | + nla_nest_cancel(skb, nest); |
---|
| 814 | + return -ENOMEM; |
---|
| 815 | +} |
---|
| 816 | + |
---|
| 817 | +static int ip_tun_fill_encap_opts(struct sk_buff *skb, int type, |
---|
| 818 | + struct ip_tunnel_info *tun_info) |
---|
| 819 | +{ |
---|
| 820 | + struct nlattr *nest; |
---|
| 821 | + int err = 0; |
---|
| 822 | + |
---|
| 823 | + if (!(tun_info->key.tun_flags & TUNNEL_OPTIONS_PRESENT)) |
---|
| 824 | + return 0; |
---|
| 825 | + |
---|
| 826 | + nest = nla_nest_start_noflag(skb, type); |
---|
| 827 | + if (!nest) |
---|
| 828 | + return -ENOMEM; |
---|
| 829 | + |
---|
| 830 | + if (tun_info->key.tun_flags & TUNNEL_GENEVE_OPT) |
---|
| 831 | + err = ip_tun_fill_encap_opts_geneve(skb, tun_info); |
---|
| 832 | + else if (tun_info->key.tun_flags & TUNNEL_VXLAN_OPT) |
---|
| 833 | + err = ip_tun_fill_encap_opts_vxlan(skb, tun_info); |
---|
| 834 | + else if (tun_info->key.tun_flags & TUNNEL_ERSPAN_OPT) |
---|
| 835 | + err = ip_tun_fill_encap_opts_erspan(skb, tun_info); |
---|
| 836 | + |
---|
| 837 | + if (err) { |
---|
| 838 | + nla_nest_cancel(skb, nest); |
---|
| 839 | + return err; |
---|
| 840 | + } |
---|
| 841 | + |
---|
| 842 | + nla_nest_end(skb, nest); |
---|
280 | 843 | return 0; |
---|
281 | 844 | } |
---|
282 | 845 | |
---|
.. | .. |
---|
291 | 854 | nla_put_in_addr(skb, LWTUNNEL_IP_SRC, tun_info->key.u.ipv4.src) || |
---|
292 | 855 | nla_put_u8(skb, LWTUNNEL_IP_TOS, tun_info->key.tos) || |
---|
293 | 856 | nla_put_u8(skb, LWTUNNEL_IP_TTL, tun_info->key.ttl) || |
---|
294 | | - nla_put_be16(skb, LWTUNNEL_IP_FLAGS, tun_info->key.tun_flags)) |
---|
| 857 | + nla_put_be16(skb, LWTUNNEL_IP_FLAGS, tun_info->key.tun_flags) || |
---|
| 858 | + ip_tun_fill_encap_opts(skb, LWTUNNEL_IP_OPTS, tun_info)) |
---|
295 | 859 | return -ENOMEM; |
---|
296 | 860 | |
---|
297 | 861 | return 0; |
---|
| 862 | +} |
---|
| 863 | + |
---|
| 864 | +static int ip_tun_opts_nlsize(struct ip_tunnel_info *info) |
---|
| 865 | +{ |
---|
| 866 | + int opt_len; |
---|
| 867 | + |
---|
| 868 | + if (!(info->key.tun_flags & TUNNEL_OPTIONS_PRESENT)) |
---|
| 869 | + return 0; |
---|
| 870 | + |
---|
| 871 | + opt_len = nla_total_size(0); /* LWTUNNEL_IP_OPTS */ |
---|
| 872 | + if (info->key.tun_flags & TUNNEL_GENEVE_OPT) { |
---|
| 873 | + struct geneve_opt *opt; |
---|
| 874 | + int offset = 0; |
---|
| 875 | + |
---|
| 876 | + opt_len += nla_total_size(0); /* LWTUNNEL_IP_OPTS_GENEVE */ |
---|
| 877 | + while (info->options_len > offset) { |
---|
| 878 | + opt = ip_tunnel_info_opts(info) + offset; |
---|
| 879 | + opt_len += nla_total_size(2) /* OPT_GENEVE_CLASS */ |
---|
| 880 | + + nla_total_size(1) /* OPT_GENEVE_TYPE */ |
---|
| 881 | + + nla_total_size(opt->length * 4); |
---|
| 882 | + /* OPT_GENEVE_DATA */ |
---|
| 883 | + offset += sizeof(*opt) + opt->length * 4; |
---|
| 884 | + } |
---|
| 885 | + } else if (info->key.tun_flags & TUNNEL_VXLAN_OPT) { |
---|
| 886 | + opt_len += nla_total_size(0) /* LWTUNNEL_IP_OPTS_VXLAN */ |
---|
| 887 | + + nla_total_size(4); /* OPT_VXLAN_GBP */ |
---|
| 888 | + } else if (info->key.tun_flags & TUNNEL_ERSPAN_OPT) { |
---|
| 889 | + struct erspan_metadata *md = ip_tunnel_info_opts(info); |
---|
| 890 | + |
---|
| 891 | + opt_len += nla_total_size(0) /* LWTUNNEL_IP_OPTS_ERSPAN */ |
---|
| 892 | + + nla_total_size(1) /* OPT_ERSPAN_VER */ |
---|
| 893 | + + (md->version == 1 ? nla_total_size(4) |
---|
| 894 | + /* OPT_ERSPAN_INDEX (v1) */ |
---|
| 895 | + : nla_total_size(1) + |
---|
| 896 | + nla_total_size(1)); |
---|
| 897 | + /* OPT_ERSPAN_DIR + HWID (v2) */ |
---|
| 898 | + } |
---|
| 899 | + |
---|
| 900 | + return opt_len; |
---|
298 | 901 | } |
---|
299 | 902 | |
---|
300 | 903 | static int ip_tun_encap_nlsize(struct lwtunnel_state *lwtstate) |
---|
.. | .. |
---|
304 | 907 | + nla_total_size(4) /* LWTUNNEL_IP_SRC */ |
---|
305 | 908 | + nla_total_size(1) /* LWTUNNEL_IP_TOS */ |
---|
306 | 909 | + nla_total_size(1) /* LWTUNNEL_IP_TTL */ |
---|
307 | | - + nla_total_size(2); /* LWTUNNEL_IP_FLAGS */ |
---|
| 910 | + + nla_total_size(2) /* LWTUNNEL_IP_FLAGS */ |
---|
| 911 | + + ip_tun_opts_nlsize(lwt_tun_info(lwtstate)); |
---|
| 912 | + /* LWTUNNEL_IP_OPTS */ |
---|
308 | 913 | } |
---|
309 | 914 | |
---|
310 | 915 | static int ip_tun_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b) |
---|
311 | 916 | { |
---|
312 | | - return memcmp(lwt_tun_info(a), lwt_tun_info(b), |
---|
313 | | - sizeof(struct ip_tunnel_info)); |
---|
| 917 | + struct ip_tunnel_info *info_a = lwt_tun_info(a); |
---|
| 918 | + struct ip_tunnel_info *info_b = lwt_tun_info(b); |
---|
| 919 | + |
---|
| 920 | + return memcmp(info_a, info_b, sizeof(info_a->key)) || |
---|
| 921 | + info_a->mode != info_b->mode || |
---|
| 922 | + info_a->options_len != info_b->options_len || |
---|
| 923 | + memcmp(ip_tunnel_info_opts(info_a), |
---|
| 924 | + ip_tunnel_info_opts(info_b), info_a->options_len); |
---|
314 | 925 | } |
---|
315 | 926 | |
---|
316 | 927 | static const struct lwtunnel_encap_ops ip_tun_lwt_ops = { |
---|
317 | 928 | .build_state = ip_tun_build_state, |
---|
| 929 | + .destroy_state = ip_tun_destroy_state, |
---|
318 | 930 | .fill_encap = ip_tun_fill_encap_info, |
---|
319 | 931 | .get_encap_size = ip_tun_encap_nlsize, |
---|
320 | 932 | .cmp_encap = ip_tun_cmp_encap, |
---|
.. | .. |
---|
322 | 934 | }; |
---|
323 | 935 | |
---|
324 | 936 | static const struct nla_policy ip6_tun_policy[LWTUNNEL_IP6_MAX + 1] = { |
---|
| 937 | + [LWTUNNEL_IP6_UNSPEC] = { .strict_start_type = LWTUNNEL_IP6_OPTS }, |
---|
325 | 938 | [LWTUNNEL_IP6_ID] = { .type = NLA_U64 }, |
---|
326 | 939 | [LWTUNNEL_IP6_DST] = { .len = sizeof(struct in6_addr) }, |
---|
327 | 940 | [LWTUNNEL_IP6_SRC] = { .len = sizeof(struct in6_addr) }, |
---|
328 | 941 | [LWTUNNEL_IP6_HOPLIMIT] = { .type = NLA_U8 }, |
---|
329 | 942 | [LWTUNNEL_IP6_TC] = { .type = NLA_U8 }, |
---|
330 | 943 | [LWTUNNEL_IP6_FLAGS] = { .type = NLA_U16 }, |
---|
| 944 | + [LWTUNNEL_IP6_OPTS] = { .type = NLA_NESTED }, |
---|
331 | 945 | }; |
---|
332 | 946 | |
---|
333 | | -static int ip6_tun_build_state(struct nlattr *attr, |
---|
| 947 | +static int ip6_tun_build_state(struct net *net, struct nlattr *attr, |
---|
334 | 948 | unsigned int family, const void *cfg, |
---|
335 | 949 | struct lwtunnel_state **ts, |
---|
336 | 950 | struct netlink_ext_ack *extack) |
---|
337 | 951 | { |
---|
338 | | - struct ip_tunnel_info *tun_info; |
---|
339 | | - struct lwtunnel_state *new_state; |
---|
340 | 952 | struct nlattr *tb[LWTUNNEL_IP6_MAX + 1]; |
---|
341 | | - int err; |
---|
| 953 | + struct lwtunnel_state *new_state; |
---|
| 954 | + struct ip_tunnel_info *tun_info; |
---|
| 955 | + int err, opt_len; |
---|
342 | 956 | |
---|
343 | | - err = nla_parse_nested(tb, LWTUNNEL_IP6_MAX, attr, ip6_tun_policy, |
---|
344 | | - extack); |
---|
| 957 | + err = nla_parse_nested_deprecated(tb, LWTUNNEL_IP6_MAX, attr, |
---|
| 958 | + ip6_tun_policy, extack); |
---|
345 | 959 | if (err < 0) |
---|
346 | 960 | return err; |
---|
347 | 961 | |
---|
348 | | - new_state = lwtunnel_state_alloc(sizeof(*tun_info)); |
---|
| 962 | + opt_len = ip_tun_get_optlen(tb[LWTUNNEL_IP6_OPTS], extack); |
---|
| 963 | + if (opt_len < 0) |
---|
| 964 | + return opt_len; |
---|
| 965 | + |
---|
| 966 | + new_state = lwtunnel_state_alloc(sizeof(*tun_info) + opt_len); |
---|
349 | 967 | if (!new_state) |
---|
350 | 968 | return -ENOMEM; |
---|
351 | 969 | |
---|
352 | 970 | new_state->type = LWTUNNEL_ENCAP_IP6; |
---|
353 | 971 | |
---|
354 | 972 | tun_info = lwt_tun_info(new_state); |
---|
| 973 | + |
---|
| 974 | + err = ip_tun_set_opts(tb[LWTUNNEL_IP6_OPTS], tun_info, extack); |
---|
| 975 | + if (err < 0) { |
---|
| 976 | + lwtstate_free(new_state); |
---|
| 977 | + return err; |
---|
| 978 | + } |
---|
355 | 979 | |
---|
356 | 980 | if (tb[LWTUNNEL_IP6_ID]) |
---|
357 | 981 | tun_info->key.tun_id = nla_get_be64(tb[LWTUNNEL_IP6_ID]); |
---|
.. | .. |
---|
369 | 993 | tun_info->key.tos = nla_get_u8(tb[LWTUNNEL_IP6_TC]); |
---|
370 | 994 | |
---|
371 | 995 | if (tb[LWTUNNEL_IP6_FLAGS]) |
---|
372 | | - tun_info->key.tun_flags = nla_get_be16(tb[LWTUNNEL_IP6_FLAGS]); |
---|
| 996 | + tun_info->key.tun_flags |= |
---|
| 997 | + (nla_get_be16(tb[LWTUNNEL_IP6_FLAGS]) & |
---|
| 998 | + ~TUNNEL_OPTIONS_PRESENT); |
---|
373 | 999 | |
---|
374 | 1000 | tun_info->mode = IP_TUNNEL_INFO_TX | IP_TUNNEL_INFO_IPV6; |
---|
375 | | - tun_info->options_len = 0; |
---|
| 1001 | + tun_info->options_len = opt_len; |
---|
376 | 1002 | |
---|
377 | 1003 | *ts = new_state; |
---|
378 | 1004 | |
---|
.. | .. |
---|
390 | 1016 | nla_put_in6_addr(skb, LWTUNNEL_IP6_SRC, &tun_info->key.u.ipv6.src) || |
---|
391 | 1017 | nla_put_u8(skb, LWTUNNEL_IP6_TC, tun_info->key.tos) || |
---|
392 | 1018 | nla_put_u8(skb, LWTUNNEL_IP6_HOPLIMIT, tun_info->key.ttl) || |
---|
393 | | - nla_put_be16(skb, LWTUNNEL_IP6_FLAGS, tun_info->key.tun_flags)) |
---|
| 1019 | + nla_put_be16(skb, LWTUNNEL_IP6_FLAGS, tun_info->key.tun_flags) || |
---|
| 1020 | + ip_tun_fill_encap_opts(skb, LWTUNNEL_IP6_OPTS, tun_info)) |
---|
394 | 1021 | return -ENOMEM; |
---|
395 | 1022 | |
---|
396 | 1023 | return 0; |
---|
.. | .. |
---|
403 | 1030 | + nla_total_size(16) /* LWTUNNEL_IP6_SRC */ |
---|
404 | 1031 | + nla_total_size(1) /* LWTUNNEL_IP6_HOPLIMIT */ |
---|
405 | 1032 | + nla_total_size(1) /* LWTUNNEL_IP6_TC */ |
---|
406 | | - + nla_total_size(2); /* LWTUNNEL_IP6_FLAGS */ |
---|
| 1033 | + + nla_total_size(2) /* LWTUNNEL_IP6_FLAGS */ |
---|
| 1034 | + + ip_tun_opts_nlsize(lwt_tun_info(lwtstate)); |
---|
| 1035 | + /* LWTUNNEL_IP6_OPTS */ |
---|
407 | 1036 | } |
---|
408 | 1037 | |
---|
409 | 1038 | static const struct lwtunnel_encap_ops ip6_tun_lwt_ops = { |
---|
.. | .. |
---|
455 | 1084 | return 0; |
---|
456 | 1085 | } |
---|
457 | 1086 | EXPORT_SYMBOL(ip_tunnel_parse_protocol); |
---|
| 1087 | + |
---|
| 1088 | +const struct header_ops ip_tunnel_header_ops = { .parse_protocol = ip_tunnel_parse_protocol }; |
---|
| 1089 | +EXPORT_SYMBOL(ip_tunnel_header_ops); |
---|