162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2013 Nicira, Inc. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/capability.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/types.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/uaccess.h> 1462306a36Sopenharmony_ci#include <linux/skbuff.h> 1562306a36Sopenharmony_ci#include <linux/netdevice.h> 1662306a36Sopenharmony_ci#include <linux/in.h> 1762306a36Sopenharmony_ci#include <linux/tcp.h> 1862306a36Sopenharmony_ci#include <linux/udp.h> 1962306a36Sopenharmony_ci#include <linux/if_arp.h> 2062306a36Sopenharmony_ci#include <linux/init.h> 2162306a36Sopenharmony_ci#include <linux/in6.h> 2262306a36Sopenharmony_ci#include <linux/inetdevice.h> 2362306a36Sopenharmony_ci#include <linux/igmp.h> 2462306a36Sopenharmony_ci#include <linux/netfilter_ipv4.h> 2562306a36Sopenharmony_ci#include <linux/etherdevice.h> 2662306a36Sopenharmony_ci#include <linux/if_ether.h> 2762306a36Sopenharmony_ci#include <linux/if_vlan.h> 2862306a36Sopenharmony_ci#include <linux/rculist.h> 2962306a36Sopenharmony_ci#include <linux/err.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <net/sock.h> 3262306a36Sopenharmony_ci#include <net/ip.h> 3362306a36Sopenharmony_ci#include <net/icmp.h> 3462306a36Sopenharmony_ci#include <net/protocol.h> 3562306a36Sopenharmony_ci#include <net/ip_tunnels.h> 3662306a36Sopenharmony_ci#include <net/arp.h> 3762306a36Sopenharmony_ci#include <net/checksum.h> 3862306a36Sopenharmony_ci#include <net/dsfield.h> 3962306a36Sopenharmony_ci#include <net/inet_ecn.h> 4062306a36Sopenharmony_ci#include <net/xfrm.h> 4162306a36Sopenharmony_ci#include <net/net_namespace.h> 4262306a36Sopenharmony_ci#include <net/netns/generic.h> 4362306a36Sopenharmony_ci#include <net/rtnetlink.h> 4462306a36Sopenharmony_ci#include <net/udp.h> 4562306a36Sopenharmony_ci#include <net/dst_metadata.h> 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 4862306a36Sopenharmony_ci#include <net/ipv6.h> 4962306a36Sopenharmony_ci#include <net/ip6_fib.h> 5062306a36Sopenharmony_ci#include <net/ip6_route.h> 5162306a36Sopenharmony_ci#endif 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic unsigned int ip_tunnel_hash(__be32 key, __be32 remote) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci return hash_32((__force u32)key ^ (__force u32)remote, 5662306a36Sopenharmony_ci IP_TNL_HASH_BITS); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic bool ip_tunnel_key_match(const struct ip_tunnel_parm *p, 6062306a36Sopenharmony_ci __be16 flags, __be32 key) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci if (p->i_flags & TUNNEL_KEY) { 6362306a36Sopenharmony_ci if (flags & TUNNEL_KEY) 6462306a36Sopenharmony_ci return key == p->i_key; 6562306a36Sopenharmony_ci else 6662306a36Sopenharmony_ci /* key expected, none present */ 6762306a36Sopenharmony_ci return false; 6862306a36Sopenharmony_ci } else 6962306a36Sopenharmony_ci return !(flags & TUNNEL_KEY); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* Fallback tunnel: no source, no destination, no key, no options 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci Tunnel hash table: 7562306a36Sopenharmony_ci We require exact key match i.e. if a key is present in packet 7662306a36Sopenharmony_ci it will match only tunnel with the same key; if it is not present, 7762306a36Sopenharmony_ci it will match only keyless tunnel. 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci All keysless packets, if not matched configured keyless tunnels 8062306a36Sopenharmony_ci will match fallback tunnel. 8162306a36Sopenharmony_ci Given src, dst and key, find appropriate for input tunnel. 8262306a36Sopenharmony_ci*/ 8362306a36Sopenharmony_cistruct ip_tunnel *ip_tunnel_lookup(struct ip_tunnel_net *itn, 8462306a36Sopenharmony_ci int link, __be16 flags, 8562306a36Sopenharmony_ci __be32 remote, __be32 local, 8662306a36Sopenharmony_ci __be32 key) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct ip_tunnel *t, *cand = NULL; 8962306a36Sopenharmony_ci struct hlist_head *head; 9062306a36Sopenharmony_ci struct net_device *ndev; 9162306a36Sopenharmony_ci unsigned int hash; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci hash = ip_tunnel_hash(key, remote); 9462306a36Sopenharmony_ci head = &itn->tunnels[hash]; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci hlist_for_each_entry_rcu(t, head, hash_node) { 9762306a36Sopenharmony_ci if (local != t->parms.iph.saddr || 9862306a36Sopenharmony_ci remote != t->parms.iph.daddr || 9962306a36Sopenharmony_ci !(t->dev->flags & IFF_UP)) 10062306a36Sopenharmony_ci continue; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (!ip_tunnel_key_match(&t->parms, flags, key)) 10362306a36Sopenharmony_ci continue; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (t->parms.link == link) 10662306a36Sopenharmony_ci return t; 10762306a36Sopenharmony_ci else 10862306a36Sopenharmony_ci cand = t; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci hlist_for_each_entry_rcu(t, head, hash_node) { 11262306a36Sopenharmony_ci if (remote != t->parms.iph.daddr || 11362306a36Sopenharmony_ci t->parms.iph.saddr != 0 || 11462306a36Sopenharmony_ci !(t->dev->flags & IFF_UP)) 11562306a36Sopenharmony_ci continue; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (!ip_tunnel_key_match(&t->parms, flags, key)) 11862306a36Sopenharmony_ci continue; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (t->parms.link == link) 12162306a36Sopenharmony_ci return t; 12262306a36Sopenharmony_ci else if (!cand) 12362306a36Sopenharmony_ci cand = t; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci hash = ip_tunnel_hash(key, 0); 12762306a36Sopenharmony_ci head = &itn->tunnels[hash]; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci hlist_for_each_entry_rcu(t, head, hash_node) { 13062306a36Sopenharmony_ci if ((local != t->parms.iph.saddr || t->parms.iph.daddr != 0) && 13162306a36Sopenharmony_ci (local != t->parms.iph.daddr || !ipv4_is_multicast(local))) 13262306a36Sopenharmony_ci continue; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (!(t->dev->flags & IFF_UP)) 13562306a36Sopenharmony_ci continue; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (!ip_tunnel_key_match(&t->parms, flags, key)) 13862306a36Sopenharmony_ci continue; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (t->parms.link == link) 14162306a36Sopenharmony_ci return t; 14262306a36Sopenharmony_ci else if (!cand) 14362306a36Sopenharmony_ci cand = t; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci hlist_for_each_entry_rcu(t, head, hash_node) { 14762306a36Sopenharmony_ci if ((!(flags & TUNNEL_NO_KEY) && t->parms.i_key != key) || 14862306a36Sopenharmony_ci t->parms.iph.saddr != 0 || 14962306a36Sopenharmony_ci t->parms.iph.daddr != 0 || 15062306a36Sopenharmony_ci !(t->dev->flags & IFF_UP)) 15162306a36Sopenharmony_ci continue; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (t->parms.link == link) 15462306a36Sopenharmony_ci return t; 15562306a36Sopenharmony_ci else if (!cand) 15662306a36Sopenharmony_ci cand = t; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (cand) 16062306a36Sopenharmony_ci return cand; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci t = rcu_dereference(itn->collect_md_tun); 16362306a36Sopenharmony_ci if (t && t->dev->flags & IFF_UP) 16462306a36Sopenharmony_ci return t; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci ndev = READ_ONCE(itn->fb_tunnel_dev); 16762306a36Sopenharmony_ci if (ndev && ndev->flags & IFF_UP) 16862306a36Sopenharmony_ci return netdev_priv(ndev); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return NULL; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip_tunnel_lookup); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic struct hlist_head *ip_bucket(struct ip_tunnel_net *itn, 17562306a36Sopenharmony_ci struct ip_tunnel_parm *parms) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci unsigned int h; 17862306a36Sopenharmony_ci __be32 remote; 17962306a36Sopenharmony_ci __be32 i_key = parms->i_key; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (parms->iph.daddr && !ipv4_is_multicast(parms->iph.daddr)) 18262306a36Sopenharmony_ci remote = parms->iph.daddr; 18362306a36Sopenharmony_ci else 18462306a36Sopenharmony_ci remote = 0; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (!(parms->i_flags & TUNNEL_KEY) && (parms->i_flags & VTI_ISVTI)) 18762306a36Sopenharmony_ci i_key = 0; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci h = ip_tunnel_hash(i_key, remote); 19062306a36Sopenharmony_ci return &itn->tunnels[h]; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic void ip_tunnel_add(struct ip_tunnel_net *itn, struct ip_tunnel *t) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct hlist_head *head = ip_bucket(itn, &t->parms); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (t->collect_md) 19862306a36Sopenharmony_ci rcu_assign_pointer(itn->collect_md_tun, t); 19962306a36Sopenharmony_ci hlist_add_head_rcu(&t->hash_node, head); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic void ip_tunnel_del(struct ip_tunnel_net *itn, struct ip_tunnel *t) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci if (t->collect_md) 20562306a36Sopenharmony_ci rcu_assign_pointer(itn->collect_md_tun, NULL); 20662306a36Sopenharmony_ci hlist_del_init_rcu(&t->hash_node); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic struct ip_tunnel *ip_tunnel_find(struct ip_tunnel_net *itn, 21062306a36Sopenharmony_ci struct ip_tunnel_parm *parms, 21162306a36Sopenharmony_ci int type) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci __be32 remote = parms->iph.daddr; 21462306a36Sopenharmony_ci __be32 local = parms->iph.saddr; 21562306a36Sopenharmony_ci __be32 key = parms->i_key; 21662306a36Sopenharmony_ci __be16 flags = parms->i_flags; 21762306a36Sopenharmony_ci int link = parms->link; 21862306a36Sopenharmony_ci struct ip_tunnel *t = NULL; 21962306a36Sopenharmony_ci struct hlist_head *head = ip_bucket(itn, parms); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci hlist_for_each_entry_rcu(t, head, hash_node) { 22262306a36Sopenharmony_ci if (local == t->parms.iph.saddr && 22362306a36Sopenharmony_ci remote == t->parms.iph.daddr && 22462306a36Sopenharmony_ci link == t->parms.link && 22562306a36Sopenharmony_ci type == t->dev->type && 22662306a36Sopenharmony_ci ip_tunnel_key_match(&t->parms, flags, key)) 22762306a36Sopenharmony_ci break; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci return t; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic struct net_device *__ip_tunnel_create(struct net *net, 23362306a36Sopenharmony_ci const struct rtnl_link_ops *ops, 23462306a36Sopenharmony_ci struct ip_tunnel_parm *parms) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci int err; 23762306a36Sopenharmony_ci struct ip_tunnel *tunnel; 23862306a36Sopenharmony_ci struct net_device *dev; 23962306a36Sopenharmony_ci char name[IFNAMSIZ]; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci err = -E2BIG; 24262306a36Sopenharmony_ci if (parms->name[0]) { 24362306a36Sopenharmony_ci if (!dev_valid_name(parms->name)) 24462306a36Sopenharmony_ci goto failed; 24562306a36Sopenharmony_ci strscpy(name, parms->name, IFNAMSIZ); 24662306a36Sopenharmony_ci } else { 24762306a36Sopenharmony_ci if (strlen(ops->kind) > (IFNAMSIZ - 3)) 24862306a36Sopenharmony_ci goto failed; 24962306a36Sopenharmony_ci strcpy(name, ops->kind); 25062306a36Sopenharmony_ci strcat(name, "%d"); 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci ASSERT_RTNL(); 25462306a36Sopenharmony_ci dev = alloc_netdev(ops->priv_size, name, NET_NAME_UNKNOWN, ops->setup); 25562306a36Sopenharmony_ci if (!dev) { 25662306a36Sopenharmony_ci err = -ENOMEM; 25762306a36Sopenharmony_ci goto failed; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci dev_net_set(dev, net); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci dev->rtnl_link_ops = ops; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci tunnel = netdev_priv(dev); 26462306a36Sopenharmony_ci tunnel->parms = *parms; 26562306a36Sopenharmony_ci tunnel->net = net; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci err = register_netdevice(dev); 26862306a36Sopenharmony_ci if (err) 26962306a36Sopenharmony_ci goto failed_free; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return dev; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cifailed_free: 27462306a36Sopenharmony_ci free_netdev(dev); 27562306a36Sopenharmony_cifailed: 27662306a36Sopenharmony_ci return ERR_PTR(err); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic int ip_tunnel_bind_dev(struct net_device *dev) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct net_device *tdev = NULL; 28262306a36Sopenharmony_ci struct ip_tunnel *tunnel = netdev_priv(dev); 28362306a36Sopenharmony_ci const struct iphdr *iph; 28462306a36Sopenharmony_ci int hlen = LL_MAX_HEADER; 28562306a36Sopenharmony_ci int mtu = ETH_DATA_LEN; 28662306a36Sopenharmony_ci int t_hlen = tunnel->hlen + sizeof(struct iphdr); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci iph = &tunnel->parms.iph; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* Guess output device to choose reasonable mtu and needed_headroom */ 29162306a36Sopenharmony_ci if (iph->daddr) { 29262306a36Sopenharmony_ci struct flowi4 fl4; 29362306a36Sopenharmony_ci struct rtable *rt; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci ip_tunnel_init_flow(&fl4, iph->protocol, iph->daddr, 29662306a36Sopenharmony_ci iph->saddr, tunnel->parms.o_key, 29762306a36Sopenharmony_ci RT_TOS(iph->tos), dev_net(dev), 29862306a36Sopenharmony_ci tunnel->parms.link, tunnel->fwmark, 0, 0); 29962306a36Sopenharmony_ci rt = ip_route_output_key(tunnel->net, &fl4); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (!IS_ERR(rt)) { 30262306a36Sopenharmony_ci tdev = rt->dst.dev; 30362306a36Sopenharmony_ci ip_rt_put(rt); 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci if (dev->type != ARPHRD_ETHER) 30662306a36Sopenharmony_ci dev->flags |= IFF_POINTOPOINT; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci dst_cache_reset(&tunnel->dst_cache); 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (!tdev && tunnel->parms.link) 31262306a36Sopenharmony_ci tdev = __dev_get_by_index(tunnel->net, tunnel->parms.link); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (tdev) { 31562306a36Sopenharmony_ci hlen = tdev->hard_header_len + tdev->needed_headroom; 31662306a36Sopenharmony_ci mtu = min(tdev->mtu, IP_MAX_MTU); 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci dev->needed_headroom = t_hlen + hlen; 32062306a36Sopenharmony_ci mtu -= t_hlen + (dev->type == ARPHRD_ETHER ? dev->hard_header_len : 0); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (mtu < IPV4_MIN_MTU) 32362306a36Sopenharmony_ci mtu = IPV4_MIN_MTU; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci return mtu; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic struct ip_tunnel *ip_tunnel_create(struct net *net, 32962306a36Sopenharmony_ci struct ip_tunnel_net *itn, 33062306a36Sopenharmony_ci struct ip_tunnel_parm *parms) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct ip_tunnel *nt; 33362306a36Sopenharmony_ci struct net_device *dev; 33462306a36Sopenharmony_ci int t_hlen; 33562306a36Sopenharmony_ci int mtu; 33662306a36Sopenharmony_ci int err; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci dev = __ip_tunnel_create(net, itn->rtnl_link_ops, parms); 33962306a36Sopenharmony_ci if (IS_ERR(dev)) 34062306a36Sopenharmony_ci return ERR_CAST(dev); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci mtu = ip_tunnel_bind_dev(dev); 34362306a36Sopenharmony_ci err = dev_set_mtu(dev, mtu); 34462306a36Sopenharmony_ci if (err) 34562306a36Sopenharmony_ci goto err_dev_set_mtu; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci nt = netdev_priv(dev); 34862306a36Sopenharmony_ci t_hlen = nt->hlen + sizeof(struct iphdr); 34962306a36Sopenharmony_ci dev->min_mtu = ETH_MIN_MTU; 35062306a36Sopenharmony_ci dev->max_mtu = IP_MAX_MTU - t_hlen; 35162306a36Sopenharmony_ci if (dev->type == ARPHRD_ETHER) 35262306a36Sopenharmony_ci dev->max_mtu -= dev->hard_header_len; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci ip_tunnel_add(itn, nt); 35562306a36Sopenharmony_ci return nt; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cierr_dev_set_mtu: 35862306a36Sopenharmony_ci unregister_netdevice(dev); 35962306a36Sopenharmony_ci return ERR_PTR(err); 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_civoid ip_tunnel_md_udp_encap(struct sk_buff *skb, struct ip_tunnel_info *info) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci const struct iphdr *iph = ip_hdr(skb); 36562306a36Sopenharmony_ci const struct udphdr *udph; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (iph->protocol != IPPROTO_UDP) 36862306a36Sopenharmony_ci return; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci udph = (struct udphdr *)((__u8 *)iph + (iph->ihl << 2)); 37162306a36Sopenharmony_ci info->encap.sport = udph->source; 37262306a36Sopenharmony_ci info->encap.dport = udph->dest; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ciEXPORT_SYMBOL(ip_tunnel_md_udp_encap); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ciint ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb, 37762306a36Sopenharmony_ci const struct tnl_ptk_info *tpi, struct metadata_dst *tun_dst, 37862306a36Sopenharmony_ci bool log_ecn_error) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci const struct iphdr *iph = ip_hdr(skb); 38162306a36Sopenharmony_ci int nh, err; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci#ifdef CONFIG_NET_IPGRE_BROADCAST 38462306a36Sopenharmony_ci if (ipv4_is_multicast(iph->daddr)) { 38562306a36Sopenharmony_ci DEV_STATS_INC(tunnel->dev, multicast); 38662306a36Sopenharmony_ci skb->pkt_type = PACKET_BROADCAST; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci#endif 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if ((!(tpi->flags&TUNNEL_CSUM) && (tunnel->parms.i_flags&TUNNEL_CSUM)) || 39162306a36Sopenharmony_ci ((tpi->flags&TUNNEL_CSUM) && !(tunnel->parms.i_flags&TUNNEL_CSUM))) { 39262306a36Sopenharmony_ci DEV_STATS_INC(tunnel->dev, rx_crc_errors); 39362306a36Sopenharmony_ci DEV_STATS_INC(tunnel->dev, rx_errors); 39462306a36Sopenharmony_ci goto drop; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (tunnel->parms.i_flags&TUNNEL_SEQ) { 39862306a36Sopenharmony_ci if (!(tpi->flags&TUNNEL_SEQ) || 39962306a36Sopenharmony_ci (tunnel->i_seqno && (s32)(ntohl(tpi->seq) - tunnel->i_seqno) < 0)) { 40062306a36Sopenharmony_ci DEV_STATS_INC(tunnel->dev, rx_fifo_errors); 40162306a36Sopenharmony_ci DEV_STATS_INC(tunnel->dev, rx_errors); 40262306a36Sopenharmony_ci goto drop; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci tunnel->i_seqno = ntohl(tpi->seq) + 1; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* Save offset of outer header relative to skb->head, 40862306a36Sopenharmony_ci * because we are going to reset the network header to the inner header 40962306a36Sopenharmony_ci * and might change skb->head. 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_ci nh = skb_network_header(skb) - skb->head; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci skb_set_network_header(skb, (tunnel->dev->type == ARPHRD_ETHER) ? ETH_HLEN : 0); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (!pskb_inet_may_pull(skb)) { 41662306a36Sopenharmony_ci DEV_STATS_INC(tunnel->dev, rx_length_errors); 41762306a36Sopenharmony_ci DEV_STATS_INC(tunnel->dev, rx_errors); 41862306a36Sopenharmony_ci goto drop; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci iph = (struct iphdr *)(skb->head + nh); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci err = IP_ECN_decapsulate(iph, skb); 42362306a36Sopenharmony_ci if (unlikely(err)) { 42462306a36Sopenharmony_ci if (log_ecn_error) 42562306a36Sopenharmony_ci net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n", 42662306a36Sopenharmony_ci &iph->saddr, iph->tos); 42762306a36Sopenharmony_ci if (err > 1) { 42862306a36Sopenharmony_ci DEV_STATS_INC(tunnel->dev, rx_frame_errors); 42962306a36Sopenharmony_ci DEV_STATS_INC(tunnel->dev, rx_errors); 43062306a36Sopenharmony_ci goto drop; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci dev_sw_netstats_rx_add(tunnel->dev, skb->len); 43562306a36Sopenharmony_ci skb_scrub_packet(skb, !net_eq(tunnel->net, dev_net(tunnel->dev))); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (tunnel->dev->type == ARPHRD_ETHER) { 43862306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, tunnel->dev); 43962306a36Sopenharmony_ci skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); 44062306a36Sopenharmony_ci } else { 44162306a36Sopenharmony_ci skb->dev = tunnel->dev; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (tun_dst) 44562306a36Sopenharmony_ci skb_dst_set(skb, (struct dst_entry *)tun_dst); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci gro_cells_receive(&tunnel->gro_cells, skb); 44862306a36Sopenharmony_ci return 0; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cidrop: 45162306a36Sopenharmony_ci if (tun_dst) 45262306a36Sopenharmony_ci dst_release((struct dst_entry *)tun_dst); 45362306a36Sopenharmony_ci kfree_skb(skb); 45462306a36Sopenharmony_ci return 0; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip_tunnel_rcv); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ciint ip_tunnel_encap_add_ops(const struct ip_tunnel_encap_ops *ops, 45962306a36Sopenharmony_ci unsigned int num) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci if (num >= MAX_IPTUN_ENCAP_OPS) 46262306a36Sopenharmony_ci return -ERANGE; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return !cmpxchg((const struct ip_tunnel_encap_ops **) 46562306a36Sopenharmony_ci &iptun_encaps[num], 46662306a36Sopenharmony_ci NULL, ops) ? 0 : -1; 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ciEXPORT_SYMBOL(ip_tunnel_encap_add_ops); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ciint ip_tunnel_encap_del_ops(const struct ip_tunnel_encap_ops *ops, 47162306a36Sopenharmony_ci unsigned int num) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci int ret; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (num >= MAX_IPTUN_ENCAP_OPS) 47662306a36Sopenharmony_ci return -ERANGE; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci ret = (cmpxchg((const struct ip_tunnel_encap_ops **) 47962306a36Sopenharmony_ci &iptun_encaps[num], 48062306a36Sopenharmony_ci ops, NULL) == ops) ? 0 : -1; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci synchronize_net(); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci return ret; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ciEXPORT_SYMBOL(ip_tunnel_encap_del_ops); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ciint ip_tunnel_encap_setup(struct ip_tunnel *t, 48962306a36Sopenharmony_ci struct ip_tunnel_encap *ipencap) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci int hlen; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci memset(&t->encap, 0, sizeof(t->encap)); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci hlen = ip_encap_hlen(ipencap); 49662306a36Sopenharmony_ci if (hlen < 0) 49762306a36Sopenharmony_ci return hlen; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci t->encap.type = ipencap->type; 50062306a36Sopenharmony_ci t->encap.sport = ipencap->sport; 50162306a36Sopenharmony_ci t->encap.dport = ipencap->dport; 50262306a36Sopenharmony_ci t->encap.flags = ipencap->flags; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci t->encap_hlen = hlen; 50562306a36Sopenharmony_ci t->hlen = t->encap_hlen + t->tun_hlen; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci return 0; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip_tunnel_encap_setup); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic int tnl_update_pmtu(struct net_device *dev, struct sk_buff *skb, 51262306a36Sopenharmony_ci struct rtable *rt, __be16 df, 51362306a36Sopenharmony_ci const struct iphdr *inner_iph, 51462306a36Sopenharmony_ci int tunnel_hlen, __be32 dst, bool md) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci struct ip_tunnel *tunnel = netdev_priv(dev); 51762306a36Sopenharmony_ci int pkt_size; 51862306a36Sopenharmony_ci int mtu; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci tunnel_hlen = md ? tunnel_hlen : tunnel->hlen; 52162306a36Sopenharmony_ci pkt_size = skb->len - tunnel_hlen; 52262306a36Sopenharmony_ci pkt_size -= dev->type == ARPHRD_ETHER ? dev->hard_header_len : 0; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (df) { 52562306a36Sopenharmony_ci mtu = dst_mtu(&rt->dst) - (sizeof(struct iphdr) + tunnel_hlen); 52662306a36Sopenharmony_ci mtu -= dev->type == ARPHRD_ETHER ? dev->hard_header_len : 0; 52762306a36Sopenharmony_ci } else { 52862306a36Sopenharmony_ci mtu = skb_valid_dst(skb) ? dst_mtu(skb_dst(skb)) : dev->mtu; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (skb_valid_dst(skb)) 53262306a36Sopenharmony_ci skb_dst_update_pmtu_no_confirm(skb, mtu); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) { 53562306a36Sopenharmony_ci if (!skb_is_gso(skb) && 53662306a36Sopenharmony_ci (inner_iph->frag_off & htons(IP_DF)) && 53762306a36Sopenharmony_ci mtu < pkt_size) { 53862306a36Sopenharmony_ci icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu)); 53962306a36Sopenharmony_ci return -E2BIG; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 54362306a36Sopenharmony_ci else if (skb->protocol == htons(ETH_P_IPV6)) { 54462306a36Sopenharmony_ci struct rt6_info *rt6; 54562306a36Sopenharmony_ci __be32 daddr; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci rt6 = skb_valid_dst(skb) ? (struct rt6_info *)skb_dst(skb) : 54862306a36Sopenharmony_ci NULL; 54962306a36Sopenharmony_ci daddr = md ? dst : tunnel->parms.iph.daddr; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (rt6 && mtu < dst_mtu(skb_dst(skb)) && 55262306a36Sopenharmony_ci mtu >= IPV6_MIN_MTU) { 55362306a36Sopenharmony_ci if ((daddr && !ipv4_is_multicast(daddr)) || 55462306a36Sopenharmony_ci rt6->rt6i_dst.plen == 128) { 55562306a36Sopenharmony_ci rt6->rt6i_flags |= RTF_MODIFIED; 55662306a36Sopenharmony_ci dst_metric_set(skb_dst(skb), RTAX_MTU, mtu); 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (!skb_is_gso(skb) && mtu >= IPV6_MIN_MTU && 56162306a36Sopenharmony_ci mtu < pkt_size) { 56262306a36Sopenharmony_ci icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); 56362306a36Sopenharmony_ci return -E2BIG; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci#endif 56762306a36Sopenharmony_ci return 0; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cistatic void ip_tunnel_adj_headroom(struct net_device *dev, unsigned int headroom) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci /* we must cap headroom to some upperlimit, else pskb_expand_head 57362306a36Sopenharmony_ci * will overflow header offsets in skb_headers_offset_update(). 57462306a36Sopenharmony_ci */ 57562306a36Sopenharmony_ci static const unsigned int max_allowed = 512; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (headroom > max_allowed) 57862306a36Sopenharmony_ci headroom = max_allowed; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (headroom > READ_ONCE(dev->needed_headroom)) 58162306a36Sopenharmony_ci WRITE_ONCE(dev->needed_headroom, headroom); 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_civoid ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, 58562306a36Sopenharmony_ci u8 proto, int tunnel_hlen) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci struct ip_tunnel *tunnel = netdev_priv(dev); 58862306a36Sopenharmony_ci u32 headroom = sizeof(struct iphdr); 58962306a36Sopenharmony_ci struct ip_tunnel_info *tun_info; 59062306a36Sopenharmony_ci const struct ip_tunnel_key *key; 59162306a36Sopenharmony_ci const struct iphdr *inner_iph; 59262306a36Sopenharmony_ci struct rtable *rt = NULL; 59362306a36Sopenharmony_ci struct flowi4 fl4; 59462306a36Sopenharmony_ci __be16 df = 0; 59562306a36Sopenharmony_ci u8 tos, ttl; 59662306a36Sopenharmony_ci bool use_cache; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci tun_info = skb_tunnel_info(skb); 59962306a36Sopenharmony_ci if (unlikely(!tun_info || !(tun_info->mode & IP_TUNNEL_INFO_TX) || 60062306a36Sopenharmony_ci ip_tunnel_info_af(tun_info) != AF_INET)) 60162306a36Sopenharmony_ci goto tx_error; 60262306a36Sopenharmony_ci key = &tun_info->key; 60362306a36Sopenharmony_ci memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); 60462306a36Sopenharmony_ci inner_iph = (const struct iphdr *)skb_inner_network_header(skb); 60562306a36Sopenharmony_ci tos = key->tos; 60662306a36Sopenharmony_ci if (tos == 1) { 60762306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) 60862306a36Sopenharmony_ci tos = inner_iph->tos; 60962306a36Sopenharmony_ci else if (skb->protocol == htons(ETH_P_IPV6)) 61062306a36Sopenharmony_ci tos = ipv6_get_dsfield((const struct ipv6hdr *)inner_iph); 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci ip_tunnel_init_flow(&fl4, proto, key->u.ipv4.dst, key->u.ipv4.src, 61362306a36Sopenharmony_ci tunnel_id_to_key32(key->tun_id), RT_TOS(tos), 61462306a36Sopenharmony_ci dev_net(dev), 0, skb->mark, skb_get_hash(skb), 61562306a36Sopenharmony_ci key->flow_flags); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (!tunnel_hlen) 61862306a36Sopenharmony_ci tunnel_hlen = ip_encap_hlen(&tun_info->encap); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (ip_tunnel_encap(skb, &tun_info->encap, &proto, &fl4) < 0) 62162306a36Sopenharmony_ci goto tx_error; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci use_cache = ip_tunnel_dst_cache_usable(skb, tun_info); 62462306a36Sopenharmony_ci if (use_cache) 62562306a36Sopenharmony_ci rt = dst_cache_get_ip4(&tun_info->dst_cache, &fl4.saddr); 62662306a36Sopenharmony_ci if (!rt) { 62762306a36Sopenharmony_ci rt = ip_route_output_key(tunnel->net, &fl4); 62862306a36Sopenharmony_ci if (IS_ERR(rt)) { 62962306a36Sopenharmony_ci DEV_STATS_INC(dev, tx_carrier_errors); 63062306a36Sopenharmony_ci goto tx_error; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci if (use_cache) 63362306a36Sopenharmony_ci dst_cache_set_ip4(&tun_info->dst_cache, &rt->dst, 63462306a36Sopenharmony_ci fl4.saddr); 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci if (rt->dst.dev == dev) { 63762306a36Sopenharmony_ci ip_rt_put(rt); 63862306a36Sopenharmony_ci DEV_STATS_INC(dev, collisions); 63962306a36Sopenharmony_ci goto tx_error; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (key->tun_flags & TUNNEL_DONT_FRAGMENT) 64362306a36Sopenharmony_ci df = htons(IP_DF); 64462306a36Sopenharmony_ci if (tnl_update_pmtu(dev, skb, rt, df, inner_iph, tunnel_hlen, 64562306a36Sopenharmony_ci key->u.ipv4.dst, true)) { 64662306a36Sopenharmony_ci ip_rt_put(rt); 64762306a36Sopenharmony_ci goto tx_error; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci tos = ip_tunnel_ecn_encap(tos, inner_iph, skb); 65162306a36Sopenharmony_ci ttl = key->ttl; 65262306a36Sopenharmony_ci if (ttl == 0) { 65362306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) 65462306a36Sopenharmony_ci ttl = inner_iph->ttl; 65562306a36Sopenharmony_ci else if (skb->protocol == htons(ETH_P_IPV6)) 65662306a36Sopenharmony_ci ttl = ((const struct ipv6hdr *)inner_iph)->hop_limit; 65762306a36Sopenharmony_ci else 65862306a36Sopenharmony_ci ttl = ip4_dst_hoplimit(&rt->dst); 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci headroom += LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len; 66262306a36Sopenharmony_ci if (skb_cow_head(skb, headroom)) { 66362306a36Sopenharmony_ci ip_rt_put(rt); 66462306a36Sopenharmony_ci goto tx_dropped; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci ip_tunnel_adj_headroom(dev, headroom); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr, proto, tos, ttl, 67062306a36Sopenharmony_ci df, !net_eq(tunnel->net, dev_net(dev))); 67162306a36Sopenharmony_ci return; 67262306a36Sopenharmony_citx_error: 67362306a36Sopenharmony_ci DEV_STATS_INC(dev, tx_errors); 67462306a36Sopenharmony_ci goto kfree; 67562306a36Sopenharmony_citx_dropped: 67662306a36Sopenharmony_ci DEV_STATS_INC(dev, tx_dropped); 67762306a36Sopenharmony_cikfree: 67862306a36Sopenharmony_ci kfree_skb(skb); 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip_md_tunnel_xmit); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_civoid ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, 68362306a36Sopenharmony_ci const struct iphdr *tnl_params, u8 protocol) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci struct ip_tunnel *tunnel = netdev_priv(dev); 68662306a36Sopenharmony_ci struct ip_tunnel_info *tun_info = NULL; 68762306a36Sopenharmony_ci const struct iphdr *inner_iph; 68862306a36Sopenharmony_ci unsigned int max_headroom; /* The extra header space needed */ 68962306a36Sopenharmony_ci struct rtable *rt = NULL; /* Route to the other host */ 69062306a36Sopenharmony_ci __be16 payload_protocol; 69162306a36Sopenharmony_ci bool use_cache = false; 69262306a36Sopenharmony_ci struct flowi4 fl4; 69362306a36Sopenharmony_ci bool md = false; 69462306a36Sopenharmony_ci bool connected; 69562306a36Sopenharmony_ci u8 tos, ttl; 69662306a36Sopenharmony_ci __be32 dst; 69762306a36Sopenharmony_ci __be16 df; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci inner_iph = (const struct iphdr *)skb_inner_network_header(skb); 70062306a36Sopenharmony_ci connected = (tunnel->parms.iph.daddr != 0); 70162306a36Sopenharmony_ci payload_protocol = skb_protocol(skb, true); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci dst = tnl_params->daddr; 70662306a36Sopenharmony_ci if (dst == 0) { 70762306a36Sopenharmony_ci /* NBMA tunnel */ 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci if (!skb_dst(skb)) { 71062306a36Sopenharmony_ci DEV_STATS_INC(dev, tx_fifo_errors); 71162306a36Sopenharmony_ci goto tx_error; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci tun_info = skb_tunnel_info(skb); 71562306a36Sopenharmony_ci if (tun_info && (tun_info->mode & IP_TUNNEL_INFO_TX) && 71662306a36Sopenharmony_ci ip_tunnel_info_af(tun_info) == AF_INET && 71762306a36Sopenharmony_ci tun_info->key.u.ipv4.dst) { 71862306a36Sopenharmony_ci dst = tun_info->key.u.ipv4.dst; 71962306a36Sopenharmony_ci md = true; 72062306a36Sopenharmony_ci connected = true; 72162306a36Sopenharmony_ci } else if (payload_protocol == htons(ETH_P_IP)) { 72262306a36Sopenharmony_ci rt = skb_rtable(skb); 72362306a36Sopenharmony_ci dst = rt_nexthop(rt, inner_iph->daddr); 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 72662306a36Sopenharmony_ci else if (payload_protocol == htons(ETH_P_IPV6)) { 72762306a36Sopenharmony_ci const struct in6_addr *addr6; 72862306a36Sopenharmony_ci struct neighbour *neigh; 72962306a36Sopenharmony_ci bool do_tx_error_icmp; 73062306a36Sopenharmony_ci int addr_type; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci neigh = dst_neigh_lookup(skb_dst(skb), 73362306a36Sopenharmony_ci &ipv6_hdr(skb)->daddr); 73462306a36Sopenharmony_ci if (!neigh) 73562306a36Sopenharmony_ci goto tx_error; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci addr6 = (const struct in6_addr *)&neigh->primary_key; 73862306a36Sopenharmony_ci addr_type = ipv6_addr_type(addr6); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (addr_type == IPV6_ADDR_ANY) { 74162306a36Sopenharmony_ci addr6 = &ipv6_hdr(skb)->daddr; 74262306a36Sopenharmony_ci addr_type = ipv6_addr_type(addr6); 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci if ((addr_type & IPV6_ADDR_COMPATv4) == 0) 74662306a36Sopenharmony_ci do_tx_error_icmp = true; 74762306a36Sopenharmony_ci else { 74862306a36Sopenharmony_ci do_tx_error_icmp = false; 74962306a36Sopenharmony_ci dst = addr6->s6_addr32[3]; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci neigh_release(neigh); 75262306a36Sopenharmony_ci if (do_tx_error_icmp) 75362306a36Sopenharmony_ci goto tx_error_icmp; 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci#endif 75662306a36Sopenharmony_ci else 75762306a36Sopenharmony_ci goto tx_error; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (!md) 76062306a36Sopenharmony_ci connected = false; 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci tos = tnl_params->tos; 76462306a36Sopenharmony_ci if (tos & 0x1) { 76562306a36Sopenharmony_ci tos &= ~0x1; 76662306a36Sopenharmony_ci if (payload_protocol == htons(ETH_P_IP)) { 76762306a36Sopenharmony_ci tos = inner_iph->tos; 76862306a36Sopenharmony_ci connected = false; 76962306a36Sopenharmony_ci } else if (payload_protocol == htons(ETH_P_IPV6)) { 77062306a36Sopenharmony_ci tos = ipv6_get_dsfield((const struct ipv6hdr *)inner_iph); 77162306a36Sopenharmony_ci connected = false; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci ip_tunnel_init_flow(&fl4, protocol, dst, tnl_params->saddr, 77662306a36Sopenharmony_ci tunnel->parms.o_key, RT_TOS(tos), 77762306a36Sopenharmony_ci dev_net(dev), tunnel->parms.link, 77862306a36Sopenharmony_ci tunnel->fwmark, skb_get_hash(skb), 0); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (ip_tunnel_encap(skb, &tunnel->encap, &protocol, &fl4) < 0) 78162306a36Sopenharmony_ci goto tx_error; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if (connected && md) { 78462306a36Sopenharmony_ci use_cache = ip_tunnel_dst_cache_usable(skb, tun_info); 78562306a36Sopenharmony_ci if (use_cache) 78662306a36Sopenharmony_ci rt = dst_cache_get_ip4(&tun_info->dst_cache, 78762306a36Sopenharmony_ci &fl4.saddr); 78862306a36Sopenharmony_ci } else { 78962306a36Sopenharmony_ci rt = connected ? dst_cache_get_ip4(&tunnel->dst_cache, 79062306a36Sopenharmony_ci &fl4.saddr) : NULL; 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci if (!rt) { 79462306a36Sopenharmony_ci rt = ip_route_output_key(tunnel->net, &fl4); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (IS_ERR(rt)) { 79762306a36Sopenharmony_ci DEV_STATS_INC(dev, tx_carrier_errors); 79862306a36Sopenharmony_ci goto tx_error; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci if (use_cache) 80162306a36Sopenharmony_ci dst_cache_set_ip4(&tun_info->dst_cache, &rt->dst, 80262306a36Sopenharmony_ci fl4.saddr); 80362306a36Sopenharmony_ci else if (!md && connected) 80462306a36Sopenharmony_ci dst_cache_set_ip4(&tunnel->dst_cache, &rt->dst, 80562306a36Sopenharmony_ci fl4.saddr); 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (rt->dst.dev == dev) { 80962306a36Sopenharmony_ci ip_rt_put(rt); 81062306a36Sopenharmony_ci DEV_STATS_INC(dev, collisions); 81162306a36Sopenharmony_ci goto tx_error; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci df = tnl_params->frag_off; 81562306a36Sopenharmony_ci if (payload_protocol == htons(ETH_P_IP) && !tunnel->ignore_df) 81662306a36Sopenharmony_ci df |= (inner_iph->frag_off & htons(IP_DF)); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci if (tnl_update_pmtu(dev, skb, rt, df, inner_iph, 0, 0, false)) { 81962306a36Sopenharmony_ci ip_rt_put(rt); 82062306a36Sopenharmony_ci goto tx_error; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci if (tunnel->err_count > 0) { 82462306a36Sopenharmony_ci if (time_before(jiffies, 82562306a36Sopenharmony_ci tunnel->err_time + IPTUNNEL_ERR_TIMEO)) { 82662306a36Sopenharmony_ci tunnel->err_count--; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci dst_link_failure(skb); 82962306a36Sopenharmony_ci } else 83062306a36Sopenharmony_ci tunnel->err_count = 0; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci tos = ip_tunnel_ecn_encap(tos, inner_iph, skb); 83462306a36Sopenharmony_ci ttl = tnl_params->ttl; 83562306a36Sopenharmony_ci if (ttl == 0) { 83662306a36Sopenharmony_ci if (payload_protocol == htons(ETH_P_IP)) 83762306a36Sopenharmony_ci ttl = inner_iph->ttl; 83862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 83962306a36Sopenharmony_ci else if (payload_protocol == htons(ETH_P_IPV6)) 84062306a36Sopenharmony_ci ttl = ((const struct ipv6hdr *)inner_iph)->hop_limit; 84162306a36Sopenharmony_ci#endif 84262306a36Sopenharmony_ci else 84362306a36Sopenharmony_ci ttl = ip4_dst_hoplimit(&rt->dst); 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci max_headroom = LL_RESERVED_SPACE(rt->dst.dev) + sizeof(struct iphdr) 84762306a36Sopenharmony_ci + rt->dst.header_len + ip_encap_hlen(&tunnel->encap); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (skb_cow_head(skb, max_headroom)) { 85062306a36Sopenharmony_ci ip_rt_put(rt); 85162306a36Sopenharmony_ci DEV_STATS_INC(dev, tx_dropped); 85262306a36Sopenharmony_ci kfree_skb(skb); 85362306a36Sopenharmony_ci return; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci ip_tunnel_adj_headroom(dev, max_headroom); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr, protocol, tos, ttl, 85962306a36Sopenharmony_ci df, !net_eq(tunnel->net, dev_net(dev))); 86062306a36Sopenharmony_ci return; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 86362306a36Sopenharmony_citx_error_icmp: 86462306a36Sopenharmony_ci dst_link_failure(skb); 86562306a36Sopenharmony_ci#endif 86662306a36Sopenharmony_citx_error: 86762306a36Sopenharmony_ci DEV_STATS_INC(dev, tx_errors); 86862306a36Sopenharmony_ci kfree_skb(skb); 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip_tunnel_xmit); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_cistatic void ip_tunnel_update(struct ip_tunnel_net *itn, 87362306a36Sopenharmony_ci struct ip_tunnel *t, 87462306a36Sopenharmony_ci struct net_device *dev, 87562306a36Sopenharmony_ci struct ip_tunnel_parm *p, 87662306a36Sopenharmony_ci bool set_mtu, 87762306a36Sopenharmony_ci __u32 fwmark) 87862306a36Sopenharmony_ci{ 87962306a36Sopenharmony_ci ip_tunnel_del(itn, t); 88062306a36Sopenharmony_ci t->parms.iph.saddr = p->iph.saddr; 88162306a36Sopenharmony_ci t->parms.iph.daddr = p->iph.daddr; 88262306a36Sopenharmony_ci t->parms.i_key = p->i_key; 88362306a36Sopenharmony_ci t->parms.o_key = p->o_key; 88462306a36Sopenharmony_ci if (dev->type != ARPHRD_ETHER) { 88562306a36Sopenharmony_ci __dev_addr_set(dev, &p->iph.saddr, 4); 88662306a36Sopenharmony_ci memcpy(dev->broadcast, &p->iph.daddr, 4); 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci ip_tunnel_add(itn, t); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci t->parms.iph.ttl = p->iph.ttl; 89162306a36Sopenharmony_ci t->parms.iph.tos = p->iph.tos; 89262306a36Sopenharmony_ci t->parms.iph.frag_off = p->iph.frag_off; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci if (t->parms.link != p->link || t->fwmark != fwmark) { 89562306a36Sopenharmony_ci int mtu; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci t->parms.link = p->link; 89862306a36Sopenharmony_ci t->fwmark = fwmark; 89962306a36Sopenharmony_ci mtu = ip_tunnel_bind_dev(dev); 90062306a36Sopenharmony_ci if (set_mtu) 90162306a36Sopenharmony_ci dev->mtu = mtu; 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci dst_cache_reset(&t->dst_cache); 90462306a36Sopenharmony_ci netdev_state_change(dev); 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ciint ip_tunnel_ctl(struct net_device *dev, struct ip_tunnel_parm *p, int cmd) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci int err = 0; 91062306a36Sopenharmony_ci struct ip_tunnel *t = netdev_priv(dev); 91162306a36Sopenharmony_ci struct net *net = t->net; 91262306a36Sopenharmony_ci struct ip_tunnel_net *itn = net_generic(net, t->ip_tnl_net_id); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci switch (cmd) { 91562306a36Sopenharmony_ci case SIOCGETTUNNEL: 91662306a36Sopenharmony_ci if (dev == itn->fb_tunnel_dev) { 91762306a36Sopenharmony_ci t = ip_tunnel_find(itn, p, itn->fb_tunnel_dev->type); 91862306a36Sopenharmony_ci if (!t) 91962306a36Sopenharmony_ci t = netdev_priv(dev); 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci memcpy(p, &t->parms, sizeof(*p)); 92262306a36Sopenharmony_ci break; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci case SIOCADDTUNNEL: 92562306a36Sopenharmony_ci case SIOCCHGTUNNEL: 92662306a36Sopenharmony_ci err = -EPERM; 92762306a36Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 92862306a36Sopenharmony_ci goto done; 92962306a36Sopenharmony_ci if (p->iph.ttl) 93062306a36Sopenharmony_ci p->iph.frag_off |= htons(IP_DF); 93162306a36Sopenharmony_ci if (!(p->i_flags & VTI_ISVTI)) { 93262306a36Sopenharmony_ci if (!(p->i_flags & TUNNEL_KEY)) 93362306a36Sopenharmony_ci p->i_key = 0; 93462306a36Sopenharmony_ci if (!(p->o_flags & TUNNEL_KEY)) 93562306a36Sopenharmony_ci p->o_key = 0; 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci t = ip_tunnel_find(itn, p, itn->type); 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci if (cmd == SIOCADDTUNNEL) { 94162306a36Sopenharmony_ci if (!t) { 94262306a36Sopenharmony_ci t = ip_tunnel_create(net, itn, p); 94362306a36Sopenharmony_ci err = PTR_ERR_OR_ZERO(t); 94462306a36Sopenharmony_ci break; 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci err = -EEXIST; 94862306a36Sopenharmony_ci break; 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci if (dev != itn->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) { 95162306a36Sopenharmony_ci if (t) { 95262306a36Sopenharmony_ci if (t->dev != dev) { 95362306a36Sopenharmony_ci err = -EEXIST; 95462306a36Sopenharmony_ci break; 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci } else { 95762306a36Sopenharmony_ci unsigned int nflags = 0; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci if (ipv4_is_multicast(p->iph.daddr)) 96062306a36Sopenharmony_ci nflags = IFF_BROADCAST; 96162306a36Sopenharmony_ci else if (p->iph.daddr) 96262306a36Sopenharmony_ci nflags = IFF_POINTOPOINT; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci if ((dev->flags^nflags)&(IFF_POINTOPOINT|IFF_BROADCAST)) { 96562306a36Sopenharmony_ci err = -EINVAL; 96662306a36Sopenharmony_ci break; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci t = netdev_priv(dev); 97062306a36Sopenharmony_ci } 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci if (t) { 97462306a36Sopenharmony_ci err = 0; 97562306a36Sopenharmony_ci ip_tunnel_update(itn, t, dev, p, true, 0); 97662306a36Sopenharmony_ci } else { 97762306a36Sopenharmony_ci err = -ENOENT; 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci break; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci case SIOCDELTUNNEL: 98262306a36Sopenharmony_ci err = -EPERM; 98362306a36Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 98462306a36Sopenharmony_ci goto done; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (dev == itn->fb_tunnel_dev) { 98762306a36Sopenharmony_ci err = -ENOENT; 98862306a36Sopenharmony_ci t = ip_tunnel_find(itn, p, itn->fb_tunnel_dev->type); 98962306a36Sopenharmony_ci if (!t) 99062306a36Sopenharmony_ci goto done; 99162306a36Sopenharmony_ci err = -EPERM; 99262306a36Sopenharmony_ci if (t == netdev_priv(itn->fb_tunnel_dev)) 99362306a36Sopenharmony_ci goto done; 99462306a36Sopenharmony_ci dev = t->dev; 99562306a36Sopenharmony_ci } 99662306a36Sopenharmony_ci unregister_netdevice(dev); 99762306a36Sopenharmony_ci err = 0; 99862306a36Sopenharmony_ci break; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci default: 100162306a36Sopenharmony_ci err = -EINVAL; 100262306a36Sopenharmony_ci } 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_cidone: 100562306a36Sopenharmony_ci return err; 100662306a36Sopenharmony_ci} 100762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip_tunnel_ctl); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ciint ip_tunnel_siocdevprivate(struct net_device *dev, struct ifreq *ifr, 101062306a36Sopenharmony_ci void __user *data, int cmd) 101162306a36Sopenharmony_ci{ 101262306a36Sopenharmony_ci struct ip_tunnel_parm p; 101362306a36Sopenharmony_ci int err; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci if (copy_from_user(&p, data, sizeof(p))) 101662306a36Sopenharmony_ci return -EFAULT; 101762306a36Sopenharmony_ci err = dev->netdev_ops->ndo_tunnel_ctl(dev, &p, cmd); 101862306a36Sopenharmony_ci if (!err && copy_to_user(data, &p, sizeof(p))) 101962306a36Sopenharmony_ci return -EFAULT; 102062306a36Sopenharmony_ci return err; 102162306a36Sopenharmony_ci} 102262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip_tunnel_siocdevprivate); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ciint __ip_tunnel_change_mtu(struct net_device *dev, int new_mtu, bool strict) 102562306a36Sopenharmony_ci{ 102662306a36Sopenharmony_ci struct ip_tunnel *tunnel = netdev_priv(dev); 102762306a36Sopenharmony_ci int t_hlen = tunnel->hlen + sizeof(struct iphdr); 102862306a36Sopenharmony_ci int max_mtu = IP_MAX_MTU - t_hlen; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci if (dev->type == ARPHRD_ETHER) 103162306a36Sopenharmony_ci max_mtu -= dev->hard_header_len; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci if (new_mtu < ETH_MIN_MTU) 103462306a36Sopenharmony_ci return -EINVAL; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci if (new_mtu > max_mtu) { 103762306a36Sopenharmony_ci if (strict) 103862306a36Sopenharmony_ci return -EINVAL; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci new_mtu = max_mtu; 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci dev->mtu = new_mtu; 104462306a36Sopenharmony_ci return 0; 104562306a36Sopenharmony_ci} 104662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__ip_tunnel_change_mtu); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ciint ip_tunnel_change_mtu(struct net_device *dev, int new_mtu) 104962306a36Sopenharmony_ci{ 105062306a36Sopenharmony_ci return __ip_tunnel_change_mtu(dev, new_mtu, true); 105162306a36Sopenharmony_ci} 105262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip_tunnel_change_mtu); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_cistatic void ip_tunnel_dev_free(struct net_device *dev) 105562306a36Sopenharmony_ci{ 105662306a36Sopenharmony_ci struct ip_tunnel *tunnel = netdev_priv(dev); 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci gro_cells_destroy(&tunnel->gro_cells); 105962306a36Sopenharmony_ci dst_cache_destroy(&tunnel->dst_cache); 106062306a36Sopenharmony_ci free_percpu(dev->tstats); 106162306a36Sopenharmony_ci} 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_civoid ip_tunnel_dellink(struct net_device *dev, struct list_head *head) 106462306a36Sopenharmony_ci{ 106562306a36Sopenharmony_ci struct ip_tunnel *tunnel = netdev_priv(dev); 106662306a36Sopenharmony_ci struct ip_tunnel_net *itn; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci itn = net_generic(tunnel->net, tunnel->ip_tnl_net_id); 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci if (itn->fb_tunnel_dev != dev) { 107162306a36Sopenharmony_ci ip_tunnel_del(itn, netdev_priv(dev)); 107262306a36Sopenharmony_ci unregister_netdevice_queue(dev, head); 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci} 107562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip_tunnel_dellink); 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_cistruct net *ip_tunnel_get_link_net(const struct net_device *dev) 107862306a36Sopenharmony_ci{ 107962306a36Sopenharmony_ci struct ip_tunnel *tunnel = netdev_priv(dev); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci return tunnel->net; 108262306a36Sopenharmony_ci} 108362306a36Sopenharmony_ciEXPORT_SYMBOL(ip_tunnel_get_link_net); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ciint ip_tunnel_get_iflink(const struct net_device *dev) 108662306a36Sopenharmony_ci{ 108762306a36Sopenharmony_ci struct ip_tunnel *tunnel = netdev_priv(dev); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci return tunnel->parms.link; 109062306a36Sopenharmony_ci} 109162306a36Sopenharmony_ciEXPORT_SYMBOL(ip_tunnel_get_iflink); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ciint ip_tunnel_init_net(struct net *net, unsigned int ip_tnl_net_id, 109462306a36Sopenharmony_ci struct rtnl_link_ops *ops, char *devname) 109562306a36Sopenharmony_ci{ 109662306a36Sopenharmony_ci struct ip_tunnel_net *itn = net_generic(net, ip_tnl_net_id); 109762306a36Sopenharmony_ci struct ip_tunnel_parm parms; 109862306a36Sopenharmony_ci unsigned int i; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci itn->rtnl_link_ops = ops; 110162306a36Sopenharmony_ci for (i = 0; i < IP_TNL_HASH_SIZE; i++) 110262306a36Sopenharmony_ci INIT_HLIST_HEAD(&itn->tunnels[i]); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci if (!ops || !net_has_fallback_tunnels(net)) { 110562306a36Sopenharmony_ci struct ip_tunnel_net *it_init_net; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci it_init_net = net_generic(&init_net, ip_tnl_net_id); 110862306a36Sopenharmony_ci itn->type = it_init_net->type; 110962306a36Sopenharmony_ci itn->fb_tunnel_dev = NULL; 111062306a36Sopenharmony_ci return 0; 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci memset(&parms, 0, sizeof(parms)); 111462306a36Sopenharmony_ci if (devname) 111562306a36Sopenharmony_ci strscpy(parms.name, devname, IFNAMSIZ); 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci rtnl_lock(); 111862306a36Sopenharmony_ci itn->fb_tunnel_dev = __ip_tunnel_create(net, ops, &parms); 111962306a36Sopenharmony_ci /* FB netdevice is special: we have one, and only one per netns. 112062306a36Sopenharmony_ci * Allowing to move it to another netns is clearly unsafe. 112162306a36Sopenharmony_ci */ 112262306a36Sopenharmony_ci if (!IS_ERR(itn->fb_tunnel_dev)) { 112362306a36Sopenharmony_ci itn->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL; 112462306a36Sopenharmony_ci itn->fb_tunnel_dev->mtu = ip_tunnel_bind_dev(itn->fb_tunnel_dev); 112562306a36Sopenharmony_ci ip_tunnel_add(itn, netdev_priv(itn->fb_tunnel_dev)); 112662306a36Sopenharmony_ci itn->type = itn->fb_tunnel_dev->type; 112762306a36Sopenharmony_ci } 112862306a36Sopenharmony_ci rtnl_unlock(); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(itn->fb_tunnel_dev); 113162306a36Sopenharmony_ci} 113262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip_tunnel_init_net); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_cistatic void ip_tunnel_destroy(struct net *net, struct ip_tunnel_net *itn, 113562306a36Sopenharmony_ci struct list_head *head, 113662306a36Sopenharmony_ci struct rtnl_link_ops *ops) 113762306a36Sopenharmony_ci{ 113862306a36Sopenharmony_ci struct net_device *dev, *aux; 113962306a36Sopenharmony_ci int h; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci for_each_netdev_safe(net, dev, aux) 114262306a36Sopenharmony_ci if (dev->rtnl_link_ops == ops) 114362306a36Sopenharmony_ci unregister_netdevice_queue(dev, head); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci for (h = 0; h < IP_TNL_HASH_SIZE; h++) { 114662306a36Sopenharmony_ci struct ip_tunnel *t; 114762306a36Sopenharmony_ci struct hlist_node *n; 114862306a36Sopenharmony_ci struct hlist_head *thead = &itn->tunnels[h]; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci hlist_for_each_entry_safe(t, n, thead, hash_node) 115162306a36Sopenharmony_ci /* If dev is in the same netns, it has already 115262306a36Sopenharmony_ci * been added to the list by the previous loop. 115362306a36Sopenharmony_ci */ 115462306a36Sopenharmony_ci if (!net_eq(dev_net(t->dev), net)) 115562306a36Sopenharmony_ci unregister_netdevice_queue(t->dev, head); 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_civoid ip_tunnel_delete_nets(struct list_head *net_list, unsigned int id, 116062306a36Sopenharmony_ci struct rtnl_link_ops *ops) 116162306a36Sopenharmony_ci{ 116262306a36Sopenharmony_ci struct ip_tunnel_net *itn; 116362306a36Sopenharmony_ci struct net *net; 116462306a36Sopenharmony_ci LIST_HEAD(list); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci rtnl_lock(); 116762306a36Sopenharmony_ci list_for_each_entry(net, net_list, exit_list) { 116862306a36Sopenharmony_ci itn = net_generic(net, id); 116962306a36Sopenharmony_ci ip_tunnel_destroy(net, itn, &list, ops); 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci unregister_netdevice_many(&list); 117262306a36Sopenharmony_ci rtnl_unlock(); 117362306a36Sopenharmony_ci} 117462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip_tunnel_delete_nets); 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ciint ip_tunnel_newlink(struct net_device *dev, struct nlattr *tb[], 117762306a36Sopenharmony_ci struct ip_tunnel_parm *p, __u32 fwmark) 117862306a36Sopenharmony_ci{ 117962306a36Sopenharmony_ci struct ip_tunnel *nt; 118062306a36Sopenharmony_ci struct net *net = dev_net(dev); 118162306a36Sopenharmony_ci struct ip_tunnel_net *itn; 118262306a36Sopenharmony_ci int mtu; 118362306a36Sopenharmony_ci int err; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci nt = netdev_priv(dev); 118662306a36Sopenharmony_ci itn = net_generic(net, nt->ip_tnl_net_id); 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci if (nt->collect_md) { 118962306a36Sopenharmony_ci if (rtnl_dereference(itn->collect_md_tun)) 119062306a36Sopenharmony_ci return -EEXIST; 119162306a36Sopenharmony_ci } else { 119262306a36Sopenharmony_ci if (ip_tunnel_find(itn, p, dev->type)) 119362306a36Sopenharmony_ci return -EEXIST; 119462306a36Sopenharmony_ci } 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci nt->net = net; 119762306a36Sopenharmony_ci nt->parms = *p; 119862306a36Sopenharmony_ci nt->fwmark = fwmark; 119962306a36Sopenharmony_ci err = register_netdevice(dev); 120062306a36Sopenharmony_ci if (err) 120162306a36Sopenharmony_ci goto err_register_netdevice; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci if (dev->type == ARPHRD_ETHER && !tb[IFLA_ADDRESS]) 120462306a36Sopenharmony_ci eth_hw_addr_random(dev); 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci mtu = ip_tunnel_bind_dev(dev); 120762306a36Sopenharmony_ci if (tb[IFLA_MTU]) { 120862306a36Sopenharmony_ci unsigned int max = IP_MAX_MTU - (nt->hlen + sizeof(struct iphdr)); 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci if (dev->type == ARPHRD_ETHER) 121162306a36Sopenharmony_ci max -= dev->hard_header_len; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci mtu = clamp(dev->mtu, (unsigned int)ETH_MIN_MTU, max); 121462306a36Sopenharmony_ci } 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci err = dev_set_mtu(dev, mtu); 121762306a36Sopenharmony_ci if (err) 121862306a36Sopenharmony_ci goto err_dev_set_mtu; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci ip_tunnel_add(itn, nt); 122162306a36Sopenharmony_ci return 0; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_cierr_dev_set_mtu: 122462306a36Sopenharmony_ci unregister_netdevice(dev); 122562306a36Sopenharmony_cierr_register_netdevice: 122662306a36Sopenharmony_ci return err; 122762306a36Sopenharmony_ci} 122862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip_tunnel_newlink); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ciint ip_tunnel_changelink(struct net_device *dev, struct nlattr *tb[], 123162306a36Sopenharmony_ci struct ip_tunnel_parm *p, __u32 fwmark) 123262306a36Sopenharmony_ci{ 123362306a36Sopenharmony_ci struct ip_tunnel *t; 123462306a36Sopenharmony_ci struct ip_tunnel *tunnel = netdev_priv(dev); 123562306a36Sopenharmony_ci struct net *net = tunnel->net; 123662306a36Sopenharmony_ci struct ip_tunnel_net *itn = net_generic(net, tunnel->ip_tnl_net_id); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci if (dev == itn->fb_tunnel_dev) 123962306a36Sopenharmony_ci return -EINVAL; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci t = ip_tunnel_find(itn, p, dev->type); 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci if (t) { 124462306a36Sopenharmony_ci if (t->dev != dev) 124562306a36Sopenharmony_ci return -EEXIST; 124662306a36Sopenharmony_ci } else { 124762306a36Sopenharmony_ci t = tunnel; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci if (dev->type != ARPHRD_ETHER) { 125062306a36Sopenharmony_ci unsigned int nflags = 0; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci if (ipv4_is_multicast(p->iph.daddr)) 125362306a36Sopenharmony_ci nflags = IFF_BROADCAST; 125462306a36Sopenharmony_ci else if (p->iph.daddr) 125562306a36Sopenharmony_ci nflags = IFF_POINTOPOINT; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci if ((dev->flags ^ nflags) & 125862306a36Sopenharmony_ci (IFF_POINTOPOINT | IFF_BROADCAST)) 125962306a36Sopenharmony_ci return -EINVAL; 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci } 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci ip_tunnel_update(itn, t, dev, p, !tb[IFLA_MTU], fwmark); 126462306a36Sopenharmony_ci return 0; 126562306a36Sopenharmony_ci} 126662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip_tunnel_changelink); 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ciint ip_tunnel_init(struct net_device *dev) 126962306a36Sopenharmony_ci{ 127062306a36Sopenharmony_ci struct ip_tunnel *tunnel = netdev_priv(dev); 127162306a36Sopenharmony_ci struct iphdr *iph = &tunnel->parms.iph; 127262306a36Sopenharmony_ci int err; 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci dev->needs_free_netdev = true; 127562306a36Sopenharmony_ci dev->priv_destructor = ip_tunnel_dev_free; 127662306a36Sopenharmony_ci dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); 127762306a36Sopenharmony_ci if (!dev->tstats) 127862306a36Sopenharmony_ci return -ENOMEM; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci err = dst_cache_init(&tunnel->dst_cache, GFP_KERNEL); 128162306a36Sopenharmony_ci if (err) { 128262306a36Sopenharmony_ci free_percpu(dev->tstats); 128362306a36Sopenharmony_ci return err; 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci err = gro_cells_init(&tunnel->gro_cells, dev); 128762306a36Sopenharmony_ci if (err) { 128862306a36Sopenharmony_ci dst_cache_destroy(&tunnel->dst_cache); 128962306a36Sopenharmony_ci free_percpu(dev->tstats); 129062306a36Sopenharmony_ci return err; 129162306a36Sopenharmony_ci } 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci tunnel->dev = dev; 129462306a36Sopenharmony_ci tunnel->net = dev_net(dev); 129562306a36Sopenharmony_ci strcpy(tunnel->parms.name, dev->name); 129662306a36Sopenharmony_ci iph->version = 4; 129762306a36Sopenharmony_ci iph->ihl = 5; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci if (tunnel->collect_md) 130062306a36Sopenharmony_ci netif_keep_dst(dev); 130162306a36Sopenharmony_ci return 0; 130262306a36Sopenharmony_ci} 130362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip_tunnel_init); 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_civoid ip_tunnel_uninit(struct net_device *dev) 130662306a36Sopenharmony_ci{ 130762306a36Sopenharmony_ci struct ip_tunnel *tunnel = netdev_priv(dev); 130862306a36Sopenharmony_ci struct net *net = tunnel->net; 130962306a36Sopenharmony_ci struct ip_tunnel_net *itn; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci itn = net_generic(net, tunnel->ip_tnl_net_id); 131262306a36Sopenharmony_ci ip_tunnel_del(itn, netdev_priv(dev)); 131362306a36Sopenharmony_ci if (itn->fb_tunnel_dev == dev) 131462306a36Sopenharmony_ci WRITE_ONCE(itn->fb_tunnel_dev, NULL); 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci dst_cache_reset(&tunnel->dst_cache); 131762306a36Sopenharmony_ci} 131862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip_tunnel_uninit); 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci/* Do least required initialization, rest of init is done in tunnel_init call */ 132162306a36Sopenharmony_civoid ip_tunnel_setup(struct net_device *dev, unsigned int net_id) 132262306a36Sopenharmony_ci{ 132362306a36Sopenharmony_ci struct ip_tunnel *tunnel = netdev_priv(dev); 132462306a36Sopenharmony_ci tunnel->ip_tnl_net_id = net_id; 132562306a36Sopenharmony_ci} 132662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip_tunnel_setup); 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1329