162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * IPv6 virtual tunneling interface 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013 secunet Security Networks AG 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: 862306a36Sopenharmony_ci * Steffen Klassert <steffen.klassert@secunet.com> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Based on: 1162306a36Sopenharmony_ci * net/ipv6/ip6_tunnel.c 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/capability.h> 1662306a36Sopenharmony_ci#include <linux/errno.h> 1762306a36Sopenharmony_ci#include <linux/types.h> 1862306a36Sopenharmony_ci#include <linux/sockios.h> 1962306a36Sopenharmony_ci#include <linux/icmp.h> 2062306a36Sopenharmony_ci#include <linux/if.h> 2162306a36Sopenharmony_ci#include <linux/in.h> 2262306a36Sopenharmony_ci#include <linux/ip.h> 2362306a36Sopenharmony_ci#include <linux/net.h> 2462306a36Sopenharmony_ci#include <linux/in6.h> 2562306a36Sopenharmony_ci#include <linux/netdevice.h> 2662306a36Sopenharmony_ci#include <linux/if_arp.h> 2762306a36Sopenharmony_ci#include <linux/icmpv6.h> 2862306a36Sopenharmony_ci#include <linux/init.h> 2962306a36Sopenharmony_ci#include <linux/route.h> 3062306a36Sopenharmony_ci#include <linux/rtnetlink.h> 3162306a36Sopenharmony_ci#include <linux/netfilter_ipv6.h> 3262306a36Sopenharmony_ci#include <linux/slab.h> 3362306a36Sopenharmony_ci#include <linux/hash.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <linux/uaccess.h> 3662306a36Sopenharmony_ci#include <linux/atomic.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include <net/icmp.h> 3962306a36Sopenharmony_ci#include <net/ip.h> 4062306a36Sopenharmony_ci#include <net/ip_tunnels.h> 4162306a36Sopenharmony_ci#include <net/ipv6.h> 4262306a36Sopenharmony_ci#include <net/ip6_route.h> 4362306a36Sopenharmony_ci#include <net/addrconf.h> 4462306a36Sopenharmony_ci#include <net/ip6_tunnel.h> 4562306a36Sopenharmony_ci#include <net/xfrm.h> 4662306a36Sopenharmony_ci#include <net/net_namespace.h> 4762306a36Sopenharmony_ci#include <net/netns/generic.h> 4862306a36Sopenharmony_ci#include <linux/etherdevice.h> 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define IP6_VTI_HASH_SIZE_SHIFT 5 5162306a36Sopenharmony_ci#define IP6_VTI_HASH_SIZE (1 << IP6_VTI_HASH_SIZE_SHIFT) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic u32 HASH(const struct in6_addr *addr1, const struct in6_addr *addr2) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci u32 hash = ipv6_addr_hash(addr1) ^ ipv6_addr_hash(addr2); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return hash_32(hash, IP6_VTI_HASH_SIZE_SHIFT); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic int vti6_dev_init(struct net_device *dev); 6162306a36Sopenharmony_cistatic void vti6_dev_setup(struct net_device *dev); 6262306a36Sopenharmony_cistatic struct rtnl_link_ops vti6_link_ops __read_mostly; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic unsigned int vti6_net_id __read_mostly; 6562306a36Sopenharmony_cistruct vti6_net { 6662306a36Sopenharmony_ci /* the vti6 tunnel fallback device */ 6762306a36Sopenharmony_ci struct net_device *fb_tnl_dev; 6862306a36Sopenharmony_ci /* lists for storing tunnels in use */ 6962306a36Sopenharmony_ci struct ip6_tnl __rcu *tnls_r_l[IP6_VTI_HASH_SIZE]; 7062306a36Sopenharmony_ci struct ip6_tnl __rcu *tnls_wc[1]; 7162306a36Sopenharmony_ci struct ip6_tnl __rcu **tnls[2]; 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define for_each_vti6_tunnel_rcu(start) \ 7562306a36Sopenharmony_ci for (t = rcu_dereference(start); t; t = rcu_dereference(t->next)) 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/** 7862306a36Sopenharmony_ci * vti6_tnl_lookup - fetch tunnel matching the end-point addresses 7962306a36Sopenharmony_ci * @net: network namespace 8062306a36Sopenharmony_ci * @remote: the address of the tunnel exit-point 8162306a36Sopenharmony_ci * @local: the address of the tunnel entry-point 8262306a36Sopenharmony_ci * 8362306a36Sopenharmony_ci * Return: 8462306a36Sopenharmony_ci * tunnel matching given end-points if found, 8562306a36Sopenharmony_ci * else fallback tunnel if its device is up, 8662306a36Sopenharmony_ci * else %NULL 8762306a36Sopenharmony_ci **/ 8862306a36Sopenharmony_cistatic struct ip6_tnl * 8962306a36Sopenharmony_civti6_tnl_lookup(struct net *net, const struct in6_addr *remote, 9062306a36Sopenharmony_ci const struct in6_addr *local) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci unsigned int hash = HASH(remote, local); 9362306a36Sopenharmony_ci struct ip6_tnl *t; 9462306a36Sopenharmony_ci struct vti6_net *ip6n = net_generic(net, vti6_net_id); 9562306a36Sopenharmony_ci struct in6_addr any; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci for_each_vti6_tunnel_rcu(ip6n->tnls_r_l[hash]) { 9862306a36Sopenharmony_ci if (ipv6_addr_equal(local, &t->parms.laddr) && 9962306a36Sopenharmony_ci ipv6_addr_equal(remote, &t->parms.raddr) && 10062306a36Sopenharmony_ci (t->dev->flags & IFF_UP)) 10162306a36Sopenharmony_ci return t; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci memset(&any, 0, sizeof(any)); 10562306a36Sopenharmony_ci hash = HASH(&any, local); 10662306a36Sopenharmony_ci for_each_vti6_tunnel_rcu(ip6n->tnls_r_l[hash]) { 10762306a36Sopenharmony_ci if (ipv6_addr_equal(local, &t->parms.laddr) && 10862306a36Sopenharmony_ci (t->dev->flags & IFF_UP)) 10962306a36Sopenharmony_ci return t; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci hash = HASH(remote, &any); 11362306a36Sopenharmony_ci for_each_vti6_tunnel_rcu(ip6n->tnls_r_l[hash]) { 11462306a36Sopenharmony_ci if (ipv6_addr_equal(remote, &t->parms.raddr) && 11562306a36Sopenharmony_ci (t->dev->flags & IFF_UP)) 11662306a36Sopenharmony_ci return t; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci t = rcu_dereference(ip6n->tnls_wc[0]); 12062306a36Sopenharmony_ci if (t && (t->dev->flags & IFF_UP)) 12162306a36Sopenharmony_ci return t; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return NULL; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/** 12762306a36Sopenharmony_ci * vti6_tnl_bucket - get head of list matching given tunnel parameters 12862306a36Sopenharmony_ci * @ip6n: the private data for ip6_vti in the netns 12962306a36Sopenharmony_ci * @p: parameters containing tunnel end-points 13062306a36Sopenharmony_ci * 13162306a36Sopenharmony_ci * Description: 13262306a36Sopenharmony_ci * vti6_tnl_bucket() returns the head of the list matching the 13362306a36Sopenharmony_ci * &struct in6_addr entries laddr and raddr in @p. 13462306a36Sopenharmony_ci * 13562306a36Sopenharmony_ci * Return: head of IPv6 tunnel list 13662306a36Sopenharmony_ci **/ 13762306a36Sopenharmony_cistatic struct ip6_tnl __rcu ** 13862306a36Sopenharmony_civti6_tnl_bucket(struct vti6_net *ip6n, const struct __ip6_tnl_parm *p) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci const struct in6_addr *remote = &p->raddr; 14162306a36Sopenharmony_ci const struct in6_addr *local = &p->laddr; 14262306a36Sopenharmony_ci unsigned int h = 0; 14362306a36Sopenharmony_ci int prio = 0; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) { 14662306a36Sopenharmony_ci prio = 1; 14762306a36Sopenharmony_ci h = HASH(remote, local); 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci return &ip6n->tnls[prio][h]; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic void 15362306a36Sopenharmony_civti6_tnl_link(struct vti6_net *ip6n, struct ip6_tnl *t) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct ip6_tnl __rcu **tp = vti6_tnl_bucket(ip6n, &t->parms); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci rcu_assign_pointer(t->next, rtnl_dereference(*tp)); 15862306a36Sopenharmony_ci rcu_assign_pointer(*tp, t); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic void 16262306a36Sopenharmony_civti6_tnl_unlink(struct vti6_net *ip6n, struct ip6_tnl *t) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct ip6_tnl __rcu **tp; 16562306a36Sopenharmony_ci struct ip6_tnl *iter; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci for (tp = vti6_tnl_bucket(ip6n, &t->parms); 16862306a36Sopenharmony_ci (iter = rtnl_dereference(*tp)) != NULL; 16962306a36Sopenharmony_ci tp = &iter->next) { 17062306a36Sopenharmony_ci if (t == iter) { 17162306a36Sopenharmony_ci rcu_assign_pointer(*tp, t->next); 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void vti6_dev_free(struct net_device *dev) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci free_percpu(dev->tstats); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int vti6_tnl_create2(struct net_device *dev) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct ip6_tnl *t = netdev_priv(dev); 18562306a36Sopenharmony_ci struct net *net = dev_net(dev); 18662306a36Sopenharmony_ci struct vti6_net *ip6n = net_generic(net, vti6_net_id); 18762306a36Sopenharmony_ci int err; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci dev->rtnl_link_ops = &vti6_link_ops; 19062306a36Sopenharmony_ci err = register_netdevice(dev); 19162306a36Sopenharmony_ci if (err < 0) 19262306a36Sopenharmony_ci goto out; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci strcpy(t->parms.name, dev->name); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci vti6_tnl_link(ip6n, t); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return 0; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ciout: 20162306a36Sopenharmony_ci return err; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic struct ip6_tnl *vti6_tnl_create(struct net *net, struct __ip6_tnl_parm *p) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct net_device *dev; 20762306a36Sopenharmony_ci struct ip6_tnl *t; 20862306a36Sopenharmony_ci char name[IFNAMSIZ]; 20962306a36Sopenharmony_ci int err; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (p->name[0]) { 21262306a36Sopenharmony_ci if (!dev_valid_name(p->name)) 21362306a36Sopenharmony_ci goto failed; 21462306a36Sopenharmony_ci strscpy(name, p->name, IFNAMSIZ); 21562306a36Sopenharmony_ci } else { 21662306a36Sopenharmony_ci sprintf(name, "ip6_vti%%d"); 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci dev = alloc_netdev(sizeof(*t), name, NET_NAME_UNKNOWN, vti6_dev_setup); 22062306a36Sopenharmony_ci if (!dev) 22162306a36Sopenharmony_ci goto failed; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci dev_net_set(dev, net); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci t = netdev_priv(dev); 22662306a36Sopenharmony_ci t->parms = *p; 22762306a36Sopenharmony_ci t->net = dev_net(dev); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci err = vti6_tnl_create2(dev); 23062306a36Sopenharmony_ci if (err < 0) 23162306a36Sopenharmony_ci goto failed_free; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return t; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cifailed_free: 23662306a36Sopenharmony_ci free_netdev(dev); 23762306a36Sopenharmony_cifailed: 23862306a36Sopenharmony_ci return NULL; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/** 24262306a36Sopenharmony_ci * vti6_locate - find or create tunnel matching given parameters 24362306a36Sopenharmony_ci * @net: network namespace 24462306a36Sopenharmony_ci * @p: tunnel parameters 24562306a36Sopenharmony_ci * @create: != 0 if allowed to create new tunnel if no match found 24662306a36Sopenharmony_ci * 24762306a36Sopenharmony_ci * Description: 24862306a36Sopenharmony_ci * vti6_locate() first tries to locate an existing tunnel 24962306a36Sopenharmony_ci * based on @parms. If this is unsuccessful, but @create is set a new 25062306a36Sopenharmony_ci * tunnel device is created and registered for use. 25162306a36Sopenharmony_ci * 25262306a36Sopenharmony_ci * Return: 25362306a36Sopenharmony_ci * matching tunnel or NULL 25462306a36Sopenharmony_ci **/ 25562306a36Sopenharmony_cistatic struct ip6_tnl *vti6_locate(struct net *net, struct __ip6_tnl_parm *p, 25662306a36Sopenharmony_ci int create) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci const struct in6_addr *remote = &p->raddr; 25962306a36Sopenharmony_ci const struct in6_addr *local = &p->laddr; 26062306a36Sopenharmony_ci struct ip6_tnl __rcu **tp; 26162306a36Sopenharmony_ci struct ip6_tnl *t; 26262306a36Sopenharmony_ci struct vti6_net *ip6n = net_generic(net, vti6_net_id); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci for (tp = vti6_tnl_bucket(ip6n, p); 26562306a36Sopenharmony_ci (t = rtnl_dereference(*tp)) != NULL; 26662306a36Sopenharmony_ci tp = &t->next) { 26762306a36Sopenharmony_ci if (ipv6_addr_equal(local, &t->parms.laddr) && 26862306a36Sopenharmony_ci ipv6_addr_equal(remote, &t->parms.raddr)) { 26962306a36Sopenharmony_ci if (create) 27062306a36Sopenharmony_ci return NULL; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci return t; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci if (!create) 27662306a36Sopenharmony_ci return NULL; 27762306a36Sopenharmony_ci return vti6_tnl_create(net, p); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci/** 28162306a36Sopenharmony_ci * vti6_dev_uninit - tunnel device uninitializer 28262306a36Sopenharmony_ci * @dev: the device to be destroyed 28362306a36Sopenharmony_ci * 28462306a36Sopenharmony_ci * Description: 28562306a36Sopenharmony_ci * vti6_dev_uninit() removes tunnel from its list 28662306a36Sopenharmony_ci **/ 28762306a36Sopenharmony_cistatic void vti6_dev_uninit(struct net_device *dev) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct ip6_tnl *t = netdev_priv(dev); 29062306a36Sopenharmony_ci struct vti6_net *ip6n = net_generic(t->net, vti6_net_id); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (dev == ip6n->fb_tnl_dev) 29362306a36Sopenharmony_ci RCU_INIT_POINTER(ip6n->tnls_wc[0], NULL); 29462306a36Sopenharmony_ci else 29562306a36Sopenharmony_ci vti6_tnl_unlink(ip6n, t); 29662306a36Sopenharmony_ci netdev_put(dev, &t->dev_tracker); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int vti6_input_proto(struct sk_buff *skb, int nexthdr, __be32 spi, 30062306a36Sopenharmony_ci int encap_type) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct ip6_tnl *t; 30362306a36Sopenharmony_ci const struct ipv6hdr *ipv6h = ipv6_hdr(skb); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci rcu_read_lock(); 30662306a36Sopenharmony_ci t = vti6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr, &ipv6h->daddr); 30762306a36Sopenharmony_ci if (t) { 30862306a36Sopenharmony_ci if (t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) { 30962306a36Sopenharmony_ci rcu_read_unlock(); 31062306a36Sopenharmony_ci goto discard; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) { 31462306a36Sopenharmony_ci rcu_read_unlock(); 31562306a36Sopenharmony_ci goto discard; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci ipv6h = ipv6_hdr(skb); 31962306a36Sopenharmony_ci if (!ip6_tnl_rcv_ctl(t, &ipv6h->daddr, &ipv6h->saddr)) { 32062306a36Sopenharmony_ci DEV_STATS_INC(t->dev, rx_dropped); 32162306a36Sopenharmony_ci rcu_read_unlock(); 32262306a36Sopenharmony_ci goto discard; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci rcu_read_unlock(); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = t; 32862306a36Sopenharmony_ci XFRM_SPI_SKB_CB(skb)->family = AF_INET6; 32962306a36Sopenharmony_ci XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct ipv6hdr, daddr); 33062306a36Sopenharmony_ci return xfrm_input(skb, nexthdr, spi, encap_type); 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci rcu_read_unlock(); 33362306a36Sopenharmony_ci return -EINVAL; 33462306a36Sopenharmony_cidiscard: 33562306a36Sopenharmony_ci kfree_skb(skb); 33662306a36Sopenharmony_ci return 0; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic int vti6_rcv(struct sk_buff *skb) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci int nexthdr = skb_network_header(skb)[IP6CB(skb)->nhoff]; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return vti6_input_proto(skb, nexthdr, 0, 0); 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic int vti6_rcv_cb(struct sk_buff *skb, int err) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci unsigned short family; 34962306a36Sopenharmony_ci struct net_device *dev; 35062306a36Sopenharmony_ci struct xfrm_state *x; 35162306a36Sopenharmony_ci const struct xfrm_mode *inner_mode; 35262306a36Sopenharmony_ci struct ip6_tnl *t = XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6; 35362306a36Sopenharmony_ci u32 orig_mark = skb->mark; 35462306a36Sopenharmony_ci int ret; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (!t) 35762306a36Sopenharmony_ci return 1; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci dev = t->dev; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (err) { 36262306a36Sopenharmony_ci DEV_STATS_INC(dev, rx_errors); 36362306a36Sopenharmony_ci DEV_STATS_INC(dev, rx_dropped); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci x = xfrm_input_state(skb); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci inner_mode = &x->inner_mode; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (x->sel.family == AF_UNSPEC) { 37362306a36Sopenharmony_ci inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol); 37462306a36Sopenharmony_ci if (inner_mode == NULL) { 37562306a36Sopenharmony_ci XFRM_INC_STATS(dev_net(skb->dev), 37662306a36Sopenharmony_ci LINUX_MIB_XFRMINSTATEMODEERROR); 37762306a36Sopenharmony_ci return -EINVAL; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci family = inner_mode->family; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci skb->mark = be32_to_cpu(t->parms.i_key); 38462306a36Sopenharmony_ci ret = xfrm_policy_check(NULL, XFRM_POLICY_IN, skb, family); 38562306a36Sopenharmony_ci skb->mark = orig_mark; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (!ret) 38862306a36Sopenharmony_ci return -EPERM; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci skb_scrub_packet(skb, !net_eq(t->net, dev_net(skb->dev))); 39162306a36Sopenharmony_ci skb->dev = dev; 39262306a36Sopenharmony_ci dev_sw_netstats_rx_add(dev, skb->len); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci return 0; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci/** 39862306a36Sopenharmony_ci * vti6_addr_conflict - compare packet addresses to tunnel's own 39962306a36Sopenharmony_ci * @t: the outgoing tunnel device 40062306a36Sopenharmony_ci * @hdr: IPv6 header from the incoming packet 40162306a36Sopenharmony_ci * 40262306a36Sopenharmony_ci * Description: 40362306a36Sopenharmony_ci * Avoid trivial tunneling loop by checking that tunnel exit-point 40462306a36Sopenharmony_ci * doesn't match source of incoming packet. 40562306a36Sopenharmony_ci * 40662306a36Sopenharmony_ci * Return: 40762306a36Sopenharmony_ci * 1 if conflict, 40862306a36Sopenharmony_ci * 0 else 40962306a36Sopenharmony_ci **/ 41062306a36Sopenharmony_cistatic inline bool 41162306a36Sopenharmony_civti6_addr_conflict(const struct ip6_tnl *t, const struct ipv6hdr *hdr) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr); 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic bool vti6_state_check(const struct xfrm_state *x, 41762306a36Sopenharmony_ci const struct in6_addr *dst, 41862306a36Sopenharmony_ci const struct in6_addr *src) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci xfrm_address_t *daddr = (xfrm_address_t *)dst; 42162306a36Sopenharmony_ci xfrm_address_t *saddr = (xfrm_address_t *)src; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* if there is no transform then this tunnel is not functional. 42462306a36Sopenharmony_ci * Or if the xfrm is not mode tunnel. 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_ci if (!x || x->props.mode != XFRM_MODE_TUNNEL || 42762306a36Sopenharmony_ci x->props.family != AF_INET6) 42862306a36Sopenharmony_ci return false; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (ipv6_addr_any(dst)) 43162306a36Sopenharmony_ci return xfrm_addr_equal(saddr, &x->props.saddr, AF_INET6); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (!xfrm_state_addr_check(x, daddr, saddr, AF_INET6)) 43462306a36Sopenharmony_ci return false; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci return true; 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci/** 44062306a36Sopenharmony_ci * vti6_xmit - send a packet 44162306a36Sopenharmony_ci * @skb: the outgoing socket buffer 44262306a36Sopenharmony_ci * @dev: the outgoing tunnel device 44362306a36Sopenharmony_ci * @fl: the flow informations for the xfrm_lookup 44462306a36Sopenharmony_ci **/ 44562306a36Sopenharmony_cistatic int 44662306a36Sopenharmony_civti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci struct ip6_tnl *t = netdev_priv(dev); 44962306a36Sopenharmony_ci struct dst_entry *dst = skb_dst(skb); 45062306a36Sopenharmony_ci struct net_device *tdev; 45162306a36Sopenharmony_ci struct xfrm_state *x; 45262306a36Sopenharmony_ci int pkt_len = skb->len; 45362306a36Sopenharmony_ci int err = -1; 45462306a36Sopenharmony_ci int mtu; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (!dst) { 45762306a36Sopenharmony_ci switch (skb->protocol) { 45862306a36Sopenharmony_ci case htons(ETH_P_IP): { 45962306a36Sopenharmony_ci struct rtable *rt; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci fl->u.ip4.flowi4_oif = dev->ifindex; 46262306a36Sopenharmony_ci fl->u.ip4.flowi4_flags |= FLOWI_FLAG_ANYSRC; 46362306a36Sopenharmony_ci rt = __ip_route_output_key(dev_net(dev), &fl->u.ip4); 46462306a36Sopenharmony_ci if (IS_ERR(rt)) 46562306a36Sopenharmony_ci goto tx_err_link_failure; 46662306a36Sopenharmony_ci dst = &rt->dst; 46762306a36Sopenharmony_ci skb_dst_set(skb, dst); 46862306a36Sopenharmony_ci break; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci case htons(ETH_P_IPV6): 47162306a36Sopenharmony_ci fl->u.ip6.flowi6_oif = dev->ifindex; 47262306a36Sopenharmony_ci fl->u.ip6.flowi6_flags |= FLOWI_FLAG_ANYSRC; 47362306a36Sopenharmony_ci dst = ip6_route_output(dev_net(dev), NULL, &fl->u.ip6); 47462306a36Sopenharmony_ci if (dst->error) { 47562306a36Sopenharmony_ci dst_release(dst); 47662306a36Sopenharmony_ci dst = NULL; 47762306a36Sopenharmony_ci goto tx_err_link_failure; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci skb_dst_set(skb, dst); 48062306a36Sopenharmony_ci break; 48162306a36Sopenharmony_ci default: 48262306a36Sopenharmony_ci goto tx_err_link_failure; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci dst_hold(dst); 48762306a36Sopenharmony_ci dst = xfrm_lookup_route(t->net, dst, fl, NULL, 0); 48862306a36Sopenharmony_ci if (IS_ERR(dst)) { 48962306a36Sopenharmony_ci err = PTR_ERR(dst); 49062306a36Sopenharmony_ci dst = NULL; 49162306a36Sopenharmony_ci goto tx_err_link_failure; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (dst->flags & DST_XFRM_QUEUE) 49562306a36Sopenharmony_ci goto xmit; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci x = dst->xfrm; 49862306a36Sopenharmony_ci if (!vti6_state_check(x, &t->parms.raddr, &t->parms.laddr)) 49962306a36Sopenharmony_ci goto tx_err_link_failure; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (!ip6_tnl_xmit_ctl(t, (const struct in6_addr *)&x->props.saddr, 50262306a36Sopenharmony_ci (const struct in6_addr *)&x->id.daddr)) 50362306a36Sopenharmony_ci goto tx_err_link_failure; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci tdev = dst->dev; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (tdev == dev) { 50862306a36Sopenharmony_ci DEV_STATS_INC(dev, collisions); 50962306a36Sopenharmony_ci net_warn_ratelimited("%s: Local routing loop detected!\n", 51062306a36Sopenharmony_ci t->parms.name); 51162306a36Sopenharmony_ci goto tx_err_dst_release; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci mtu = dst_mtu(dst); 51562306a36Sopenharmony_ci if (skb->len > mtu) { 51662306a36Sopenharmony_ci skb_dst_update_pmtu_no_confirm(skb, mtu); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IPV6)) { 51962306a36Sopenharmony_ci if (mtu < IPV6_MIN_MTU) 52062306a36Sopenharmony_ci mtu = IPV6_MIN_MTU; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); 52362306a36Sopenharmony_ci } else { 52462306a36Sopenharmony_ci if (!(ip_hdr(skb)->frag_off & htons(IP_DF))) 52562306a36Sopenharmony_ci goto xmit; 52662306a36Sopenharmony_ci icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, 52762306a36Sopenharmony_ci htonl(mtu)); 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci err = -EMSGSIZE; 53162306a36Sopenharmony_ci goto tx_err_dst_release; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cixmit: 53562306a36Sopenharmony_ci skb_scrub_packet(skb, !net_eq(t->net, dev_net(dev))); 53662306a36Sopenharmony_ci skb_dst_set(skb, dst); 53762306a36Sopenharmony_ci skb->dev = skb_dst(skb)->dev; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci err = dst_output(t->net, skb->sk, skb); 54062306a36Sopenharmony_ci if (net_xmit_eval(err) == 0) 54162306a36Sopenharmony_ci err = pkt_len; 54262306a36Sopenharmony_ci iptunnel_xmit_stats(dev, err); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci return 0; 54562306a36Sopenharmony_citx_err_link_failure: 54662306a36Sopenharmony_ci DEV_STATS_INC(dev, tx_carrier_errors); 54762306a36Sopenharmony_ci dst_link_failure(skb); 54862306a36Sopenharmony_citx_err_dst_release: 54962306a36Sopenharmony_ci dst_release(dst); 55062306a36Sopenharmony_ci return err; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic netdev_tx_t 55462306a36Sopenharmony_civti6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci struct ip6_tnl *t = netdev_priv(dev); 55762306a36Sopenharmony_ci struct flowi fl; 55862306a36Sopenharmony_ci int ret; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (!pskb_inet_may_pull(skb)) 56162306a36Sopenharmony_ci goto tx_err; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci memset(&fl, 0, sizeof(fl)); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci switch (skb->protocol) { 56662306a36Sopenharmony_ci case htons(ETH_P_IPV6): 56762306a36Sopenharmony_ci if ((t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) || 56862306a36Sopenharmony_ci vti6_addr_conflict(t, ipv6_hdr(skb))) 56962306a36Sopenharmony_ci goto tx_err; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); 57262306a36Sopenharmony_ci xfrm_decode_session(skb, &fl, AF_INET6); 57362306a36Sopenharmony_ci break; 57462306a36Sopenharmony_ci case htons(ETH_P_IP): 57562306a36Sopenharmony_ci memset(IPCB(skb), 0, sizeof(*IPCB(skb))); 57662306a36Sopenharmony_ci xfrm_decode_session(skb, &fl, AF_INET); 57762306a36Sopenharmony_ci break; 57862306a36Sopenharmony_ci default: 57962306a36Sopenharmony_ci goto tx_err; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* override mark with tunnel output key */ 58362306a36Sopenharmony_ci fl.flowi_mark = be32_to_cpu(t->parms.o_key); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci ret = vti6_xmit(skb, dev, &fl); 58662306a36Sopenharmony_ci if (ret < 0) 58762306a36Sopenharmony_ci goto tx_err; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci return NETDEV_TX_OK; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_citx_err: 59262306a36Sopenharmony_ci DEV_STATS_INC(dev, tx_errors); 59362306a36Sopenharmony_ci DEV_STATS_INC(dev, tx_dropped); 59462306a36Sopenharmony_ci kfree_skb(skb); 59562306a36Sopenharmony_ci return NETDEV_TX_OK; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic int vti6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 59962306a36Sopenharmony_ci u8 type, u8 code, int offset, __be32 info) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci __be32 spi; 60262306a36Sopenharmony_ci __u32 mark; 60362306a36Sopenharmony_ci struct xfrm_state *x; 60462306a36Sopenharmony_ci struct ip6_tnl *t; 60562306a36Sopenharmony_ci struct ip_esp_hdr *esph; 60662306a36Sopenharmony_ci struct ip_auth_hdr *ah; 60762306a36Sopenharmony_ci struct ip_comp_hdr *ipch; 60862306a36Sopenharmony_ci struct net *net = dev_net(skb->dev); 60962306a36Sopenharmony_ci const struct ipv6hdr *iph = (const struct ipv6hdr *)skb->data; 61062306a36Sopenharmony_ci int protocol = iph->nexthdr; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci t = vti6_tnl_lookup(dev_net(skb->dev), &iph->daddr, &iph->saddr); 61362306a36Sopenharmony_ci if (!t) 61462306a36Sopenharmony_ci return -1; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci mark = be32_to_cpu(t->parms.o_key); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci switch (protocol) { 61962306a36Sopenharmony_ci case IPPROTO_ESP: 62062306a36Sopenharmony_ci esph = (struct ip_esp_hdr *)(skb->data + offset); 62162306a36Sopenharmony_ci spi = esph->spi; 62262306a36Sopenharmony_ci break; 62362306a36Sopenharmony_ci case IPPROTO_AH: 62462306a36Sopenharmony_ci ah = (struct ip_auth_hdr *)(skb->data + offset); 62562306a36Sopenharmony_ci spi = ah->spi; 62662306a36Sopenharmony_ci break; 62762306a36Sopenharmony_ci case IPPROTO_COMP: 62862306a36Sopenharmony_ci ipch = (struct ip_comp_hdr *)(skb->data + offset); 62962306a36Sopenharmony_ci spi = htonl(ntohs(ipch->cpi)); 63062306a36Sopenharmony_ci break; 63162306a36Sopenharmony_ci default: 63262306a36Sopenharmony_ci return 0; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (type != ICMPV6_PKT_TOOBIG && 63662306a36Sopenharmony_ci type != NDISC_REDIRECT) 63762306a36Sopenharmony_ci return 0; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci x = xfrm_state_lookup(net, mark, (const xfrm_address_t *)&iph->daddr, 64062306a36Sopenharmony_ci spi, protocol, AF_INET6); 64162306a36Sopenharmony_ci if (!x) 64262306a36Sopenharmony_ci return 0; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (type == NDISC_REDIRECT) 64562306a36Sopenharmony_ci ip6_redirect(skb, net, skb->dev->ifindex, 0, 64662306a36Sopenharmony_ci sock_net_uid(net, NULL)); 64762306a36Sopenharmony_ci else 64862306a36Sopenharmony_ci ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL)); 64962306a36Sopenharmony_ci xfrm_state_put(x); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci return 0; 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_cistatic void vti6_link_config(struct ip6_tnl *t, bool keep_mtu) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci struct net_device *dev = t->dev; 65762306a36Sopenharmony_ci struct __ip6_tnl_parm *p = &t->parms; 65862306a36Sopenharmony_ci struct net_device *tdev = NULL; 65962306a36Sopenharmony_ci int mtu; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci __dev_addr_set(dev, &p->laddr, sizeof(struct in6_addr)); 66262306a36Sopenharmony_ci memcpy(dev->broadcast, &p->raddr, sizeof(struct in6_addr)); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci p->flags &= ~(IP6_TNL_F_CAP_XMIT | IP6_TNL_F_CAP_RCV | 66562306a36Sopenharmony_ci IP6_TNL_F_CAP_PER_PACKET); 66662306a36Sopenharmony_ci p->flags |= ip6_tnl_get_cap(t, &p->laddr, &p->raddr); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci if (p->flags & IP6_TNL_F_CAP_XMIT && p->flags & IP6_TNL_F_CAP_RCV) 66962306a36Sopenharmony_ci dev->flags |= IFF_POINTOPOINT; 67062306a36Sopenharmony_ci else 67162306a36Sopenharmony_ci dev->flags &= ~IFF_POINTOPOINT; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci if (keep_mtu && dev->mtu) { 67462306a36Sopenharmony_ci dev->mtu = clamp(dev->mtu, dev->min_mtu, dev->max_mtu); 67562306a36Sopenharmony_ci return; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (p->flags & IP6_TNL_F_CAP_XMIT) { 67962306a36Sopenharmony_ci int strict = (ipv6_addr_type(&p->raddr) & 68062306a36Sopenharmony_ci (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)); 68162306a36Sopenharmony_ci struct rt6_info *rt = rt6_lookup(t->net, 68262306a36Sopenharmony_ci &p->raddr, &p->laddr, 68362306a36Sopenharmony_ci p->link, NULL, strict); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (rt) 68662306a36Sopenharmony_ci tdev = rt->dst.dev; 68762306a36Sopenharmony_ci ip6_rt_put(rt); 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci if (!tdev && p->link) 69162306a36Sopenharmony_ci tdev = __dev_get_by_index(t->net, p->link); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (tdev) 69462306a36Sopenharmony_ci mtu = tdev->mtu - sizeof(struct ipv6hdr); 69562306a36Sopenharmony_ci else 69662306a36Sopenharmony_ci mtu = ETH_DATA_LEN - LL_MAX_HEADER - sizeof(struct ipv6hdr); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci dev->mtu = max_t(int, mtu, IPV4_MIN_MTU); 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci/** 70262306a36Sopenharmony_ci * vti6_tnl_change - update the tunnel parameters 70362306a36Sopenharmony_ci * @t: tunnel to be changed 70462306a36Sopenharmony_ci * @p: tunnel configuration parameters 70562306a36Sopenharmony_ci * @keep_mtu: MTU was set from userspace, don't re-compute it 70662306a36Sopenharmony_ci * 70762306a36Sopenharmony_ci * Description: 70862306a36Sopenharmony_ci * vti6_tnl_change() updates the tunnel parameters 70962306a36Sopenharmony_ci **/ 71062306a36Sopenharmony_cistatic int 71162306a36Sopenharmony_civti6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p, 71262306a36Sopenharmony_ci bool keep_mtu) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci t->parms.laddr = p->laddr; 71562306a36Sopenharmony_ci t->parms.raddr = p->raddr; 71662306a36Sopenharmony_ci t->parms.link = p->link; 71762306a36Sopenharmony_ci t->parms.i_key = p->i_key; 71862306a36Sopenharmony_ci t->parms.o_key = p->o_key; 71962306a36Sopenharmony_ci t->parms.proto = p->proto; 72062306a36Sopenharmony_ci t->parms.fwmark = p->fwmark; 72162306a36Sopenharmony_ci dst_cache_reset(&t->dst_cache); 72262306a36Sopenharmony_ci vti6_link_config(t, keep_mtu); 72362306a36Sopenharmony_ci return 0; 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic int vti6_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p, 72762306a36Sopenharmony_ci bool keep_mtu) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci struct net *net = dev_net(t->dev); 73062306a36Sopenharmony_ci struct vti6_net *ip6n = net_generic(net, vti6_net_id); 73162306a36Sopenharmony_ci int err; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci vti6_tnl_unlink(ip6n, t); 73462306a36Sopenharmony_ci synchronize_net(); 73562306a36Sopenharmony_ci err = vti6_tnl_change(t, p, keep_mtu); 73662306a36Sopenharmony_ci vti6_tnl_link(ip6n, t); 73762306a36Sopenharmony_ci netdev_state_change(t->dev); 73862306a36Sopenharmony_ci return err; 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_cistatic void 74262306a36Sopenharmony_civti6_parm_from_user(struct __ip6_tnl_parm *p, const struct ip6_tnl_parm2 *u) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci p->laddr = u->laddr; 74562306a36Sopenharmony_ci p->raddr = u->raddr; 74662306a36Sopenharmony_ci p->link = u->link; 74762306a36Sopenharmony_ci p->i_key = u->i_key; 74862306a36Sopenharmony_ci p->o_key = u->o_key; 74962306a36Sopenharmony_ci p->proto = u->proto; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci memcpy(p->name, u->name, sizeof(u->name)); 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic void 75562306a36Sopenharmony_civti6_parm_to_user(struct ip6_tnl_parm2 *u, const struct __ip6_tnl_parm *p) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci u->laddr = p->laddr; 75862306a36Sopenharmony_ci u->raddr = p->raddr; 75962306a36Sopenharmony_ci u->link = p->link; 76062306a36Sopenharmony_ci u->i_key = p->i_key; 76162306a36Sopenharmony_ci u->o_key = p->o_key; 76262306a36Sopenharmony_ci if (u->i_key) 76362306a36Sopenharmony_ci u->i_flags |= GRE_KEY; 76462306a36Sopenharmony_ci if (u->o_key) 76562306a36Sopenharmony_ci u->o_flags |= GRE_KEY; 76662306a36Sopenharmony_ci u->proto = p->proto; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci memcpy(u->name, p->name, sizeof(u->name)); 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci/** 77262306a36Sopenharmony_ci * vti6_siocdevprivate - configure vti6 tunnels from userspace 77362306a36Sopenharmony_ci * @dev: virtual device associated with tunnel 77462306a36Sopenharmony_ci * @ifr: unused 77562306a36Sopenharmony_ci * @data: parameters passed from userspace 77662306a36Sopenharmony_ci * @cmd: command to be performed 77762306a36Sopenharmony_ci * 77862306a36Sopenharmony_ci * Description: 77962306a36Sopenharmony_ci * vti6_siocdevprivate() is used for managing vti6 tunnels 78062306a36Sopenharmony_ci * from userspace. 78162306a36Sopenharmony_ci * 78262306a36Sopenharmony_ci * The possible commands are the following: 78362306a36Sopenharmony_ci * %SIOCGETTUNNEL: get tunnel parameters for device 78462306a36Sopenharmony_ci * %SIOCADDTUNNEL: add tunnel matching given tunnel parameters 78562306a36Sopenharmony_ci * %SIOCCHGTUNNEL: change tunnel parameters to those given 78662306a36Sopenharmony_ci * %SIOCDELTUNNEL: delete tunnel 78762306a36Sopenharmony_ci * 78862306a36Sopenharmony_ci * The fallback device "ip6_vti0", created during module 78962306a36Sopenharmony_ci * initialization, can be used for creating other tunnel devices. 79062306a36Sopenharmony_ci * 79162306a36Sopenharmony_ci * Return: 79262306a36Sopenharmony_ci * 0 on success, 79362306a36Sopenharmony_ci * %-EFAULT if unable to copy data to or from userspace, 79462306a36Sopenharmony_ci * %-EPERM if current process hasn't %CAP_NET_ADMIN set 79562306a36Sopenharmony_ci * %-EINVAL if passed tunnel parameters are invalid, 79662306a36Sopenharmony_ci * %-EEXIST if changing a tunnel's parameters would cause a conflict 79762306a36Sopenharmony_ci * %-ENODEV if attempting to change or delete a nonexisting device 79862306a36Sopenharmony_ci **/ 79962306a36Sopenharmony_cistatic int 80062306a36Sopenharmony_civti6_siocdevprivate(struct net_device *dev, struct ifreq *ifr, void __user *data, int cmd) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci int err = 0; 80362306a36Sopenharmony_ci struct ip6_tnl_parm2 p; 80462306a36Sopenharmony_ci struct __ip6_tnl_parm p1; 80562306a36Sopenharmony_ci struct ip6_tnl *t = NULL; 80662306a36Sopenharmony_ci struct net *net = dev_net(dev); 80762306a36Sopenharmony_ci struct vti6_net *ip6n = net_generic(net, vti6_net_id); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci memset(&p1, 0, sizeof(p1)); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci switch (cmd) { 81262306a36Sopenharmony_ci case SIOCGETTUNNEL: 81362306a36Sopenharmony_ci if (dev == ip6n->fb_tnl_dev) { 81462306a36Sopenharmony_ci if (copy_from_user(&p, data, sizeof(p))) { 81562306a36Sopenharmony_ci err = -EFAULT; 81662306a36Sopenharmony_ci break; 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci vti6_parm_from_user(&p1, &p); 81962306a36Sopenharmony_ci t = vti6_locate(net, &p1, 0); 82062306a36Sopenharmony_ci } else { 82162306a36Sopenharmony_ci memset(&p, 0, sizeof(p)); 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci if (!t) 82462306a36Sopenharmony_ci t = netdev_priv(dev); 82562306a36Sopenharmony_ci vti6_parm_to_user(&p, &t->parms); 82662306a36Sopenharmony_ci if (copy_to_user(data, &p, sizeof(p))) 82762306a36Sopenharmony_ci err = -EFAULT; 82862306a36Sopenharmony_ci break; 82962306a36Sopenharmony_ci case SIOCADDTUNNEL: 83062306a36Sopenharmony_ci case SIOCCHGTUNNEL: 83162306a36Sopenharmony_ci err = -EPERM; 83262306a36Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 83362306a36Sopenharmony_ci break; 83462306a36Sopenharmony_ci err = -EFAULT; 83562306a36Sopenharmony_ci if (copy_from_user(&p, data, sizeof(p))) 83662306a36Sopenharmony_ci break; 83762306a36Sopenharmony_ci err = -EINVAL; 83862306a36Sopenharmony_ci if (p.proto != IPPROTO_IPV6 && p.proto != 0) 83962306a36Sopenharmony_ci break; 84062306a36Sopenharmony_ci vti6_parm_from_user(&p1, &p); 84162306a36Sopenharmony_ci t = vti6_locate(net, &p1, cmd == SIOCADDTUNNEL); 84262306a36Sopenharmony_ci if (dev != ip6n->fb_tnl_dev && cmd == SIOCCHGTUNNEL) { 84362306a36Sopenharmony_ci if (t) { 84462306a36Sopenharmony_ci if (t->dev != dev) { 84562306a36Sopenharmony_ci err = -EEXIST; 84662306a36Sopenharmony_ci break; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci } else 84962306a36Sopenharmony_ci t = netdev_priv(dev); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci err = vti6_update(t, &p1, false); 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci if (t) { 85462306a36Sopenharmony_ci err = 0; 85562306a36Sopenharmony_ci vti6_parm_to_user(&p, &t->parms); 85662306a36Sopenharmony_ci if (copy_to_user(data, &p, sizeof(p))) 85762306a36Sopenharmony_ci err = -EFAULT; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci } else 86062306a36Sopenharmony_ci err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT); 86162306a36Sopenharmony_ci break; 86262306a36Sopenharmony_ci case SIOCDELTUNNEL: 86362306a36Sopenharmony_ci err = -EPERM; 86462306a36Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 86562306a36Sopenharmony_ci break; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci if (dev == ip6n->fb_tnl_dev) { 86862306a36Sopenharmony_ci err = -EFAULT; 86962306a36Sopenharmony_ci if (copy_from_user(&p, data, sizeof(p))) 87062306a36Sopenharmony_ci break; 87162306a36Sopenharmony_ci err = -ENOENT; 87262306a36Sopenharmony_ci vti6_parm_from_user(&p1, &p); 87362306a36Sopenharmony_ci t = vti6_locate(net, &p1, 0); 87462306a36Sopenharmony_ci if (!t) 87562306a36Sopenharmony_ci break; 87662306a36Sopenharmony_ci err = -EPERM; 87762306a36Sopenharmony_ci if (t->dev == ip6n->fb_tnl_dev) 87862306a36Sopenharmony_ci break; 87962306a36Sopenharmony_ci dev = t->dev; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci err = 0; 88262306a36Sopenharmony_ci unregister_netdevice(dev); 88362306a36Sopenharmony_ci break; 88462306a36Sopenharmony_ci default: 88562306a36Sopenharmony_ci err = -EINVAL; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci return err; 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_cistatic const struct net_device_ops vti6_netdev_ops = { 89162306a36Sopenharmony_ci .ndo_init = vti6_dev_init, 89262306a36Sopenharmony_ci .ndo_uninit = vti6_dev_uninit, 89362306a36Sopenharmony_ci .ndo_start_xmit = vti6_tnl_xmit, 89462306a36Sopenharmony_ci .ndo_siocdevprivate = vti6_siocdevprivate, 89562306a36Sopenharmony_ci .ndo_get_stats64 = dev_get_tstats64, 89662306a36Sopenharmony_ci .ndo_get_iflink = ip6_tnl_get_iflink, 89762306a36Sopenharmony_ci}; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci/** 90062306a36Sopenharmony_ci * vti6_dev_setup - setup virtual tunnel device 90162306a36Sopenharmony_ci * @dev: virtual device associated with tunnel 90262306a36Sopenharmony_ci * 90362306a36Sopenharmony_ci * Description: 90462306a36Sopenharmony_ci * Initialize function pointers and device parameters 90562306a36Sopenharmony_ci **/ 90662306a36Sopenharmony_cistatic void vti6_dev_setup(struct net_device *dev) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci dev->netdev_ops = &vti6_netdev_ops; 90962306a36Sopenharmony_ci dev->header_ops = &ip_tunnel_header_ops; 91062306a36Sopenharmony_ci dev->needs_free_netdev = true; 91162306a36Sopenharmony_ci dev->priv_destructor = vti6_dev_free; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci dev->type = ARPHRD_TUNNEL6; 91462306a36Sopenharmony_ci dev->min_mtu = IPV4_MIN_MTU; 91562306a36Sopenharmony_ci dev->max_mtu = IP_MAX_MTU - sizeof(struct ipv6hdr); 91662306a36Sopenharmony_ci dev->flags |= IFF_NOARP; 91762306a36Sopenharmony_ci dev->addr_len = sizeof(struct in6_addr); 91862306a36Sopenharmony_ci netif_keep_dst(dev); 91962306a36Sopenharmony_ci /* This perm addr will be used as interface identifier by IPv6 */ 92062306a36Sopenharmony_ci dev->addr_assign_type = NET_ADDR_RANDOM; 92162306a36Sopenharmony_ci eth_random_addr(dev->perm_addr); 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci/** 92562306a36Sopenharmony_ci * vti6_dev_init_gen - general initializer for all tunnel devices 92662306a36Sopenharmony_ci * @dev: virtual device associated with tunnel 92762306a36Sopenharmony_ci **/ 92862306a36Sopenharmony_cistatic inline int vti6_dev_init_gen(struct net_device *dev) 92962306a36Sopenharmony_ci{ 93062306a36Sopenharmony_ci struct ip6_tnl *t = netdev_priv(dev); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci t->dev = dev; 93362306a36Sopenharmony_ci t->net = dev_net(dev); 93462306a36Sopenharmony_ci dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); 93562306a36Sopenharmony_ci if (!dev->tstats) 93662306a36Sopenharmony_ci return -ENOMEM; 93762306a36Sopenharmony_ci netdev_hold(dev, &t->dev_tracker, GFP_KERNEL); 93862306a36Sopenharmony_ci return 0; 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci/** 94262306a36Sopenharmony_ci * vti6_dev_init - initializer for all non fallback tunnel devices 94362306a36Sopenharmony_ci * @dev: virtual device associated with tunnel 94462306a36Sopenharmony_ci **/ 94562306a36Sopenharmony_cistatic int vti6_dev_init(struct net_device *dev) 94662306a36Sopenharmony_ci{ 94762306a36Sopenharmony_ci struct ip6_tnl *t = netdev_priv(dev); 94862306a36Sopenharmony_ci int err = vti6_dev_init_gen(dev); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci if (err) 95162306a36Sopenharmony_ci return err; 95262306a36Sopenharmony_ci vti6_link_config(t, true); 95362306a36Sopenharmony_ci return 0; 95462306a36Sopenharmony_ci} 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci/** 95762306a36Sopenharmony_ci * vti6_fb_tnl_dev_init - initializer for fallback tunnel device 95862306a36Sopenharmony_ci * @dev: fallback device 95962306a36Sopenharmony_ci * 96062306a36Sopenharmony_ci * Return: 0 96162306a36Sopenharmony_ci **/ 96262306a36Sopenharmony_cistatic int __net_init vti6_fb_tnl_dev_init(struct net_device *dev) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci struct ip6_tnl *t = netdev_priv(dev); 96562306a36Sopenharmony_ci struct net *net = dev_net(dev); 96662306a36Sopenharmony_ci struct vti6_net *ip6n = net_generic(net, vti6_net_id); 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci t->parms.proto = IPPROTO_IPV6; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci rcu_assign_pointer(ip6n->tnls_wc[0], t); 97162306a36Sopenharmony_ci return 0; 97262306a36Sopenharmony_ci} 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_cistatic int vti6_validate(struct nlattr *tb[], struct nlattr *data[], 97562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci return 0; 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistatic void vti6_netlink_parms(struct nlattr *data[], 98162306a36Sopenharmony_ci struct __ip6_tnl_parm *parms) 98262306a36Sopenharmony_ci{ 98362306a36Sopenharmony_ci memset(parms, 0, sizeof(*parms)); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci if (!data) 98662306a36Sopenharmony_ci return; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci if (data[IFLA_VTI_LINK]) 98962306a36Sopenharmony_ci parms->link = nla_get_u32(data[IFLA_VTI_LINK]); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci if (data[IFLA_VTI_LOCAL]) 99262306a36Sopenharmony_ci parms->laddr = nla_get_in6_addr(data[IFLA_VTI_LOCAL]); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci if (data[IFLA_VTI_REMOTE]) 99562306a36Sopenharmony_ci parms->raddr = nla_get_in6_addr(data[IFLA_VTI_REMOTE]); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci if (data[IFLA_VTI_IKEY]) 99862306a36Sopenharmony_ci parms->i_key = nla_get_be32(data[IFLA_VTI_IKEY]); 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci if (data[IFLA_VTI_OKEY]) 100162306a36Sopenharmony_ci parms->o_key = nla_get_be32(data[IFLA_VTI_OKEY]); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci if (data[IFLA_VTI_FWMARK]) 100462306a36Sopenharmony_ci parms->fwmark = nla_get_u32(data[IFLA_VTI_FWMARK]); 100562306a36Sopenharmony_ci} 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_cistatic int vti6_newlink(struct net *src_net, struct net_device *dev, 100862306a36Sopenharmony_ci struct nlattr *tb[], struct nlattr *data[], 100962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 101062306a36Sopenharmony_ci{ 101162306a36Sopenharmony_ci struct net *net = dev_net(dev); 101262306a36Sopenharmony_ci struct ip6_tnl *nt; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci nt = netdev_priv(dev); 101562306a36Sopenharmony_ci vti6_netlink_parms(data, &nt->parms); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci nt->parms.proto = IPPROTO_IPV6; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci if (vti6_locate(net, &nt->parms, 0)) 102062306a36Sopenharmony_ci return -EEXIST; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci return vti6_tnl_create2(dev); 102362306a36Sopenharmony_ci} 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_cistatic void vti6_dellink(struct net_device *dev, struct list_head *head) 102662306a36Sopenharmony_ci{ 102762306a36Sopenharmony_ci struct net *net = dev_net(dev); 102862306a36Sopenharmony_ci struct vti6_net *ip6n = net_generic(net, vti6_net_id); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci if (dev != ip6n->fb_tnl_dev) 103162306a36Sopenharmony_ci unregister_netdevice_queue(dev, head); 103262306a36Sopenharmony_ci} 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_cistatic int vti6_changelink(struct net_device *dev, struct nlattr *tb[], 103562306a36Sopenharmony_ci struct nlattr *data[], 103662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 103762306a36Sopenharmony_ci{ 103862306a36Sopenharmony_ci struct ip6_tnl *t; 103962306a36Sopenharmony_ci struct __ip6_tnl_parm p; 104062306a36Sopenharmony_ci struct net *net = dev_net(dev); 104162306a36Sopenharmony_ci struct vti6_net *ip6n = net_generic(net, vti6_net_id); 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci if (dev == ip6n->fb_tnl_dev) 104462306a36Sopenharmony_ci return -EINVAL; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci vti6_netlink_parms(data, &p); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci t = vti6_locate(net, &p, 0); 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci if (t) { 105162306a36Sopenharmony_ci if (t->dev != dev) 105262306a36Sopenharmony_ci return -EEXIST; 105362306a36Sopenharmony_ci } else 105462306a36Sopenharmony_ci t = netdev_priv(dev); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci return vti6_update(t, &p, tb && tb[IFLA_MTU]); 105762306a36Sopenharmony_ci} 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cistatic size_t vti6_get_size(const struct net_device *dev) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci return 106262306a36Sopenharmony_ci /* IFLA_VTI_LINK */ 106362306a36Sopenharmony_ci nla_total_size(4) + 106462306a36Sopenharmony_ci /* IFLA_VTI_LOCAL */ 106562306a36Sopenharmony_ci nla_total_size(sizeof(struct in6_addr)) + 106662306a36Sopenharmony_ci /* IFLA_VTI_REMOTE */ 106762306a36Sopenharmony_ci nla_total_size(sizeof(struct in6_addr)) + 106862306a36Sopenharmony_ci /* IFLA_VTI_IKEY */ 106962306a36Sopenharmony_ci nla_total_size(4) + 107062306a36Sopenharmony_ci /* IFLA_VTI_OKEY */ 107162306a36Sopenharmony_ci nla_total_size(4) + 107262306a36Sopenharmony_ci /* IFLA_VTI_FWMARK */ 107362306a36Sopenharmony_ci nla_total_size(4) + 107462306a36Sopenharmony_ci 0; 107562306a36Sopenharmony_ci} 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_cistatic int vti6_fill_info(struct sk_buff *skb, const struct net_device *dev) 107862306a36Sopenharmony_ci{ 107962306a36Sopenharmony_ci struct ip6_tnl *tunnel = netdev_priv(dev); 108062306a36Sopenharmony_ci struct __ip6_tnl_parm *parm = &tunnel->parms; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci if (nla_put_u32(skb, IFLA_VTI_LINK, parm->link) || 108362306a36Sopenharmony_ci nla_put_in6_addr(skb, IFLA_VTI_LOCAL, &parm->laddr) || 108462306a36Sopenharmony_ci nla_put_in6_addr(skb, IFLA_VTI_REMOTE, &parm->raddr) || 108562306a36Sopenharmony_ci nla_put_be32(skb, IFLA_VTI_IKEY, parm->i_key) || 108662306a36Sopenharmony_ci nla_put_be32(skb, IFLA_VTI_OKEY, parm->o_key) || 108762306a36Sopenharmony_ci nla_put_u32(skb, IFLA_VTI_FWMARK, parm->fwmark)) 108862306a36Sopenharmony_ci goto nla_put_failure; 108962306a36Sopenharmony_ci return 0; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_cinla_put_failure: 109262306a36Sopenharmony_ci return -EMSGSIZE; 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_cistatic const struct nla_policy vti6_policy[IFLA_VTI_MAX + 1] = { 109662306a36Sopenharmony_ci [IFLA_VTI_LINK] = { .type = NLA_U32 }, 109762306a36Sopenharmony_ci [IFLA_VTI_LOCAL] = { .len = sizeof(struct in6_addr) }, 109862306a36Sopenharmony_ci [IFLA_VTI_REMOTE] = { .len = sizeof(struct in6_addr) }, 109962306a36Sopenharmony_ci [IFLA_VTI_IKEY] = { .type = NLA_U32 }, 110062306a36Sopenharmony_ci [IFLA_VTI_OKEY] = { .type = NLA_U32 }, 110162306a36Sopenharmony_ci [IFLA_VTI_FWMARK] = { .type = NLA_U32 }, 110262306a36Sopenharmony_ci}; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_cistatic struct rtnl_link_ops vti6_link_ops __read_mostly = { 110562306a36Sopenharmony_ci .kind = "vti6", 110662306a36Sopenharmony_ci .maxtype = IFLA_VTI_MAX, 110762306a36Sopenharmony_ci .policy = vti6_policy, 110862306a36Sopenharmony_ci .priv_size = sizeof(struct ip6_tnl), 110962306a36Sopenharmony_ci .setup = vti6_dev_setup, 111062306a36Sopenharmony_ci .validate = vti6_validate, 111162306a36Sopenharmony_ci .newlink = vti6_newlink, 111262306a36Sopenharmony_ci .dellink = vti6_dellink, 111362306a36Sopenharmony_ci .changelink = vti6_changelink, 111462306a36Sopenharmony_ci .get_size = vti6_get_size, 111562306a36Sopenharmony_ci .fill_info = vti6_fill_info, 111662306a36Sopenharmony_ci .get_link_net = ip6_tnl_get_link_net, 111762306a36Sopenharmony_ci}; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_cistatic void __net_exit vti6_destroy_tunnels(struct vti6_net *ip6n, 112062306a36Sopenharmony_ci struct list_head *list) 112162306a36Sopenharmony_ci{ 112262306a36Sopenharmony_ci int h; 112362306a36Sopenharmony_ci struct ip6_tnl *t; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci for (h = 0; h < IP6_VTI_HASH_SIZE; h++) { 112662306a36Sopenharmony_ci t = rtnl_dereference(ip6n->tnls_r_l[h]); 112762306a36Sopenharmony_ci while (t) { 112862306a36Sopenharmony_ci unregister_netdevice_queue(t->dev, list); 112962306a36Sopenharmony_ci t = rtnl_dereference(t->next); 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci t = rtnl_dereference(ip6n->tnls_wc[0]); 113462306a36Sopenharmony_ci if (t) 113562306a36Sopenharmony_ci unregister_netdevice_queue(t->dev, list); 113662306a36Sopenharmony_ci} 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_cistatic int __net_init vti6_init_net(struct net *net) 113962306a36Sopenharmony_ci{ 114062306a36Sopenharmony_ci struct vti6_net *ip6n = net_generic(net, vti6_net_id); 114162306a36Sopenharmony_ci struct ip6_tnl *t = NULL; 114262306a36Sopenharmony_ci int err; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci ip6n->tnls[0] = ip6n->tnls_wc; 114562306a36Sopenharmony_ci ip6n->tnls[1] = ip6n->tnls_r_l; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci if (!net_has_fallback_tunnels(net)) 114862306a36Sopenharmony_ci return 0; 114962306a36Sopenharmony_ci err = -ENOMEM; 115062306a36Sopenharmony_ci ip6n->fb_tnl_dev = alloc_netdev(sizeof(struct ip6_tnl), "ip6_vti0", 115162306a36Sopenharmony_ci NET_NAME_UNKNOWN, vti6_dev_setup); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci if (!ip6n->fb_tnl_dev) 115462306a36Sopenharmony_ci goto err_alloc_dev; 115562306a36Sopenharmony_ci dev_net_set(ip6n->fb_tnl_dev, net); 115662306a36Sopenharmony_ci ip6n->fb_tnl_dev->rtnl_link_ops = &vti6_link_ops; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci err = vti6_fb_tnl_dev_init(ip6n->fb_tnl_dev); 115962306a36Sopenharmony_ci if (err < 0) 116062306a36Sopenharmony_ci goto err_register; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci err = register_netdev(ip6n->fb_tnl_dev); 116362306a36Sopenharmony_ci if (err < 0) 116462306a36Sopenharmony_ci goto err_register; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci t = netdev_priv(ip6n->fb_tnl_dev); 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci strcpy(t->parms.name, ip6n->fb_tnl_dev->name); 116962306a36Sopenharmony_ci return 0; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_cierr_register: 117262306a36Sopenharmony_ci free_netdev(ip6n->fb_tnl_dev); 117362306a36Sopenharmony_cierr_alloc_dev: 117462306a36Sopenharmony_ci return err; 117562306a36Sopenharmony_ci} 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_cistatic void __net_exit vti6_exit_batch_net(struct list_head *net_list) 117862306a36Sopenharmony_ci{ 117962306a36Sopenharmony_ci struct vti6_net *ip6n; 118062306a36Sopenharmony_ci struct net *net; 118162306a36Sopenharmony_ci LIST_HEAD(list); 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci rtnl_lock(); 118462306a36Sopenharmony_ci list_for_each_entry(net, net_list, exit_list) { 118562306a36Sopenharmony_ci ip6n = net_generic(net, vti6_net_id); 118662306a36Sopenharmony_ci vti6_destroy_tunnels(ip6n, &list); 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci unregister_netdevice_many(&list); 118962306a36Sopenharmony_ci rtnl_unlock(); 119062306a36Sopenharmony_ci} 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_cistatic struct pernet_operations vti6_net_ops = { 119362306a36Sopenharmony_ci .init = vti6_init_net, 119462306a36Sopenharmony_ci .exit_batch = vti6_exit_batch_net, 119562306a36Sopenharmony_ci .id = &vti6_net_id, 119662306a36Sopenharmony_ci .size = sizeof(struct vti6_net), 119762306a36Sopenharmony_ci}; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_cistatic struct xfrm6_protocol vti_esp6_protocol __read_mostly = { 120062306a36Sopenharmony_ci .handler = vti6_rcv, 120162306a36Sopenharmony_ci .input_handler = vti6_input_proto, 120262306a36Sopenharmony_ci .cb_handler = vti6_rcv_cb, 120362306a36Sopenharmony_ci .err_handler = vti6_err, 120462306a36Sopenharmony_ci .priority = 100, 120562306a36Sopenharmony_ci}; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_cistatic struct xfrm6_protocol vti_ah6_protocol __read_mostly = { 120862306a36Sopenharmony_ci .handler = vti6_rcv, 120962306a36Sopenharmony_ci .input_handler = vti6_input_proto, 121062306a36Sopenharmony_ci .cb_handler = vti6_rcv_cb, 121162306a36Sopenharmony_ci .err_handler = vti6_err, 121262306a36Sopenharmony_ci .priority = 100, 121362306a36Sopenharmony_ci}; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_cistatic struct xfrm6_protocol vti_ipcomp6_protocol __read_mostly = { 121662306a36Sopenharmony_ci .handler = vti6_rcv, 121762306a36Sopenharmony_ci .input_handler = vti6_input_proto, 121862306a36Sopenharmony_ci .cb_handler = vti6_rcv_cb, 121962306a36Sopenharmony_ci .err_handler = vti6_err, 122062306a36Sopenharmony_ci .priority = 100, 122162306a36Sopenharmony_ci}; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL) 122462306a36Sopenharmony_cistatic int vti6_rcv_tunnel(struct sk_buff *skb) 122562306a36Sopenharmony_ci{ 122662306a36Sopenharmony_ci const xfrm_address_t *saddr; 122762306a36Sopenharmony_ci __be32 spi; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci saddr = (const xfrm_address_t *)&ipv6_hdr(skb)->saddr; 123062306a36Sopenharmony_ci spi = xfrm6_tunnel_spi_lookup(dev_net(skb->dev), saddr); 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci return vti6_input_proto(skb, IPPROTO_IPV6, spi, 0); 123362306a36Sopenharmony_ci} 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_cistatic struct xfrm6_tunnel vti_ipv6_handler __read_mostly = { 123662306a36Sopenharmony_ci .handler = vti6_rcv_tunnel, 123762306a36Sopenharmony_ci .cb_handler = vti6_rcv_cb, 123862306a36Sopenharmony_ci .err_handler = vti6_err, 123962306a36Sopenharmony_ci .priority = 0, 124062306a36Sopenharmony_ci}; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_cistatic struct xfrm6_tunnel vti_ip6ip_handler __read_mostly = { 124362306a36Sopenharmony_ci .handler = vti6_rcv_tunnel, 124462306a36Sopenharmony_ci .cb_handler = vti6_rcv_cb, 124562306a36Sopenharmony_ci .err_handler = vti6_err, 124662306a36Sopenharmony_ci .priority = 0, 124762306a36Sopenharmony_ci}; 124862306a36Sopenharmony_ci#endif 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci/** 125162306a36Sopenharmony_ci * vti6_tunnel_init - register protocol and reserve needed resources 125262306a36Sopenharmony_ci * 125362306a36Sopenharmony_ci * Return: 0 on success 125462306a36Sopenharmony_ci **/ 125562306a36Sopenharmony_cistatic int __init vti6_tunnel_init(void) 125662306a36Sopenharmony_ci{ 125762306a36Sopenharmony_ci const char *msg; 125862306a36Sopenharmony_ci int err; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci msg = "tunnel device"; 126162306a36Sopenharmony_ci err = register_pernet_device(&vti6_net_ops); 126262306a36Sopenharmony_ci if (err < 0) 126362306a36Sopenharmony_ci goto pernet_dev_failed; 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci msg = "tunnel protocols"; 126662306a36Sopenharmony_ci err = xfrm6_protocol_register(&vti_esp6_protocol, IPPROTO_ESP); 126762306a36Sopenharmony_ci if (err < 0) 126862306a36Sopenharmony_ci goto xfrm_proto_esp_failed; 126962306a36Sopenharmony_ci err = xfrm6_protocol_register(&vti_ah6_protocol, IPPROTO_AH); 127062306a36Sopenharmony_ci if (err < 0) 127162306a36Sopenharmony_ci goto xfrm_proto_ah_failed; 127262306a36Sopenharmony_ci err = xfrm6_protocol_register(&vti_ipcomp6_protocol, IPPROTO_COMP); 127362306a36Sopenharmony_ci if (err < 0) 127462306a36Sopenharmony_ci goto xfrm_proto_comp_failed; 127562306a36Sopenharmony_ci#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL) 127662306a36Sopenharmony_ci msg = "ipv6 tunnel"; 127762306a36Sopenharmony_ci err = xfrm6_tunnel_register(&vti_ipv6_handler, AF_INET6); 127862306a36Sopenharmony_ci if (err < 0) 127962306a36Sopenharmony_ci goto vti_tunnel_ipv6_failed; 128062306a36Sopenharmony_ci err = xfrm6_tunnel_register(&vti_ip6ip_handler, AF_INET); 128162306a36Sopenharmony_ci if (err < 0) 128262306a36Sopenharmony_ci goto vti_tunnel_ip6ip_failed; 128362306a36Sopenharmony_ci#endif 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci msg = "netlink interface"; 128662306a36Sopenharmony_ci err = rtnl_link_register(&vti6_link_ops); 128762306a36Sopenharmony_ci if (err < 0) 128862306a36Sopenharmony_ci goto rtnl_link_failed; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci return 0; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_cirtnl_link_failed: 129362306a36Sopenharmony_ci#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL) 129462306a36Sopenharmony_ci err = xfrm6_tunnel_deregister(&vti_ip6ip_handler, AF_INET); 129562306a36Sopenharmony_civti_tunnel_ip6ip_failed: 129662306a36Sopenharmony_ci err = xfrm6_tunnel_deregister(&vti_ipv6_handler, AF_INET6); 129762306a36Sopenharmony_civti_tunnel_ipv6_failed: 129862306a36Sopenharmony_ci#endif 129962306a36Sopenharmony_ci xfrm6_protocol_deregister(&vti_ipcomp6_protocol, IPPROTO_COMP); 130062306a36Sopenharmony_cixfrm_proto_comp_failed: 130162306a36Sopenharmony_ci xfrm6_protocol_deregister(&vti_ah6_protocol, IPPROTO_AH); 130262306a36Sopenharmony_cixfrm_proto_ah_failed: 130362306a36Sopenharmony_ci xfrm6_protocol_deregister(&vti_esp6_protocol, IPPROTO_ESP); 130462306a36Sopenharmony_cixfrm_proto_esp_failed: 130562306a36Sopenharmony_ci unregister_pernet_device(&vti6_net_ops); 130662306a36Sopenharmony_cipernet_dev_failed: 130762306a36Sopenharmony_ci pr_err("vti6 init: failed to register %s\n", msg); 130862306a36Sopenharmony_ci return err; 130962306a36Sopenharmony_ci} 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci/** 131262306a36Sopenharmony_ci * vti6_tunnel_cleanup - free resources and unregister protocol 131362306a36Sopenharmony_ci **/ 131462306a36Sopenharmony_cistatic void __exit vti6_tunnel_cleanup(void) 131562306a36Sopenharmony_ci{ 131662306a36Sopenharmony_ci rtnl_link_unregister(&vti6_link_ops); 131762306a36Sopenharmony_ci#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL) 131862306a36Sopenharmony_ci xfrm6_tunnel_deregister(&vti_ip6ip_handler, AF_INET); 131962306a36Sopenharmony_ci xfrm6_tunnel_deregister(&vti_ipv6_handler, AF_INET6); 132062306a36Sopenharmony_ci#endif 132162306a36Sopenharmony_ci xfrm6_protocol_deregister(&vti_ipcomp6_protocol, IPPROTO_COMP); 132262306a36Sopenharmony_ci xfrm6_protocol_deregister(&vti_ah6_protocol, IPPROTO_AH); 132362306a36Sopenharmony_ci xfrm6_protocol_deregister(&vti_esp6_protocol, IPPROTO_ESP); 132462306a36Sopenharmony_ci unregister_pernet_device(&vti6_net_ops); 132562306a36Sopenharmony_ci} 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_cimodule_init(vti6_tunnel_init); 132862306a36Sopenharmony_cimodule_exit(vti6_tunnel_cleanup); 132962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 133062306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("vti6"); 133162306a36Sopenharmony_ciMODULE_ALIAS_NETDEV("ip6_vti0"); 133262306a36Sopenharmony_ciMODULE_AUTHOR("Steffen Klassert"); 133362306a36Sopenharmony_ciMODULE_DESCRIPTION("IPv6 virtual tunnel interface"); 1334