18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * GRE over IPv6 protocol decoder. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: Dmitry Kozlov (xeb@mail.ru) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/capability.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/types.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 168c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 178c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 188c2ecf20Sopenharmony_ci#include <linux/in.h> 198c2ecf20Sopenharmony_ci#include <linux/tcp.h> 208c2ecf20Sopenharmony_ci#include <linux/udp.h> 218c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 228c2ecf20Sopenharmony_ci#include <linux/init.h> 238c2ecf20Sopenharmony_ci#include <linux/in6.h> 248c2ecf20Sopenharmony_ci#include <linux/inetdevice.h> 258c2ecf20Sopenharmony_ci#include <linux/igmp.h> 268c2ecf20Sopenharmony_ci#include <linux/netfilter_ipv4.h> 278c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 288c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 298c2ecf20Sopenharmony_ci#include <linux/hash.h> 308c2ecf20Sopenharmony_ci#include <linux/if_tunnel.h> 318c2ecf20Sopenharmony_ci#include <linux/ip6_tunnel.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <net/sock.h> 348c2ecf20Sopenharmony_ci#include <net/ip.h> 358c2ecf20Sopenharmony_ci#include <net/ip_tunnels.h> 368c2ecf20Sopenharmony_ci#include <net/icmp.h> 378c2ecf20Sopenharmony_ci#include <net/protocol.h> 388c2ecf20Sopenharmony_ci#include <net/addrconf.h> 398c2ecf20Sopenharmony_ci#include <net/arp.h> 408c2ecf20Sopenharmony_ci#include <net/checksum.h> 418c2ecf20Sopenharmony_ci#include <net/dsfield.h> 428c2ecf20Sopenharmony_ci#include <net/inet_ecn.h> 438c2ecf20Sopenharmony_ci#include <net/xfrm.h> 448c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 458c2ecf20Sopenharmony_ci#include <net/netns/generic.h> 468c2ecf20Sopenharmony_ci#include <net/rtnetlink.h> 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include <net/ipv6.h> 498c2ecf20Sopenharmony_ci#include <net/ip6_fib.h> 508c2ecf20Sopenharmony_ci#include <net/ip6_route.h> 518c2ecf20Sopenharmony_ci#include <net/ip6_tunnel.h> 528c2ecf20Sopenharmony_ci#include <net/gre.h> 538c2ecf20Sopenharmony_ci#include <net/erspan.h> 548c2ecf20Sopenharmony_ci#include <net/dst_metadata.h> 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic bool log_ecn_error = true; 588c2ecf20Sopenharmony_cimodule_param(log_ecn_error, bool, 0644); 598c2ecf20Sopenharmony_ciMODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define IP6_GRE_HASH_SIZE_SHIFT 5 628c2ecf20Sopenharmony_ci#define IP6_GRE_HASH_SIZE (1 << IP6_GRE_HASH_SIZE_SHIFT) 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic unsigned int ip6gre_net_id __read_mostly; 658c2ecf20Sopenharmony_cistruct ip6gre_net { 668c2ecf20Sopenharmony_ci struct ip6_tnl __rcu *tunnels[4][IP6_GRE_HASH_SIZE]; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci struct ip6_tnl __rcu *collect_md_tun; 698c2ecf20Sopenharmony_ci struct ip6_tnl __rcu *collect_md_tun_erspan; 708c2ecf20Sopenharmony_ci struct net_device *fb_tunnel_dev; 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic struct rtnl_link_ops ip6gre_link_ops __read_mostly; 748c2ecf20Sopenharmony_cistatic struct rtnl_link_ops ip6gre_tap_ops __read_mostly; 758c2ecf20Sopenharmony_cistatic struct rtnl_link_ops ip6erspan_tap_ops __read_mostly; 768c2ecf20Sopenharmony_cistatic int ip6gre_tunnel_init(struct net_device *dev); 778c2ecf20Sopenharmony_cistatic void ip6gre_tunnel_setup(struct net_device *dev); 788c2ecf20Sopenharmony_cistatic void ip6gre_tunnel_link(struct ip6gre_net *ign, struct ip6_tnl *t); 798c2ecf20Sopenharmony_cistatic void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu); 808c2ecf20Sopenharmony_cistatic void ip6erspan_tnl_link_config(struct ip6_tnl *t, int set_mtu); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* Tunnel hash table */ 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* 858c2ecf20Sopenharmony_ci 4 hash tables: 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci 3: (remote,local) 888c2ecf20Sopenharmony_ci 2: (remote,*) 898c2ecf20Sopenharmony_ci 1: (*,local) 908c2ecf20Sopenharmony_ci 0: (*,*) 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci We require exact key match i.e. if a key is present in packet 938c2ecf20Sopenharmony_ci it will match only tunnel with the same key; if it is not present, 948c2ecf20Sopenharmony_ci it will match only keyless tunnel. 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci All keysless packets, if not matched configured keyless tunnels 978c2ecf20Sopenharmony_ci will match fallback tunnel. 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define HASH_KEY(key) (((__force u32)key^((__force u32)key>>4))&(IP6_GRE_HASH_SIZE - 1)) 1018c2ecf20Sopenharmony_cistatic u32 HASH_ADDR(const struct in6_addr *addr) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci u32 hash = ipv6_addr_hash(addr); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return hash_32(hash, IP6_GRE_HASH_SIZE_SHIFT); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci#define tunnels_r_l tunnels[3] 1098c2ecf20Sopenharmony_ci#define tunnels_r tunnels[2] 1108c2ecf20Sopenharmony_ci#define tunnels_l tunnels[1] 1118c2ecf20Sopenharmony_ci#define tunnels_wc tunnels[0] 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* Given src, dst and key, find appropriate for input tunnel. */ 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic struct ip6_tnl *ip6gre_tunnel_lookup(struct net_device *dev, 1168c2ecf20Sopenharmony_ci const struct in6_addr *remote, const struct in6_addr *local, 1178c2ecf20Sopenharmony_ci __be32 key, __be16 gre_proto) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 1208c2ecf20Sopenharmony_ci int link = dev->ifindex; 1218c2ecf20Sopenharmony_ci unsigned int h0 = HASH_ADDR(remote); 1228c2ecf20Sopenharmony_ci unsigned int h1 = HASH_KEY(key); 1238c2ecf20Sopenharmony_ci struct ip6_tnl *t, *cand = NULL; 1248c2ecf20Sopenharmony_ci struct ip6gre_net *ign = net_generic(net, ip6gre_net_id); 1258c2ecf20Sopenharmony_ci int dev_type = (gre_proto == htons(ETH_P_TEB) || 1268c2ecf20Sopenharmony_ci gre_proto == htons(ETH_P_ERSPAN) || 1278c2ecf20Sopenharmony_ci gre_proto == htons(ETH_P_ERSPAN2)) ? 1288c2ecf20Sopenharmony_ci ARPHRD_ETHER : ARPHRD_IP6GRE; 1298c2ecf20Sopenharmony_ci int score, cand_score = 4; 1308c2ecf20Sopenharmony_ci struct net_device *ndev; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci for_each_ip_tunnel_rcu(t, ign->tunnels_r_l[h0 ^ h1]) { 1338c2ecf20Sopenharmony_ci if (!ipv6_addr_equal(local, &t->parms.laddr) || 1348c2ecf20Sopenharmony_ci !ipv6_addr_equal(remote, &t->parms.raddr) || 1358c2ecf20Sopenharmony_ci key != t->parms.i_key || 1368c2ecf20Sopenharmony_ci !(t->dev->flags & IFF_UP)) 1378c2ecf20Sopenharmony_ci continue; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (t->dev->type != ARPHRD_IP6GRE && 1408c2ecf20Sopenharmony_ci t->dev->type != dev_type) 1418c2ecf20Sopenharmony_ci continue; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci score = 0; 1448c2ecf20Sopenharmony_ci if (t->parms.link != link) 1458c2ecf20Sopenharmony_ci score |= 1; 1468c2ecf20Sopenharmony_ci if (t->dev->type != dev_type) 1478c2ecf20Sopenharmony_ci score |= 2; 1488c2ecf20Sopenharmony_ci if (score == 0) 1498c2ecf20Sopenharmony_ci return t; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (score < cand_score) { 1528c2ecf20Sopenharmony_ci cand = t; 1538c2ecf20Sopenharmony_ci cand_score = score; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci for_each_ip_tunnel_rcu(t, ign->tunnels_r[h0 ^ h1]) { 1588c2ecf20Sopenharmony_ci if (!ipv6_addr_equal(remote, &t->parms.raddr) || 1598c2ecf20Sopenharmony_ci key != t->parms.i_key || 1608c2ecf20Sopenharmony_ci !(t->dev->flags & IFF_UP)) 1618c2ecf20Sopenharmony_ci continue; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (t->dev->type != ARPHRD_IP6GRE && 1648c2ecf20Sopenharmony_ci t->dev->type != dev_type) 1658c2ecf20Sopenharmony_ci continue; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci score = 0; 1688c2ecf20Sopenharmony_ci if (t->parms.link != link) 1698c2ecf20Sopenharmony_ci score |= 1; 1708c2ecf20Sopenharmony_ci if (t->dev->type != dev_type) 1718c2ecf20Sopenharmony_ci score |= 2; 1728c2ecf20Sopenharmony_ci if (score == 0) 1738c2ecf20Sopenharmony_ci return t; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (score < cand_score) { 1768c2ecf20Sopenharmony_ci cand = t; 1778c2ecf20Sopenharmony_ci cand_score = score; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci for_each_ip_tunnel_rcu(t, ign->tunnels_l[h1]) { 1828c2ecf20Sopenharmony_ci if ((!ipv6_addr_equal(local, &t->parms.laddr) && 1838c2ecf20Sopenharmony_ci (!ipv6_addr_equal(local, &t->parms.raddr) || 1848c2ecf20Sopenharmony_ci !ipv6_addr_is_multicast(local))) || 1858c2ecf20Sopenharmony_ci key != t->parms.i_key || 1868c2ecf20Sopenharmony_ci !(t->dev->flags & IFF_UP)) 1878c2ecf20Sopenharmony_ci continue; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (t->dev->type != ARPHRD_IP6GRE && 1908c2ecf20Sopenharmony_ci t->dev->type != dev_type) 1918c2ecf20Sopenharmony_ci continue; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci score = 0; 1948c2ecf20Sopenharmony_ci if (t->parms.link != link) 1958c2ecf20Sopenharmony_ci score |= 1; 1968c2ecf20Sopenharmony_ci if (t->dev->type != dev_type) 1978c2ecf20Sopenharmony_ci score |= 2; 1988c2ecf20Sopenharmony_ci if (score == 0) 1998c2ecf20Sopenharmony_ci return t; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (score < cand_score) { 2028c2ecf20Sopenharmony_ci cand = t; 2038c2ecf20Sopenharmony_ci cand_score = score; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci for_each_ip_tunnel_rcu(t, ign->tunnels_wc[h1]) { 2088c2ecf20Sopenharmony_ci if (t->parms.i_key != key || 2098c2ecf20Sopenharmony_ci !(t->dev->flags & IFF_UP)) 2108c2ecf20Sopenharmony_ci continue; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (t->dev->type != ARPHRD_IP6GRE && 2138c2ecf20Sopenharmony_ci t->dev->type != dev_type) 2148c2ecf20Sopenharmony_ci continue; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci score = 0; 2178c2ecf20Sopenharmony_ci if (t->parms.link != link) 2188c2ecf20Sopenharmony_ci score |= 1; 2198c2ecf20Sopenharmony_ci if (t->dev->type != dev_type) 2208c2ecf20Sopenharmony_ci score |= 2; 2218c2ecf20Sopenharmony_ci if (score == 0) 2228c2ecf20Sopenharmony_ci return t; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (score < cand_score) { 2258c2ecf20Sopenharmony_ci cand = t; 2268c2ecf20Sopenharmony_ci cand_score = score; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (cand) 2318c2ecf20Sopenharmony_ci return cand; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (gre_proto == htons(ETH_P_ERSPAN) || 2348c2ecf20Sopenharmony_ci gre_proto == htons(ETH_P_ERSPAN2)) 2358c2ecf20Sopenharmony_ci t = rcu_dereference(ign->collect_md_tun_erspan); 2368c2ecf20Sopenharmony_ci else 2378c2ecf20Sopenharmony_ci t = rcu_dereference(ign->collect_md_tun); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (t && t->dev->flags & IFF_UP) 2408c2ecf20Sopenharmony_ci return t; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci ndev = READ_ONCE(ign->fb_tunnel_dev); 2438c2ecf20Sopenharmony_ci if (ndev && ndev->flags & IFF_UP) 2448c2ecf20Sopenharmony_ci return netdev_priv(ndev); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci return NULL; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic struct ip6_tnl __rcu **__ip6gre_bucket(struct ip6gre_net *ign, 2508c2ecf20Sopenharmony_ci const struct __ip6_tnl_parm *p) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci const struct in6_addr *remote = &p->raddr; 2538c2ecf20Sopenharmony_ci const struct in6_addr *local = &p->laddr; 2548c2ecf20Sopenharmony_ci unsigned int h = HASH_KEY(p->i_key); 2558c2ecf20Sopenharmony_ci int prio = 0; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (!ipv6_addr_any(local)) 2588c2ecf20Sopenharmony_ci prio |= 1; 2598c2ecf20Sopenharmony_ci if (!ipv6_addr_any(remote) && !ipv6_addr_is_multicast(remote)) { 2608c2ecf20Sopenharmony_ci prio |= 2; 2618c2ecf20Sopenharmony_ci h ^= HASH_ADDR(remote); 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return &ign->tunnels[prio][h]; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic void ip6gre_tunnel_link_md(struct ip6gre_net *ign, struct ip6_tnl *t) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci if (t->parms.collect_md) 2708c2ecf20Sopenharmony_ci rcu_assign_pointer(ign->collect_md_tun, t); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic void ip6erspan_tunnel_link_md(struct ip6gre_net *ign, struct ip6_tnl *t) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci if (t->parms.collect_md) 2768c2ecf20Sopenharmony_ci rcu_assign_pointer(ign->collect_md_tun_erspan, t); 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic void ip6gre_tunnel_unlink_md(struct ip6gre_net *ign, struct ip6_tnl *t) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci if (t->parms.collect_md) 2828c2ecf20Sopenharmony_ci rcu_assign_pointer(ign->collect_md_tun, NULL); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic void ip6erspan_tunnel_unlink_md(struct ip6gre_net *ign, 2868c2ecf20Sopenharmony_ci struct ip6_tnl *t) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci if (t->parms.collect_md) 2898c2ecf20Sopenharmony_ci rcu_assign_pointer(ign->collect_md_tun_erspan, NULL); 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic inline struct ip6_tnl __rcu **ip6gre_bucket(struct ip6gre_net *ign, 2938c2ecf20Sopenharmony_ci const struct ip6_tnl *t) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci return __ip6gre_bucket(ign, &t->parms); 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic void ip6gre_tunnel_link(struct ip6gre_net *ign, struct ip6_tnl *t) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct ip6_tnl __rcu **tp = ip6gre_bucket(ign, t); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci rcu_assign_pointer(t->next, rtnl_dereference(*tp)); 3038c2ecf20Sopenharmony_ci rcu_assign_pointer(*tp, t); 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic void ip6gre_tunnel_unlink(struct ip6gre_net *ign, struct ip6_tnl *t) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct ip6_tnl __rcu **tp; 3098c2ecf20Sopenharmony_ci struct ip6_tnl *iter; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci for (tp = ip6gre_bucket(ign, t); 3128c2ecf20Sopenharmony_ci (iter = rtnl_dereference(*tp)) != NULL; 3138c2ecf20Sopenharmony_ci tp = &iter->next) { 3148c2ecf20Sopenharmony_ci if (t == iter) { 3158c2ecf20Sopenharmony_ci rcu_assign_pointer(*tp, t->next); 3168c2ecf20Sopenharmony_ci break; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic struct ip6_tnl *ip6gre_tunnel_find(struct net *net, 3228c2ecf20Sopenharmony_ci const struct __ip6_tnl_parm *parms, 3238c2ecf20Sopenharmony_ci int type) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci const struct in6_addr *remote = &parms->raddr; 3268c2ecf20Sopenharmony_ci const struct in6_addr *local = &parms->laddr; 3278c2ecf20Sopenharmony_ci __be32 key = parms->i_key; 3288c2ecf20Sopenharmony_ci int link = parms->link; 3298c2ecf20Sopenharmony_ci struct ip6_tnl *t; 3308c2ecf20Sopenharmony_ci struct ip6_tnl __rcu **tp; 3318c2ecf20Sopenharmony_ci struct ip6gre_net *ign = net_generic(net, ip6gre_net_id); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci for (tp = __ip6gre_bucket(ign, parms); 3348c2ecf20Sopenharmony_ci (t = rtnl_dereference(*tp)) != NULL; 3358c2ecf20Sopenharmony_ci tp = &t->next) 3368c2ecf20Sopenharmony_ci if (ipv6_addr_equal(local, &t->parms.laddr) && 3378c2ecf20Sopenharmony_ci ipv6_addr_equal(remote, &t->parms.raddr) && 3388c2ecf20Sopenharmony_ci key == t->parms.i_key && 3398c2ecf20Sopenharmony_ci link == t->parms.link && 3408c2ecf20Sopenharmony_ci type == t->dev->type) 3418c2ecf20Sopenharmony_ci break; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci return t; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic struct ip6_tnl *ip6gre_tunnel_locate(struct net *net, 3478c2ecf20Sopenharmony_ci const struct __ip6_tnl_parm *parms, int create) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci struct ip6_tnl *t, *nt; 3508c2ecf20Sopenharmony_ci struct net_device *dev; 3518c2ecf20Sopenharmony_ci char name[IFNAMSIZ]; 3528c2ecf20Sopenharmony_ci struct ip6gre_net *ign = net_generic(net, ip6gre_net_id); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci t = ip6gre_tunnel_find(net, parms, ARPHRD_IP6GRE); 3558c2ecf20Sopenharmony_ci if (t && create) 3568c2ecf20Sopenharmony_ci return NULL; 3578c2ecf20Sopenharmony_ci if (t || !create) 3588c2ecf20Sopenharmony_ci return t; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (parms->name[0]) { 3618c2ecf20Sopenharmony_ci if (!dev_valid_name(parms->name)) 3628c2ecf20Sopenharmony_ci return NULL; 3638c2ecf20Sopenharmony_ci strlcpy(name, parms->name, IFNAMSIZ); 3648c2ecf20Sopenharmony_ci } else { 3658c2ecf20Sopenharmony_ci strcpy(name, "ip6gre%d"); 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci dev = alloc_netdev(sizeof(*t), name, NET_NAME_UNKNOWN, 3688c2ecf20Sopenharmony_ci ip6gre_tunnel_setup); 3698c2ecf20Sopenharmony_ci if (!dev) 3708c2ecf20Sopenharmony_ci return NULL; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci dev_net_set(dev, net); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci nt = netdev_priv(dev); 3758c2ecf20Sopenharmony_ci nt->parms = *parms; 3768c2ecf20Sopenharmony_ci dev->rtnl_link_ops = &ip6gre_link_ops; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci nt->dev = dev; 3798c2ecf20Sopenharmony_ci nt->net = dev_net(dev); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (register_netdevice(dev) < 0) 3828c2ecf20Sopenharmony_ci goto failed_free; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci ip6gre_tnl_link_config(nt, 1); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* Can use a lockless transmit, unless we generate output sequences */ 3878c2ecf20Sopenharmony_ci if (!(nt->parms.o_flags & TUNNEL_SEQ)) 3888c2ecf20Sopenharmony_ci dev->features |= NETIF_F_LLTX; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci ip6gre_tunnel_link(ign, nt); 3918c2ecf20Sopenharmony_ci return nt; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cifailed_free: 3948c2ecf20Sopenharmony_ci free_netdev(dev); 3958c2ecf20Sopenharmony_ci return NULL; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic void ip6erspan_tunnel_uninit(struct net_device *dev) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci struct ip6_tnl *t = netdev_priv(dev); 4018c2ecf20Sopenharmony_ci struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci ip6erspan_tunnel_unlink_md(ign, t); 4048c2ecf20Sopenharmony_ci ip6gre_tunnel_unlink(ign, t); 4058c2ecf20Sopenharmony_ci dst_cache_reset(&t->dst_cache); 4068c2ecf20Sopenharmony_ci dev_put(dev); 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic void ip6gre_tunnel_uninit(struct net_device *dev) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci struct ip6_tnl *t = netdev_priv(dev); 4128c2ecf20Sopenharmony_ci struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci ip6gre_tunnel_unlink_md(ign, t); 4158c2ecf20Sopenharmony_ci ip6gre_tunnel_unlink(ign, t); 4168c2ecf20Sopenharmony_ci if (ign->fb_tunnel_dev == dev) 4178c2ecf20Sopenharmony_ci WRITE_ONCE(ign->fb_tunnel_dev, NULL); 4188c2ecf20Sopenharmony_ci dst_cache_reset(&t->dst_cache); 4198c2ecf20Sopenharmony_ci dev_put(dev); 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic int ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 4248c2ecf20Sopenharmony_ci u8 type, u8 code, int offset, __be32 info) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 4278c2ecf20Sopenharmony_ci const struct ipv6hdr *ipv6h; 4288c2ecf20Sopenharmony_ci struct tnl_ptk_info tpi; 4298c2ecf20Sopenharmony_ci struct ip6_tnl *t; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (gre_parse_header(skb, &tpi, NULL, htons(ETH_P_IPV6), 4328c2ecf20Sopenharmony_ci offset) < 0) 4338c2ecf20Sopenharmony_ci return -EINVAL; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci ipv6h = (const struct ipv6hdr *)skb->data; 4368c2ecf20Sopenharmony_ci t = ip6gre_tunnel_lookup(skb->dev, &ipv6h->daddr, &ipv6h->saddr, 4378c2ecf20Sopenharmony_ci tpi.key, tpi.proto); 4388c2ecf20Sopenharmony_ci if (!t) 4398c2ecf20Sopenharmony_ci return -ENOENT; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci switch (type) { 4428c2ecf20Sopenharmony_ci case ICMPV6_DEST_UNREACH: 4438c2ecf20Sopenharmony_ci net_dbg_ratelimited("%s: Path to destination invalid or inactive!\n", 4448c2ecf20Sopenharmony_ci t->parms.name); 4458c2ecf20Sopenharmony_ci if (code != ICMPV6_PORT_UNREACH) 4468c2ecf20Sopenharmony_ci break; 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci case ICMPV6_TIME_EXCEED: 4498c2ecf20Sopenharmony_ci if (code == ICMPV6_EXC_HOPLIMIT) { 4508c2ecf20Sopenharmony_ci net_dbg_ratelimited("%s: Too small hop limit or routing loop in tunnel!\n", 4518c2ecf20Sopenharmony_ci t->parms.name); 4528c2ecf20Sopenharmony_ci break; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci return 0; 4558c2ecf20Sopenharmony_ci case ICMPV6_PARAMPROB: { 4568c2ecf20Sopenharmony_ci struct ipv6_tlv_tnl_enc_lim *tel; 4578c2ecf20Sopenharmony_ci __u32 teli; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci teli = 0; 4608c2ecf20Sopenharmony_ci if (code == ICMPV6_HDR_FIELD) 4618c2ecf20Sopenharmony_ci teli = ip6_tnl_parse_tlv_enc_lim(skb, skb->data); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (teli && teli == be32_to_cpu(info) - 2) { 4648c2ecf20Sopenharmony_ci tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli]; 4658c2ecf20Sopenharmony_ci if (tel->encap_limit == 0) { 4668c2ecf20Sopenharmony_ci net_dbg_ratelimited("%s: Too small encapsulation limit or routing loop in tunnel!\n", 4678c2ecf20Sopenharmony_ci t->parms.name); 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci } else { 4708c2ecf20Sopenharmony_ci net_dbg_ratelimited("%s: Recipient unable to parse tunneled packet!\n", 4718c2ecf20Sopenharmony_ci t->parms.name); 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci return 0; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci case ICMPV6_PKT_TOOBIG: 4768c2ecf20Sopenharmony_ci ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL)); 4778c2ecf20Sopenharmony_ci return 0; 4788c2ecf20Sopenharmony_ci case NDISC_REDIRECT: 4798c2ecf20Sopenharmony_ci ip6_redirect(skb, net, skb->dev->ifindex, 0, 4808c2ecf20Sopenharmony_ci sock_net_uid(net, NULL)); 4818c2ecf20Sopenharmony_ci return 0; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (time_before(jiffies, t->err_time + IP6TUNNEL_ERR_TIMEO)) 4858c2ecf20Sopenharmony_ci t->err_count++; 4868c2ecf20Sopenharmony_ci else 4878c2ecf20Sopenharmony_ci t->err_count = 1; 4888c2ecf20Sopenharmony_ci t->err_time = jiffies; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci return 0; 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic int ip6gre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci const struct ipv6hdr *ipv6h; 4968c2ecf20Sopenharmony_ci struct ip6_tnl *tunnel; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci ipv6h = ipv6_hdr(skb); 4998c2ecf20Sopenharmony_ci tunnel = ip6gre_tunnel_lookup(skb->dev, 5008c2ecf20Sopenharmony_ci &ipv6h->saddr, &ipv6h->daddr, tpi->key, 5018c2ecf20Sopenharmony_ci tpi->proto); 5028c2ecf20Sopenharmony_ci if (tunnel) { 5038c2ecf20Sopenharmony_ci if (tunnel->parms.collect_md) { 5048c2ecf20Sopenharmony_ci struct metadata_dst *tun_dst; 5058c2ecf20Sopenharmony_ci __be64 tun_id; 5068c2ecf20Sopenharmony_ci __be16 flags; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci flags = tpi->flags; 5098c2ecf20Sopenharmony_ci tun_id = key32_to_tunnel_id(tpi->key); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci tun_dst = ipv6_tun_rx_dst(skb, flags, tun_id, 0); 5128c2ecf20Sopenharmony_ci if (!tun_dst) 5138c2ecf20Sopenharmony_ci return PACKET_REJECT; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci ip6_tnl_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error); 5168c2ecf20Sopenharmony_ci } else { 5178c2ecf20Sopenharmony_ci ip6_tnl_rcv(tunnel, skb, tpi, NULL, log_ecn_error); 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci return PACKET_RCVD; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci return PACKET_REJECT; 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic int ip6erspan_rcv(struct sk_buff *skb, 5278c2ecf20Sopenharmony_ci struct tnl_ptk_info *tpi, 5288c2ecf20Sopenharmony_ci int gre_hdr_len) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci struct erspan_base_hdr *ershdr; 5318c2ecf20Sopenharmony_ci const struct ipv6hdr *ipv6h; 5328c2ecf20Sopenharmony_ci struct erspan_md2 *md2; 5338c2ecf20Sopenharmony_ci struct ip6_tnl *tunnel; 5348c2ecf20Sopenharmony_ci u8 ver; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci ipv6h = ipv6_hdr(skb); 5378c2ecf20Sopenharmony_ci ershdr = (struct erspan_base_hdr *)skb->data; 5388c2ecf20Sopenharmony_ci ver = ershdr->ver; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci tunnel = ip6gre_tunnel_lookup(skb->dev, 5418c2ecf20Sopenharmony_ci &ipv6h->saddr, &ipv6h->daddr, tpi->key, 5428c2ecf20Sopenharmony_ci tpi->proto); 5438c2ecf20Sopenharmony_ci if (tunnel) { 5448c2ecf20Sopenharmony_ci int len = erspan_hdr_len(ver); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (unlikely(!pskb_may_pull(skb, len))) 5478c2ecf20Sopenharmony_ci return PACKET_REJECT; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (__iptunnel_pull_header(skb, len, 5508c2ecf20Sopenharmony_ci htons(ETH_P_TEB), 5518c2ecf20Sopenharmony_ci false, false) < 0) 5528c2ecf20Sopenharmony_ci return PACKET_REJECT; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (tunnel->parms.collect_md) { 5558c2ecf20Sopenharmony_ci struct erspan_metadata *pkt_md, *md; 5568c2ecf20Sopenharmony_ci struct metadata_dst *tun_dst; 5578c2ecf20Sopenharmony_ci struct ip_tunnel_info *info; 5588c2ecf20Sopenharmony_ci unsigned char *gh; 5598c2ecf20Sopenharmony_ci __be64 tun_id; 5608c2ecf20Sopenharmony_ci __be16 flags; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci tpi->flags |= TUNNEL_KEY; 5638c2ecf20Sopenharmony_ci flags = tpi->flags; 5648c2ecf20Sopenharmony_ci tun_id = key32_to_tunnel_id(tpi->key); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci tun_dst = ipv6_tun_rx_dst(skb, flags, tun_id, 5678c2ecf20Sopenharmony_ci sizeof(*md)); 5688c2ecf20Sopenharmony_ci if (!tun_dst) 5698c2ecf20Sopenharmony_ci return PACKET_REJECT; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* skb can be uncloned in __iptunnel_pull_header, so 5728c2ecf20Sopenharmony_ci * old pkt_md is no longer valid and we need to reset 5738c2ecf20Sopenharmony_ci * it 5748c2ecf20Sopenharmony_ci */ 5758c2ecf20Sopenharmony_ci gh = skb_network_header(skb) + 5768c2ecf20Sopenharmony_ci skb_network_header_len(skb); 5778c2ecf20Sopenharmony_ci pkt_md = (struct erspan_metadata *)(gh + gre_hdr_len + 5788c2ecf20Sopenharmony_ci sizeof(*ershdr)); 5798c2ecf20Sopenharmony_ci info = &tun_dst->u.tun_info; 5808c2ecf20Sopenharmony_ci md = ip_tunnel_info_opts(info); 5818c2ecf20Sopenharmony_ci md->version = ver; 5828c2ecf20Sopenharmony_ci md2 = &md->u.md2; 5838c2ecf20Sopenharmony_ci memcpy(md2, pkt_md, ver == 1 ? ERSPAN_V1_MDSIZE : 5848c2ecf20Sopenharmony_ci ERSPAN_V2_MDSIZE); 5858c2ecf20Sopenharmony_ci info->key.tun_flags |= TUNNEL_ERSPAN_OPT; 5868c2ecf20Sopenharmony_ci info->options_len = sizeof(*md); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci ip6_tnl_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci } else { 5918c2ecf20Sopenharmony_ci ip6_tnl_rcv(tunnel, skb, tpi, NULL, log_ecn_error); 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci return PACKET_RCVD; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci return PACKET_REJECT; 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_cistatic int gre_rcv(struct sk_buff *skb) 6018c2ecf20Sopenharmony_ci{ 6028c2ecf20Sopenharmony_ci struct tnl_ptk_info tpi; 6038c2ecf20Sopenharmony_ci bool csum_err = false; 6048c2ecf20Sopenharmony_ci int hdr_len; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci hdr_len = gre_parse_header(skb, &tpi, &csum_err, htons(ETH_P_IPV6), 0); 6078c2ecf20Sopenharmony_ci if (hdr_len < 0) 6088c2ecf20Sopenharmony_ci goto drop; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci if (iptunnel_pull_header(skb, hdr_len, tpi.proto, false)) 6118c2ecf20Sopenharmony_ci goto drop; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (unlikely(tpi.proto == htons(ETH_P_ERSPAN) || 6148c2ecf20Sopenharmony_ci tpi.proto == htons(ETH_P_ERSPAN2))) { 6158c2ecf20Sopenharmony_ci if (ip6erspan_rcv(skb, &tpi, hdr_len) == PACKET_RCVD) 6168c2ecf20Sopenharmony_ci return 0; 6178c2ecf20Sopenharmony_ci goto out; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci if (ip6gre_rcv(skb, &tpi) == PACKET_RCVD) 6218c2ecf20Sopenharmony_ci return 0; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ciout: 6248c2ecf20Sopenharmony_ci icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); 6258c2ecf20Sopenharmony_cidrop: 6268c2ecf20Sopenharmony_ci kfree_skb(skb); 6278c2ecf20Sopenharmony_ci return 0; 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic int gre_handle_offloads(struct sk_buff *skb, bool csum) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci return iptunnel_handle_offloads(skb, 6338c2ecf20Sopenharmony_ci csum ? SKB_GSO_GRE_CSUM : SKB_GSO_GRE); 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_cistatic void prepare_ip6gre_xmit_ipv4(struct sk_buff *skb, 6378c2ecf20Sopenharmony_ci struct net_device *dev, 6388c2ecf20Sopenharmony_ci struct flowi6 *fl6, __u8 *dsfield, 6398c2ecf20Sopenharmony_ci int *encap_limit) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci const struct iphdr *iph = ip_hdr(skb); 6428c2ecf20Sopenharmony_ci struct ip6_tnl *t = netdev_priv(dev); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) 6458c2ecf20Sopenharmony_ci *encap_limit = t->parms.encap_limit; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci memcpy(fl6, &t->fl.u.ip6, sizeof(*fl6)); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS) 6508c2ecf20Sopenharmony_ci *dsfield = ipv4_get_dsfield(iph); 6518c2ecf20Sopenharmony_ci else 6528c2ecf20Sopenharmony_ci *dsfield = ip6_tclass(t->parms.flowinfo); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK) 6558c2ecf20Sopenharmony_ci fl6->flowi6_mark = skb->mark; 6568c2ecf20Sopenharmony_ci else 6578c2ecf20Sopenharmony_ci fl6->flowi6_mark = t->parms.fwmark; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci fl6->flowi6_uid = sock_net_uid(dev_net(dev), NULL); 6608c2ecf20Sopenharmony_ci} 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_cistatic int prepare_ip6gre_xmit_ipv6(struct sk_buff *skb, 6638c2ecf20Sopenharmony_ci struct net_device *dev, 6648c2ecf20Sopenharmony_ci struct flowi6 *fl6, __u8 *dsfield, 6658c2ecf20Sopenharmony_ci int *encap_limit) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci struct ipv6hdr *ipv6h; 6688c2ecf20Sopenharmony_ci struct ip6_tnl *t = netdev_priv(dev); 6698c2ecf20Sopenharmony_ci __u16 offset; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci offset = ip6_tnl_parse_tlv_enc_lim(skb, skb_network_header(skb)); 6728c2ecf20Sopenharmony_ci /* ip6_tnl_parse_tlv_enc_lim() might have reallocated skb->head */ 6738c2ecf20Sopenharmony_ci ipv6h = ipv6_hdr(skb); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci if (offset > 0) { 6768c2ecf20Sopenharmony_ci struct ipv6_tlv_tnl_enc_lim *tel; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci tel = (struct ipv6_tlv_tnl_enc_lim *)&skb_network_header(skb)[offset]; 6798c2ecf20Sopenharmony_ci if (tel->encap_limit == 0) { 6808c2ecf20Sopenharmony_ci icmpv6_ndo_send(skb, ICMPV6_PARAMPROB, 6818c2ecf20Sopenharmony_ci ICMPV6_HDR_FIELD, offset + 2); 6828c2ecf20Sopenharmony_ci return -1; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci *encap_limit = tel->encap_limit - 1; 6858c2ecf20Sopenharmony_ci } else if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) { 6868c2ecf20Sopenharmony_ci *encap_limit = t->parms.encap_limit; 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci memcpy(fl6, &t->fl.u.ip6, sizeof(*fl6)); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS) 6928c2ecf20Sopenharmony_ci *dsfield = ipv6_get_dsfield(ipv6h); 6938c2ecf20Sopenharmony_ci else 6948c2ecf20Sopenharmony_ci *dsfield = ip6_tclass(t->parms.flowinfo); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci if (t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) 6978c2ecf20Sopenharmony_ci fl6->flowlabel |= ip6_flowlabel(ipv6h); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK) 7008c2ecf20Sopenharmony_ci fl6->flowi6_mark = skb->mark; 7018c2ecf20Sopenharmony_ci else 7028c2ecf20Sopenharmony_ci fl6->flowi6_mark = t->parms.fwmark; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci fl6->flowi6_uid = sock_net_uid(dev_net(dev), NULL); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci return 0; 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_cistatic struct ip_tunnel_info *skb_tunnel_info_txcheck(struct sk_buff *skb) 7108c2ecf20Sopenharmony_ci{ 7118c2ecf20Sopenharmony_ci struct ip_tunnel_info *tun_info; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci tun_info = skb_tunnel_info(skb); 7148c2ecf20Sopenharmony_ci if (unlikely(!tun_info || !(tun_info->mode & IP_TUNNEL_INFO_TX))) 7158c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci return tun_info; 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic netdev_tx_t __gre6_xmit(struct sk_buff *skb, 7218c2ecf20Sopenharmony_ci struct net_device *dev, __u8 dsfield, 7228c2ecf20Sopenharmony_ci struct flowi6 *fl6, int encap_limit, 7238c2ecf20Sopenharmony_ci __u32 *pmtu, __be16 proto) 7248c2ecf20Sopenharmony_ci{ 7258c2ecf20Sopenharmony_ci struct ip6_tnl *tunnel = netdev_priv(dev); 7268c2ecf20Sopenharmony_ci __be16 protocol; 7278c2ecf20Sopenharmony_ci __be16 flags; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci if (dev->type == ARPHRD_ETHER) 7308c2ecf20Sopenharmony_ci IPCB(skb)->flags = 0; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci if (dev->header_ops && dev->type == ARPHRD_IP6GRE) 7338c2ecf20Sopenharmony_ci fl6->daddr = ((struct ipv6hdr *)skb->data)->daddr; 7348c2ecf20Sopenharmony_ci else 7358c2ecf20Sopenharmony_ci fl6->daddr = tunnel->parms.raddr; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci /* Push GRE header. */ 7388c2ecf20Sopenharmony_ci protocol = (dev->type == ARPHRD_ETHER) ? htons(ETH_P_TEB) : proto; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci if (tunnel->parms.collect_md) { 7418c2ecf20Sopenharmony_ci struct ip_tunnel_info *tun_info; 7428c2ecf20Sopenharmony_ci const struct ip_tunnel_key *key; 7438c2ecf20Sopenharmony_ci int tun_hlen; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci tun_info = skb_tunnel_info_txcheck(skb); 7468c2ecf20Sopenharmony_ci if (IS_ERR(tun_info) || 7478c2ecf20Sopenharmony_ci unlikely(ip_tunnel_info_af(tun_info) != AF_INET6)) 7488c2ecf20Sopenharmony_ci return -EINVAL; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci key = &tun_info->key; 7518c2ecf20Sopenharmony_ci memset(fl6, 0, sizeof(*fl6)); 7528c2ecf20Sopenharmony_ci fl6->flowi6_proto = IPPROTO_GRE; 7538c2ecf20Sopenharmony_ci fl6->daddr = key->u.ipv6.dst; 7548c2ecf20Sopenharmony_ci fl6->flowlabel = key->label; 7558c2ecf20Sopenharmony_ci fl6->flowi6_uid = sock_net_uid(dev_net(dev), NULL); 7568c2ecf20Sopenharmony_ci fl6->fl6_gre_key = tunnel_id_to_key32(key->tun_id); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci dsfield = key->tos; 7598c2ecf20Sopenharmony_ci flags = key->tun_flags & 7608c2ecf20Sopenharmony_ci (TUNNEL_CSUM | TUNNEL_KEY | TUNNEL_SEQ); 7618c2ecf20Sopenharmony_ci tun_hlen = gre_calc_hlen(flags); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci if (skb_cow_head(skb, dev->needed_headroom ?: tun_hlen + tunnel->encap_hlen)) 7648c2ecf20Sopenharmony_ci return -ENOMEM; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci gre_build_header(skb, tun_hlen, 7678c2ecf20Sopenharmony_ci flags, protocol, 7688c2ecf20Sopenharmony_ci tunnel_id_to_key32(tun_info->key.tun_id), 7698c2ecf20Sopenharmony_ci (flags & TUNNEL_SEQ) ? htonl(atomic_fetch_inc(&tunnel->o_seqno)) 7708c2ecf20Sopenharmony_ci : 0); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci } else { 7738c2ecf20Sopenharmony_ci if (skb_cow_head(skb, dev->needed_headroom ?: tunnel->hlen)) 7748c2ecf20Sopenharmony_ci return -ENOMEM; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci flags = tunnel->parms.o_flags; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci gre_build_header(skb, tunnel->tun_hlen, flags, 7798c2ecf20Sopenharmony_ci protocol, tunnel->parms.o_key, 7808c2ecf20Sopenharmony_ci (flags & TUNNEL_SEQ) ? htonl(atomic_fetch_inc(&tunnel->o_seqno)) 7818c2ecf20Sopenharmony_ci : 0); 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci return ip6_tnl_xmit(skb, dev, dsfield, fl6, encap_limit, pmtu, 7858c2ecf20Sopenharmony_ci NEXTHDR_GRE); 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_cistatic inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci struct ip6_tnl *t = netdev_priv(dev); 7918c2ecf20Sopenharmony_ci int encap_limit = -1; 7928c2ecf20Sopenharmony_ci struct flowi6 fl6; 7938c2ecf20Sopenharmony_ci __u8 dsfield = 0; 7948c2ecf20Sopenharmony_ci __u32 mtu; 7958c2ecf20Sopenharmony_ci int err; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci if (!t->parms.collect_md) 8008c2ecf20Sopenharmony_ci prepare_ip6gre_xmit_ipv4(skb, dev, &fl6, 8018c2ecf20Sopenharmony_ci &dsfield, &encap_limit); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci err = gre_handle_offloads(skb, !!(t->parms.o_flags & TUNNEL_CSUM)); 8048c2ecf20Sopenharmony_ci if (err) 8058c2ecf20Sopenharmony_ci return -1; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci err = __gre6_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu, 8088c2ecf20Sopenharmony_ci skb->protocol); 8098c2ecf20Sopenharmony_ci if (err != 0) { 8108c2ecf20Sopenharmony_ci /* XXX: send ICMP error even if DF is not set. */ 8118c2ecf20Sopenharmony_ci if (err == -EMSGSIZE) 8128c2ecf20Sopenharmony_ci icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, 8138c2ecf20Sopenharmony_ci htonl(mtu)); 8148c2ecf20Sopenharmony_ci return -1; 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci return 0; 8188c2ecf20Sopenharmony_ci} 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_cistatic inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev) 8218c2ecf20Sopenharmony_ci{ 8228c2ecf20Sopenharmony_ci struct ip6_tnl *t = netdev_priv(dev); 8238c2ecf20Sopenharmony_ci struct ipv6hdr *ipv6h = ipv6_hdr(skb); 8248c2ecf20Sopenharmony_ci int encap_limit = -1; 8258c2ecf20Sopenharmony_ci struct flowi6 fl6; 8268c2ecf20Sopenharmony_ci __u8 dsfield = 0; 8278c2ecf20Sopenharmony_ci __u32 mtu; 8288c2ecf20Sopenharmony_ci int err; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&t->parms.raddr, &ipv6h->saddr)) 8318c2ecf20Sopenharmony_ci return -1; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci if (!t->parms.collect_md && 8348c2ecf20Sopenharmony_ci prepare_ip6gre_xmit_ipv6(skb, dev, &fl6, &dsfield, &encap_limit)) 8358c2ecf20Sopenharmony_ci return -1; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if (gre_handle_offloads(skb, !!(t->parms.o_flags & TUNNEL_CSUM))) 8388c2ecf20Sopenharmony_ci return -1; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci err = __gre6_xmit(skb, dev, dsfield, &fl6, encap_limit, 8418c2ecf20Sopenharmony_ci &mtu, skb->protocol); 8428c2ecf20Sopenharmony_ci if (err != 0) { 8438c2ecf20Sopenharmony_ci if (err == -EMSGSIZE) 8448c2ecf20Sopenharmony_ci icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); 8458c2ecf20Sopenharmony_ci return -1; 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci return 0; 8498c2ecf20Sopenharmony_ci} 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci/** 8528c2ecf20Sopenharmony_ci * ip6gre_tnl_addr_conflict - compare packet addresses to tunnel's own 8538c2ecf20Sopenharmony_ci * @t: the outgoing tunnel device 8548c2ecf20Sopenharmony_ci * @hdr: IPv6 header from the incoming packet 8558c2ecf20Sopenharmony_ci * 8568c2ecf20Sopenharmony_ci * Description: 8578c2ecf20Sopenharmony_ci * Avoid trivial tunneling loop by checking that tunnel exit-point 8588c2ecf20Sopenharmony_ci * doesn't match source of incoming packet. 8598c2ecf20Sopenharmony_ci * 8608c2ecf20Sopenharmony_ci * Return: 8618c2ecf20Sopenharmony_ci * 1 if conflict, 8628c2ecf20Sopenharmony_ci * 0 else 8638c2ecf20Sopenharmony_ci **/ 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_cistatic inline bool ip6gre_tnl_addr_conflict(const struct ip6_tnl *t, 8668c2ecf20Sopenharmony_ci const struct ipv6hdr *hdr) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr); 8698c2ecf20Sopenharmony_ci} 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci struct ip6_tnl *t = netdev_priv(dev); 8748c2ecf20Sopenharmony_ci int encap_limit = -1; 8758c2ecf20Sopenharmony_ci struct flowi6 fl6; 8768c2ecf20Sopenharmony_ci __u32 mtu; 8778c2ecf20Sopenharmony_ci int err; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) 8808c2ecf20Sopenharmony_ci encap_limit = t->parms.encap_limit; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci if (!t->parms.collect_md) 8838c2ecf20Sopenharmony_ci memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6)); 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci err = gre_handle_offloads(skb, !!(t->parms.o_flags & TUNNEL_CSUM)); 8868c2ecf20Sopenharmony_ci if (err) 8878c2ecf20Sopenharmony_ci return err; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci err = __gre6_xmit(skb, dev, 0, &fl6, encap_limit, &mtu, skb->protocol); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci return err; 8928c2ecf20Sopenharmony_ci} 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_cistatic netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb, 8958c2ecf20Sopenharmony_ci struct net_device *dev) 8968c2ecf20Sopenharmony_ci{ 8978c2ecf20Sopenharmony_ci struct ip6_tnl *t = netdev_priv(dev); 8988c2ecf20Sopenharmony_ci struct net_device_stats *stats = &t->dev->stats; 8998c2ecf20Sopenharmony_ci int ret; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci if (!pskb_inet_may_pull(skb)) 9028c2ecf20Sopenharmony_ci goto tx_err; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr)) 9058c2ecf20Sopenharmony_ci goto tx_err; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci switch (skb->protocol) { 9088c2ecf20Sopenharmony_ci case htons(ETH_P_IP): 9098c2ecf20Sopenharmony_ci ret = ip6gre_xmit_ipv4(skb, dev); 9108c2ecf20Sopenharmony_ci break; 9118c2ecf20Sopenharmony_ci case htons(ETH_P_IPV6): 9128c2ecf20Sopenharmony_ci ret = ip6gre_xmit_ipv6(skb, dev); 9138c2ecf20Sopenharmony_ci break; 9148c2ecf20Sopenharmony_ci default: 9158c2ecf20Sopenharmony_ci ret = ip6gre_xmit_other(skb, dev); 9168c2ecf20Sopenharmony_ci break; 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci if (ret < 0) 9208c2ecf20Sopenharmony_ci goto tx_err; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_citx_err: 9258c2ecf20Sopenharmony_ci if (!t->parms.collect_md || !IS_ERR(skb_tunnel_info_txcheck(skb))) 9268c2ecf20Sopenharmony_ci stats->tx_errors++; 9278c2ecf20Sopenharmony_ci stats->tx_dropped++; 9288c2ecf20Sopenharmony_ci kfree_skb(skb); 9298c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 9308c2ecf20Sopenharmony_ci} 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_cistatic netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb, 9338c2ecf20Sopenharmony_ci struct net_device *dev) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci struct ip_tunnel_info *tun_info = NULL; 9368c2ecf20Sopenharmony_ci struct ip6_tnl *t = netdev_priv(dev); 9378c2ecf20Sopenharmony_ci struct dst_entry *dst = skb_dst(skb); 9388c2ecf20Sopenharmony_ci struct net_device_stats *stats; 9398c2ecf20Sopenharmony_ci bool truncate = false; 9408c2ecf20Sopenharmony_ci int encap_limit = -1; 9418c2ecf20Sopenharmony_ci __u8 dsfield = false; 9428c2ecf20Sopenharmony_ci struct flowi6 fl6; 9438c2ecf20Sopenharmony_ci int err = -EINVAL; 9448c2ecf20Sopenharmony_ci __be16 proto; 9458c2ecf20Sopenharmony_ci __u32 mtu; 9468c2ecf20Sopenharmony_ci int nhoff; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci if (!pskb_inet_may_pull(skb)) 9498c2ecf20Sopenharmony_ci goto tx_err; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr)) 9528c2ecf20Sopenharmony_ci goto tx_err; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci if (gre_handle_offloads(skb, false)) 9558c2ecf20Sopenharmony_ci goto tx_err; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (skb->len > dev->mtu + dev->hard_header_len) { 9588c2ecf20Sopenharmony_ci if (pskb_trim(skb, dev->mtu + dev->hard_header_len)) 9598c2ecf20Sopenharmony_ci goto tx_err; 9608c2ecf20Sopenharmony_ci truncate = true; 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci nhoff = skb_network_offset(skb); 9648c2ecf20Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP) && 9658c2ecf20Sopenharmony_ci (ntohs(ip_hdr(skb)->tot_len) > skb->len - nhoff)) 9668c2ecf20Sopenharmony_ci truncate = true; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci if (skb->protocol == htons(ETH_P_IPV6)) { 9698c2ecf20Sopenharmony_ci int thoff; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci if (skb_transport_header_was_set(skb)) 9728c2ecf20Sopenharmony_ci thoff = skb_transport_offset(skb); 9738c2ecf20Sopenharmony_ci else 9748c2ecf20Sopenharmony_ci thoff = nhoff + sizeof(struct ipv6hdr); 9758c2ecf20Sopenharmony_ci if (ntohs(ipv6_hdr(skb)->payload_len) > skb->len - thoff) 9768c2ecf20Sopenharmony_ci truncate = true; 9778c2ecf20Sopenharmony_ci } 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci if (skb_cow_head(skb, dev->needed_headroom ?: t->hlen)) 9808c2ecf20Sopenharmony_ci goto tx_err; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci t->parms.o_flags &= ~TUNNEL_KEY; 9838c2ecf20Sopenharmony_ci IPCB(skb)->flags = 0; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci /* For collect_md mode, derive fl6 from the tunnel key, 9868c2ecf20Sopenharmony_ci * for native mode, call prepare_ip6gre_xmit_{ipv4,ipv6}. 9878c2ecf20Sopenharmony_ci */ 9888c2ecf20Sopenharmony_ci if (t->parms.collect_md) { 9898c2ecf20Sopenharmony_ci const struct ip_tunnel_key *key; 9908c2ecf20Sopenharmony_ci struct erspan_metadata *md; 9918c2ecf20Sopenharmony_ci __be32 tun_id; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci tun_info = skb_tunnel_info_txcheck(skb); 9948c2ecf20Sopenharmony_ci if (IS_ERR(tun_info) || 9958c2ecf20Sopenharmony_ci unlikely(ip_tunnel_info_af(tun_info) != AF_INET6)) 9968c2ecf20Sopenharmony_ci goto tx_err; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci key = &tun_info->key; 9998c2ecf20Sopenharmony_ci memset(&fl6, 0, sizeof(fl6)); 10008c2ecf20Sopenharmony_ci fl6.flowi6_proto = IPPROTO_GRE; 10018c2ecf20Sopenharmony_ci fl6.daddr = key->u.ipv6.dst; 10028c2ecf20Sopenharmony_ci fl6.flowlabel = key->label; 10038c2ecf20Sopenharmony_ci fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL); 10048c2ecf20Sopenharmony_ci fl6.fl6_gre_key = tunnel_id_to_key32(key->tun_id); 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci dsfield = key->tos; 10078c2ecf20Sopenharmony_ci if (!(tun_info->key.tun_flags & TUNNEL_ERSPAN_OPT)) 10088c2ecf20Sopenharmony_ci goto tx_err; 10098c2ecf20Sopenharmony_ci if (tun_info->options_len < sizeof(*md)) 10108c2ecf20Sopenharmony_ci goto tx_err; 10118c2ecf20Sopenharmony_ci md = ip_tunnel_info_opts(tun_info); 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci tun_id = tunnel_id_to_key32(key->tun_id); 10148c2ecf20Sopenharmony_ci if (md->version == 1) { 10158c2ecf20Sopenharmony_ci erspan_build_header(skb, 10168c2ecf20Sopenharmony_ci ntohl(tun_id), 10178c2ecf20Sopenharmony_ci ntohl(md->u.index), truncate, 10188c2ecf20Sopenharmony_ci false); 10198c2ecf20Sopenharmony_ci proto = htons(ETH_P_ERSPAN); 10208c2ecf20Sopenharmony_ci } else if (md->version == 2) { 10218c2ecf20Sopenharmony_ci erspan_build_header_v2(skb, 10228c2ecf20Sopenharmony_ci ntohl(tun_id), 10238c2ecf20Sopenharmony_ci md->u.md2.dir, 10248c2ecf20Sopenharmony_ci get_hwid(&md->u.md2), 10258c2ecf20Sopenharmony_ci truncate, false); 10268c2ecf20Sopenharmony_ci proto = htons(ETH_P_ERSPAN2); 10278c2ecf20Sopenharmony_ci } else { 10288c2ecf20Sopenharmony_ci goto tx_err; 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci } else { 10318c2ecf20Sopenharmony_ci switch (skb->protocol) { 10328c2ecf20Sopenharmony_ci case htons(ETH_P_IP): 10338c2ecf20Sopenharmony_ci memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); 10348c2ecf20Sopenharmony_ci prepare_ip6gre_xmit_ipv4(skb, dev, &fl6, 10358c2ecf20Sopenharmony_ci &dsfield, &encap_limit); 10368c2ecf20Sopenharmony_ci break; 10378c2ecf20Sopenharmony_ci case htons(ETH_P_IPV6): 10388c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&t->parms.raddr, &ipv6_hdr(skb)->saddr)) 10398c2ecf20Sopenharmony_ci goto tx_err; 10408c2ecf20Sopenharmony_ci if (prepare_ip6gre_xmit_ipv6(skb, dev, &fl6, 10418c2ecf20Sopenharmony_ci &dsfield, &encap_limit)) 10428c2ecf20Sopenharmony_ci goto tx_err; 10438c2ecf20Sopenharmony_ci break; 10448c2ecf20Sopenharmony_ci default: 10458c2ecf20Sopenharmony_ci memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6)); 10468c2ecf20Sopenharmony_ci break; 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci if (t->parms.erspan_ver == 1) { 10508c2ecf20Sopenharmony_ci erspan_build_header(skb, ntohl(t->parms.o_key), 10518c2ecf20Sopenharmony_ci t->parms.index, 10528c2ecf20Sopenharmony_ci truncate, false); 10538c2ecf20Sopenharmony_ci proto = htons(ETH_P_ERSPAN); 10548c2ecf20Sopenharmony_ci } else if (t->parms.erspan_ver == 2) { 10558c2ecf20Sopenharmony_ci erspan_build_header_v2(skb, ntohl(t->parms.o_key), 10568c2ecf20Sopenharmony_ci t->parms.dir, 10578c2ecf20Sopenharmony_ci t->parms.hwid, 10588c2ecf20Sopenharmony_ci truncate, false); 10598c2ecf20Sopenharmony_ci proto = htons(ETH_P_ERSPAN2); 10608c2ecf20Sopenharmony_ci } else { 10618c2ecf20Sopenharmony_ci goto tx_err; 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci fl6.daddr = t->parms.raddr; 10658c2ecf20Sopenharmony_ci } 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci /* Push GRE header. */ 10688c2ecf20Sopenharmony_ci gre_build_header(skb, 8, TUNNEL_SEQ, proto, 0, htonl(atomic_fetch_inc(&t->o_seqno))); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci /* TooBig packet may have updated dst->dev's mtu */ 10718c2ecf20Sopenharmony_ci if (!t->parms.collect_md && dst && dst_mtu(dst) > dst->dev->mtu) 10728c2ecf20Sopenharmony_ci dst->ops->update_pmtu(dst, NULL, skb, dst->dev->mtu, false); 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci err = ip6_tnl_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu, 10758c2ecf20Sopenharmony_ci NEXTHDR_GRE); 10768c2ecf20Sopenharmony_ci if (err != 0) { 10778c2ecf20Sopenharmony_ci /* XXX: send ICMP error even if DF is not set. */ 10788c2ecf20Sopenharmony_ci if (err == -EMSGSIZE) { 10798c2ecf20Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) 10808c2ecf20Sopenharmony_ci icmp_ndo_send(skb, ICMP_DEST_UNREACH, 10818c2ecf20Sopenharmony_ci ICMP_FRAG_NEEDED, htonl(mtu)); 10828c2ecf20Sopenharmony_ci else 10838c2ecf20Sopenharmony_ci icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci goto tx_err; 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_citx_err: 10918c2ecf20Sopenharmony_ci stats = &t->dev->stats; 10928c2ecf20Sopenharmony_ci if (!IS_ERR(tun_info)) 10938c2ecf20Sopenharmony_ci stats->tx_errors++; 10948c2ecf20Sopenharmony_ci stats->tx_dropped++; 10958c2ecf20Sopenharmony_ci kfree_skb(skb); 10968c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 10978c2ecf20Sopenharmony_ci} 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_cistatic void ip6gre_tnl_link_config_common(struct ip6_tnl *t) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci struct net_device *dev = t->dev; 11028c2ecf20Sopenharmony_ci struct __ip6_tnl_parm *p = &t->parms; 11038c2ecf20Sopenharmony_ci struct flowi6 *fl6 = &t->fl.u.ip6; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci if (dev->type != ARPHRD_ETHER) { 11068c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, &p->laddr, sizeof(struct in6_addr)); 11078c2ecf20Sopenharmony_ci memcpy(dev->broadcast, &p->raddr, sizeof(struct in6_addr)); 11088c2ecf20Sopenharmony_ci } 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci /* Set up flowi template */ 11118c2ecf20Sopenharmony_ci fl6->saddr = p->laddr; 11128c2ecf20Sopenharmony_ci fl6->daddr = p->raddr; 11138c2ecf20Sopenharmony_ci fl6->flowi6_oif = p->link; 11148c2ecf20Sopenharmony_ci fl6->flowlabel = 0; 11158c2ecf20Sopenharmony_ci fl6->flowi6_proto = IPPROTO_GRE; 11168c2ecf20Sopenharmony_ci fl6->fl6_gre_key = t->parms.o_key; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci if (!(p->flags&IP6_TNL_F_USE_ORIG_TCLASS)) 11198c2ecf20Sopenharmony_ci fl6->flowlabel |= IPV6_TCLASS_MASK & p->flowinfo; 11208c2ecf20Sopenharmony_ci if (!(p->flags&IP6_TNL_F_USE_ORIG_FLOWLABEL)) 11218c2ecf20Sopenharmony_ci fl6->flowlabel |= IPV6_FLOWLABEL_MASK & p->flowinfo; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci p->flags &= ~(IP6_TNL_F_CAP_XMIT|IP6_TNL_F_CAP_RCV|IP6_TNL_F_CAP_PER_PACKET); 11248c2ecf20Sopenharmony_ci p->flags |= ip6_tnl_get_cap(t, &p->laddr, &p->raddr); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci if (p->flags&IP6_TNL_F_CAP_XMIT && 11278c2ecf20Sopenharmony_ci p->flags&IP6_TNL_F_CAP_RCV && dev->type != ARPHRD_ETHER) 11288c2ecf20Sopenharmony_ci dev->flags |= IFF_POINTOPOINT; 11298c2ecf20Sopenharmony_ci else 11308c2ecf20Sopenharmony_ci dev->flags &= ~IFF_POINTOPOINT; 11318c2ecf20Sopenharmony_ci} 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_cistatic void ip6gre_tnl_link_config_route(struct ip6_tnl *t, int set_mtu, 11348c2ecf20Sopenharmony_ci int t_hlen) 11358c2ecf20Sopenharmony_ci{ 11368c2ecf20Sopenharmony_ci const struct __ip6_tnl_parm *p = &t->parms; 11378c2ecf20Sopenharmony_ci struct net_device *dev = t->dev; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci if (p->flags & IP6_TNL_F_CAP_XMIT) { 11408c2ecf20Sopenharmony_ci int strict = (ipv6_addr_type(&p->raddr) & 11418c2ecf20Sopenharmony_ci (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL)); 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci struct rt6_info *rt = rt6_lookup(t->net, 11448c2ecf20Sopenharmony_ci &p->raddr, &p->laddr, 11458c2ecf20Sopenharmony_ci p->link, NULL, strict); 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci if (!rt) 11488c2ecf20Sopenharmony_ci return; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci if (rt->dst.dev) { 11518c2ecf20Sopenharmony_ci unsigned short dst_len = rt->dst.dev->hard_header_len + 11528c2ecf20Sopenharmony_ci t_hlen; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci if (t->dev->header_ops) 11558c2ecf20Sopenharmony_ci dev->hard_header_len = dst_len; 11568c2ecf20Sopenharmony_ci else 11578c2ecf20Sopenharmony_ci dev->needed_headroom = dst_len; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci if (set_mtu) { 11608c2ecf20Sopenharmony_ci int mtu = rt->dst.dev->mtu - t_hlen; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) 11638c2ecf20Sopenharmony_ci mtu -= 8; 11648c2ecf20Sopenharmony_ci if (dev->type == ARPHRD_ETHER) 11658c2ecf20Sopenharmony_ci mtu -= ETH_HLEN; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci if (mtu < IPV6_MIN_MTU) 11688c2ecf20Sopenharmony_ci mtu = IPV6_MIN_MTU; 11698c2ecf20Sopenharmony_ci WRITE_ONCE(dev->mtu, mtu); 11708c2ecf20Sopenharmony_ci } 11718c2ecf20Sopenharmony_ci } 11728c2ecf20Sopenharmony_ci ip6_rt_put(rt); 11738c2ecf20Sopenharmony_ci } 11748c2ecf20Sopenharmony_ci} 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_cistatic int ip6gre_calc_hlen(struct ip6_tnl *tunnel) 11778c2ecf20Sopenharmony_ci{ 11788c2ecf20Sopenharmony_ci int t_hlen; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci tunnel->tun_hlen = gre_calc_hlen(tunnel->parms.o_flags); 11818c2ecf20Sopenharmony_ci tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci t_hlen = tunnel->hlen + sizeof(struct ipv6hdr); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci if (tunnel->dev->header_ops) 11868c2ecf20Sopenharmony_ci tunnel->dev->hard_header_len = LL_MAX_HEADER + t_hlen; 11878c2ecf20Sopenharmony_ci else 11888c2ecf20Sopenharmony_ci tunnel->dev->needed_headroom = LL_MAX_HEADER + t_hlen; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci return t_hlen; 11918c2ecf20Sopenharmony_ci} 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_cistatic void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu) 11948c2ecf20Sopenharmony_ci{ 11958c2ecf20Sopenharmony_ci ip6gre_tnl_link_config_common(t); 11968c2ecf20Sopenharmony_ci ip6gre_tnl_link_config_route(t, set_mtu, ip6gre_calc_hlen(t)); 11978c2ecf20Sopenharmony_ci} 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_cistatic void ip6gre_tnl_copy_tnl_parm(struct ip6_tnl *t, 12008c2ecf20Sopenharmony_ci const struct __ip6_tnl_parm *p) 12018c2ecf20Sopenharmony_ci{ 12028c2ecf20Sopenharmony_ci t->parms.laddr = p->laddr; 12038c2ecf20Sopenharmony_ci t->parms.raddr = p->raddr; 12048c2ecf20Sopenharmony_ci t->parms.flags = p->flags; 12058c2ecf20Sopenharmony_ci t->parms.hop_limit = p->hop_limit; 12068c2ecf20Sopenharmony_ci t->parms.encap_limit = p->encap_limit; 12078c2ecf20Sopenharmony_ci t->parms.flowinfo = p->flowinfo; 12088c2ecf20Sopenharmony_ci t->parms.link = p->link; 12098c2ecf20Sopenharmony_ci t->parms.proto = p->proto; 12108c2ecf20Sopenharmony_ci t->parms.i_key = p->i_key; 12118c2ecf20Sopenharmony_ci t->parms.o_key = p->o_key; 12128c2ecf20Sopenharmony_ci t->parms.i_flags = p->i_flags; 12138c2ecf20Sopenharmony_ci t->parms.o_flags = p->o_flags; 12148c2ecf20Sopenharmony_ci t->parms.fwmark = p->fwmark; 12158c2ecf20Sopenharmony_ci t->parms.erspan_ver = p->erspan_ver; 12168c2ecf20Sopenharmony_ci t->parms.index = p->index; 12178c2ecf20Sopenharmony_ci t->parms.dir = p->dir; 12188c2ecf20Sopenharmony_ci t->parms.hwid = p->hwid; 12198c2ecf20Sopenharmony_ci dst_cache_reset(&t->dst_cache); 12208c2ecf20Sopenharmony_ci} 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_cistatic int ip6gre_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p, 12238c2ecf20Sopenharmony_ci int set_mtu) 12248c2ecf20Sopenharmony_ci{ 12258c2ecf20Sopenharmony_ci ip6gre_tnl_copy_tnl_parm(t, p); 12268c2ecf20Sopenharmony_ci ip6gre_tnl_link_config(t, set_mtu); 12278c2ecf20Sopenharmony_ci return 0; 12288c2ecf20Sopenharmony_ci} 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_cistatic void ip6gre_tnl_parm_from_user(struct __ip6_tnl_parm *p, 12318c2ecf20Sopenharmony_ci const struct ip6_tnl_parm2 *u) 12328c2ecf20Sopenharmony_ci{ 12338c2ecf20Sopenharmony_ci p->laddr = u->laddr; 12348c2ecf20Sopenharmony_ci p->raddr = u->raddr; 12358c2ecf20Sopenharmony_ci p->flags = u->flags; 12368c2ecf20Sopenharmony_ci p->hop_limit = u->hop_limit; 12378c2ecf20Sopenharmony_ci p->encap_limit = u->encap_limit; 12388c2ecf20Sopenharmony_ci p->flowinfo = u->flowinfo; 12398c2ecf20Sopenharmony_ci p->link = u->link; 12408c2ecf20Sopenharmony_ci p->i_key = u->i_key; 12418c2ecf20Sopenharmony_ci p->o_key = u->o_key; 12428c2ecf20Sopenharmony_ci p->i_flags = gre_flags_to_tnl_flags(u->i_flags); 12438c2ecf20Sopenharmony_ci p->o_flags = gre_flags_to_tnl_flags(u->o_flags); 12448c2ecf20Sopenharmony_ci memcpy(p->name, u->name, sizeof(u->name)); 12458c2ecf20Sopenharmony_ci} 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_cistatic void ip6gre_tnl_parm_to_user(struct ip6_tnl_parm2 *u, 12488c2ecf20Sopenharmony_ci const struct __ip6_tnl_parm *p) 12498c2ecf20Sopenharmony_ci{ 12508c2ecf20Sopenharmony_ci u->proto = IPPROTO_GRE; 12518c2ecf20Sopenharmony_ci u->laddr = p->laddr; 12528c2ecf20Sopenharmony_ci u->raddr = p->raddr; 12538c2ecf20Sopenharmony_ci u->flags = p->flags; 12548c2ecf20Sopenharmony_ci u->hop_limit = p->hop_limit; 12558c2ecf20Sopenharmony_ci u->encap_limit = p->encap_limit; 12568c2ecf20Sopenharmony_ci u->flowinfo = p->flowinfo; 12578c2ecf20Sopenharmony_ci u->link = p->link; 12588c2ecf20Sopenharmony_ci u->i_key = p->i_key; 12598c2ecf20Sopenharmony_ci u->o_key = p->o_key; 12608c2ecf20Sopenharmony_ci u->i_flags = gre_tnl_flags_to_gre_flags(p->i_flags); 12618c2ecf20Sopenharmony_ci u->o_flags = gre_tnl_flags_to_gre_flags(p->o_flags); 12628c2ecf20Sopenharmony_ci memcpy(u->name, p->name, sizeof(u->name)); 12638c2ecf20Sopenharmony_ci} 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_cistatic int ip6gre_tunnel_ioctl(struct net_device *dev, 12668c2ecf20Sopenharmony_ci struct ifreq *ifr, int cmd) 12678c2ecf20Sopenharmony_ci{ 12688c2ecf20Sopenharmony_ci int err = 0; 12698c2ecf20Sopenharmony_ci struct ip6_tnl_parm2 p; 12708c2ecf20Sopenharmony_ci struct __ip6_tnl_parm p1; 12718c2ecf20Sopenharmony_ci struct ip6_tnl *t = netdev_priv(dev); 12728c2ecf20Sopenharmony_ci struct net *net = t->net; 12738c2ecf20Sopenharmony_ci struct ip6gre_net *ign = net_generic(net, ip6gre_net_id); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci memset(&p1, 0, sizeof(p1)); 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci switch (cmd) { 12788c2ecf20Sopenharmony_ci case SIOCGETTUNNEL: 12798c2ecf20Sopenharmony_ci if (dev == ign->fb_tunnel_dev) { 12808c2ecf20Sopenharmony_ci if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) { 12818c2ecf20Sopenharmony_ci err = -EFAULT; 12828c2ecf20Sopenharmony_ci break; 12838c2ecf20Sopenharmony_ci } 12848c2ecf20Sopenharmony_ci ip6gre_tnl_parm_from_user(&p1, &p); 12858c2ecf20Sopenharmony_ci t = ip6gre_tunnel_locate(net, &p1, 0); 12868c2ecf20Sopenharmony_ci if (!t) 12878c2ecf20Sopenharmony_ci t = netdev_priv(dev); 12888c2ecf20Sopenharmony_ci } 12898c2ecf20Sopenharmony_ci memset(&p, 0, sizeof(p)); 12908c2ecf20Sopenharmony_ci ip6gre_tnl_parm_to_user(&p, &t->parms); 12918c2ecf20Sopenharmony_ci if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p))) 12928c2ecf20Sopenharmony_ci err = -EFAULT; 12938c2ecf20Sopenharmony_ci break; 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci case SIOCADDTUNNEL: 12968c2ecf20Sopenharmony_ci case SIOCCHGTUNNEL: 12978c2ecf20Sopenharmony_ci err = -EPERM; 12988c2ecf20Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 12998c2ecf20Sopenharmony_ci goto done; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci err = -EFAULT; 13028c2ecf20Sopenharmony_ci if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) 13038c2ecf20Sopenharmony_ci goto done; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci err = -EINVAL; 13068c2ecf20Sopenharmony_ci if ((p.i_flags|p.o_flags)&(GRE_VERSION|GRE_ROUTING)) 13078c2ecf20Sopenharmony_ci goto done; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci if (!(p.i_flags&GRE_KEY)) 13108c2ecf20Sopenharmony_ci p.i_key = 0; 13118c2ecf20Sopenharmony_ci if (!(p.o_flags&GRE_KEY)) 13128c2ecf20Sopenharmony_ci p.o_key = 0; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci ip6gre_tnl_parm_from_user(&p1, &p); 13158c2ecf20Sopenharmony_ci t = ip6gre_tunnel_locate(net, &p1, cmd == SIOCADDTUNNEL); 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci if (dev != ign->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) { 13188c2ecf20Sopenharmony_ci if (t) { 13198c2ecf20Sopenharmony_ci if (t->dev != dev) { 13208c2ecf20Sopenharmony_ci err = -EEXIST; 13218c2ecf20Sopenharmony_ci break; 13228c2ecf20Sopenharmony_ci } 13238c2ecf20Sopenharmony_ci } else { 13248c2ecf20Sopenharmony_ci t = netdev_priv(dev); 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci ip6gre_tunnel_unlink(ign, t); 13278c2ecf20Sopenharmony_ci synchronize_net(); 13288c2ecf20Sopenharmony_ci ip6gre_tnl_change(t, &p1, 1); 13298c2ecf20Sopenharmony_ci ip6gre_tunnel_link(ign, t); 13308c2ecf20Sopenharmony_ci netdev_state_change(dev); 13318c2ecf20Sopenharmony_ci } 13328c2ecf20Sopenharmony_ci } 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci if (t) { 13358c2ecf20Sopenharmony_ci err = 0; 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci memset(&p, 0, sizeof(p)); 13388c2ecf20Sopenharmony_ci ip6gre_tnl_parm_to_user(&p, &t->parms); 13398c2ecf20Sopenharmony_ci if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p))) 13408c2ecf20Sopenharmony_ci err = -EFAULT; 13418c2ecf20Sopenharmony_ci } else 13428c2ecf20Sopenharmony_ci err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT); 13438c2ecf20Sopenharmony_ci break; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci case SIOCDELTUNNEL: 13468c2ecf20Sopenharmony_ci err = -EPERM; 13478c2ecf20Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 13488c2ecf20Sopenharmony_ci goto done; 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci if (dev == ign->fb_tunnel_dev) { 13518c2ecf20Sopenharmony_ci err = -EFAULT; 13528c2ecf20Sopenharmony_ci if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) 13538c2ecf20Sopenharmony_ci goto done; 13548c2ecf20Sopenharmony_ci err = -ENOENT; 13558c2ecf20Sopenharmony_ci ip6gre_tnl_parm_from_user(&p1, &p); 13568c2ecf20Sopenharmony_ci t = ip6gre_tunnel_locate(net, &p1, 0); 13578c2ecf20Sopenharmony_ci if (!t) 13588c2ecf20Sopenharmony_ci goto done; 13598c2ecf20Sopenharmony_ci err = -EPERM; 13608c2ecf20Sopenharmony_ci if (t == netdev_priv(ign->fb_tunnel_dev)) 13618c2ecf20Sopenharmony_ci goto done; 13628c2ecf20Sopenharmony_ci dev = t->dev; 13638c2ecf20Sopenharmony_ci } 13648c2ecf20Sopenharmony_ci unregister_netdevice(dev); 13658c2ecf20Sopenharmony_ci err = 0; 13668c2ecf20Sopenharmony_ci break; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci default: 13698c2ecf20Sopenharmony_ci err = -EINVAL; 13708c2ecf20Sopenharmony_ci } 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_cidone: 13738c2ecf20Sopenharmony_ci return err; 13748c2ecf20Sopenharmony_ci} 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_cistatic int ip6gre_header(struct sk_buff *skb, struct net_device *dev, 13778c2ecf20Sopenharmony_ci unsigned short type, const void *daddr, 13788c2ecf20Sopenharmony_ci const void *saddr, unsigned int len) 13798c2ecf20Sopenharmony_ci{ 13808c2ecf20Sopenharmony_ci struct ip6_tnl *t = netdev_priv(dev); 13818c2ecf20Sopenharmony_ci struct ipv6hdr *ipv6h; 13828c2ecf20Sopenharmony_ci __be16 *p; 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci ipv6h = skb_push(skb, t->hlen + sizeof(*ipv6h)); 13858c2ecf20Sopenharmony_ci ip6_flow_hdr(ipv6h, 0, ip6_make_flowlabel(dev_net(dev), skb, 13868c2ecf20Sopenharmony_ci t->fl.u.ip6.flowlabel, 13878c2ecf20Sopenharmony_ci true, &t->fl.u.ip6)); 13888c2ecf20Sopenharmony_ci ipv6h->hop_limit = t->parms.hop_limit; 13898c2ecf20Sopenharmony_ci ipv6h->nexthdr = NEXTHDR_GRE; 13908c2ecf20Sopenharmony_ci ipv6h->saddr = t->parms.laddr; 13918c2ecf20Sopenharmony_ci ipv6h->daddr = t->parms.raddr; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci p = (__be16 *)(ipv6h + 1); 13948c2ecf20Sopenharmony_ci p[0] = t->parms.o_flags; 13958c2ecf20Sopenharmony_ci p[1] = htons(type); 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci /* 13988c2ecf20Sopenharmony_ci * Set the source hardware address. 13998c2ecf20Sopenharmony_ci */ 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci if (saddr) 14028c2ecf20Sopenharmony_ci memcpy(&ipv6h->saddr, saddr, sizeof(struct in6_addr)); 14038c2ecf20Sopenharmony_ci if (daddr) 14048c2ecf20Sopenharmony_ci memcpy(&ipv6h->daddr, daddr, sizeof(struct in6_addr)); 14058c2ecf20Sopenharmony_ci if (!ipv6_addr_any(&ipv6h->daddr)) 14068c2ecf20Sopenharmony_ci return t->hlen; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci return -t->hlen; 14098c2ecf20Sopenharmony_ci} 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_cistatic const struct header_ops ip6gre_header_ops = { 14128c2ecf20Sopenharmony_ci .create = ip6gre_header, 14138c2ecf20Sopenharmony_ci}; 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_cistatic const struct net_device_ops ip6gre_netdev_ops = { 14168c2ecf20Sopenharmony_ci .ndo_init = ip6gre_tunnel_init, 14178c2ecf20Sopenharmony_ci .ndo_uninit = ip6gre_tunnel_uninit, 14188c2ecf20Sopenharmony_ci .ndo_start_xmit = ip6gre_tunnel_xmit, 14198c2ecf20Sopenharmony_ci .ndo_do_ioctl = ip6gre_tunnel_ioctl, 14208c2ecf20Sopenharmony_ci .ndo_change_mtu = ip6_tnl_change_mtu, 14218c2ecf20Sopenharmony_ci .ndo_get_stats64 = ip_tunnel_get_stats64, 14228c2ecf20Sopenharmony_ci .ndo_get_iflink = ip6_tnl_get_iflink, 14238c2ecf20Sopenharmony_ci}; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_cistatic void ip6gre_dev_free(struct net_device *dev) 14268c2ecf20Sopenharmony_ci{ 14278c2ecf20Sopenharmony_ci struct ip6_tnl *t = netdev_priv(dev); 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci gro_cells_destroy(&t->gro_cells); 14308c2ecf20Sopenharmony_ci dst_cache_destroy(&t->dst_cache); 14318c2ecf20Sopenharmony_ci free_percpu(dev->tstats); 14328c2ecf20Sopenharmony_ci} 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_cistatic void ip6gre_tunnel_setup(struct net_device *dev) 14358c2ecf20Sopenharmony_ci{ 14368c2ecf20Sopenharmony_ci dev->netdev_ops = &ip6gre_netdev_ops; 14378c2ecf20Sopenharmony_ci dev->needs_free_netdev = true; 14388c2ecf20Sopenharmony_ci dev->priv_destructor = ip6gre_dev_free; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci dev->type = ARPHRD_IP6GRE; 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci dev->flags |= IFF_NOARP; 14438c2ecf20Sopenharmony_ci dev->addr_len = sizeof(struct in6_addr); 14448c2ecf20Sopenharmony_ci netif_keep_dst(dev); 14458c2ecf20Sopenharmony_ci /* This perm addr will be used as interface identifier by IPv6 */ 14468c2ecf20Sopenharmony_ci dev->addr_assign_type = NET_ADDR_RANDOM; 14478c2ecf20Sopenharmony_ci eth_random_addr(dev->perm_addr); 14488c2ecf20Sopenharmony_ci} 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci#define GRE6_FEATURES (NETIF_F_SG | \ 14518c2ecf20Sopenharmony_ci NETIF_F_FRAGLIST | \ 14528c2ecf20Sopenharmony_ci NETIF_F_HIGHDMA | \ 14538c2ecf20Sopenharmony_ci NETIF_F_HW_CSUM) 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_cistatic void ip6gre_tnl_init_features(struct net_device *dev) 14568c2ecf20Sopenharmony_ci{ 14578c2ecf20Sopenharmony_ci struct ip6_tnl *nt = netdev_priv(dev); 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci dev->features |= GRE6_FEATURES; 14608c2ecf20Sopenharmony_ci dev->hw_features |= GRE6_FEATURES; 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci if (!(nt->parms.o_flags & TUNNEL_SEQ)) { 14638c2ecf20Sopenharmony_ci /* TCP offload with GRE SEQ is not supported, nor 14648c2ecf20Sopenharmony_ci * can we support 2 levels of outer headers requiring 14658c2ecf20Sopenharmony_ci * an update. 14668c2ecf20Sopenharmony_ci */ 14678c2ecf20Sopenharmony_ci if (!(nt->parms.o_flags & TUNNEL_CSUM) || 14688c2ecf20Sopenharmony_ci nt->encap.type == TUNNEL_ENCAP_NONE) { 14698c2ecf20Sopenharmony_ci dev->features |= NETIF_F_GSO_SOFTWARE; 14708c2ecf20Sopenharmony_ci dev->hw_features |= NETIF_F_GSO_SOFTWARE; 14718c2ecf20Sopenharmony_ci } 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci /* Can use a lockless transmit, unless we generate 14748c2ecf20Sopenharmony_ci * output sequences 14758c2ecf20Sopenharmony_ci */ 14768c2ecf20Sopenharmony_ci dev->features |= NETIF_F_LLTX; 14778c2ecf20Sopenharmony_ci } 14788c2ecf20Sopenharmony_ci} 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_cistatic int ip6gre_tunnel_init_common(struct net_device *dev) 14818c2ecf20Sopenharmony_ci{ 14828c2ecf20Sopenharmony_ci struct ip6_tnl *tunnel; 14838c2ecf20Sopenharmony_ci int ret; 14848c2ecf20Sopenharmony_ci int t_hlen; 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci tunnel = netdev_priv(dev); 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci tunnel->dev = dev; 14898c2ecf20Sopenharmony_ci tunnel->net = dev_net(dev); 14908c2ecf20Sopenharmony_ci strcpy(tunnel->parms.name, dev->name); 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); 14938c2ecf20Sopenharmony_ci if (!dev->tstats) 14948c2ecf20Sopenharmony_ci return -ENOMEM; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci ret = dst_cache_init(&tunnel->dst_cache, GFP_KERNEL); 14978c2ecf20Sopenharmony_ci if (ret) 14988c2ecf20Sopenharmony_ci goto cleanup_alloc_pcpu_stats; 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci ret = gro_cells_init(&tunnel->gro_cells, dev); 15018c2ecf20Sopenharmony_ci if (ret) 15028c2ecf20Sopenharmony_ci goto cleanup_dst_cache_init; 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci t_hlen = ip6gre_calc_hlen(tunnel); 15058c2ecf20Sopenharmony_ci dev->mtu = ETH_DATA_LEN - t_hlen; 15068c2ecf20Sopenharmony_ci if (dev->type == ARPHRD_ETHER) 15078c2ecf20Sopenharmony_ci dev->mtu -= ETH_HLEN; 15088c2ecf20Sopenharmony_ci if (!(tunnel->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) 15098c2ecf20Sopenharmony_ci dev->mtu -= 8; 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci if (tunnel->parms.collect_md) { 15128c2ecf20Sopenharmony_ci netif_keep_dst(dev); 15138c2ecf20Sopenharmony_ci } 15148c2ecf20Sopenharmony_ci ip6gre_tnl_init_features(dev); 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci dev_hold(dev); 15178c2ecf20Sopenharmony_ci return 0; 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_cicleanup_dst_cache_init: 15208c2ecf20Sopenharmony_ci dst_cache_destroy(&tunnel->dst_cache); 15218c2ecf20Sopenharmony_cicleanup_alloc_pcpu_stats: 15228c2ecf20Sopenharmony_ci free_percpu(dev->tstats); 15238c2ecf20Sopenharmony_ci dev->tstats = NULL; 15248c2ecf20Sopenharmony_ci return ret; 15258c2ecf20Sopenharmony_ci} 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_cistatic int ip6gre_tunnel_init(struct net_device *dev) 15288c2ecf20Sopenharmony_ci{ 15298c2ecf20Sopenharmony_ci struct ip6_tnl *tunnel; 15308c2ecf20Sopenharmony_ci int ret; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci ret = ip6gre_tunnel_init_common(dev); 15338c2ecf20Sopenharmony_ci if (ret) 15348c2ecf20Sopenharmony_ci return ret; 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci tunnel = netdev_priv(dev); 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci if (tunnel->parms.collect_md) 15398c2ecf20Sopenharmony_ci return 0; 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, &tunnel->parms.laddr, sizeof(struct in6_addr)); 15428c2ecf20Sopenharmony_ci memcpy(dev->broadcast, &tunnel->parms.raddr, sizeof(struct in6_addr)); 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci if (ipv6_addr_any(&tunnel->parms.raddr)) 15458c2ecf20Sopenharmony_ci dev->header_ops = &ip6gre_header_ops; 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci return 0; 15488c2ecf20Sopenharmony_ci} 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_cistatic void ip6gre_fb_tunnel_init(struct net_device *dev) 15518c2ecf20Sopenharmony_ci{ 15528c2ecf20Sopenharmony_ci struct ip6_tnl *tunnel = netdev_priv(dev); 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci tunnel->dev = dev; 15558c2ecf20Sopenharmony_ci tunnel->net = dev_net(dev); 15568c2ecf20Sopenharmony_ci strcpy(tunnel->parms.name, dev->name); 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci tunnel->hlen = sizeof(struct ipv6hdr) + 4; 15598c2ecf20Sopenharmony_ci} 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_cistatic struct inet6_protocol ip6gre_protocol __read_mostly = { 15628c2ecf20Sopenharmony_ci .handler = gre_rcv, 15638c2ecf20Sopenharmony_ci .err_handler = ip6gre_err, 15648c2ecf20Sopenharmony_ci .flags = INET6_PROTO_FINAL, 15658c2ecf20Sopenharmony_ci}; 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_cistatic void ip6gre_destroy_tunnels(struct net *net, struct list_head *head) 15688c2ecf20Sopenharmony_ci{ 15698c2ecf20Sopenharmony_ci struct ip6gre_net *ign = net_generic(net, ip6gre_net_id); 15708c2ecf20Sopenharmony_ci struct net_device *dev, *aux; 15718c2ecf20Sopenharmony_ci int prio; 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci for_each_netdev_safe(net, dev, aux) 15748c2ecf20Sopenharmony_ci if (dev->rtnl_link_ops == &ip6gre_link_ops || 15758c2ecf20Sopenharmony_ci dev->rtnl_link_ops == &ip6gre_tap_ops || 15768c2ecf20Sopenharmony_ci dev->rtnl_link_ops == &ip6erspan_tap_ops) 15778c2ecf20Sopenharmony_ci unregister_netdevice_queue(dev, head); 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci for (prio = 0; prio < 4; prio++) { 15808c2ecf20Sopenharmony_ci int h; 15818c2ecf20Sopenharmony_ci for (h = 0; h < IP6_GRE_HASH_SIZE; h++) { 15828c2ecf20Sopenharmony_ci struct ip6_tnl *t; 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci t = rtnl_dereference(ign->tunnels[prio][h]); 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci while (t) { 15878c2ecf20Sopenharmony_ci /* If dev is in the same netns, it has already 15888c2ecf20Sopenharmony_ci * been added to the list by the previous loop. 15898c2ecf20Sopenharmony_ci */ 15908c2ecf20Sopenharmony_ci if (!net_eq(dev_net(t->dev), net)) 15918c2ecf20Sopenharmony_ci unregister_netdevice_queue(t->dev, 15928c2ecf20Sopenharmony_ci head); 15938c2ecf20Sopenharmony_ci t = rtnl_dereference(t->next); 15948c2ecf20Sopenharmony_ci } 15958c2ecf20Sopenharmony_ci } 15968c2ecf20Sopenharmony_ci } 15978c2ecf20Sopenharmony_ci} 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_cistatic int __net_init ip6gre_init_net(struct net *net) 16008c2ecf20Sopenharmony_ci{ 16018c2ecf20Sopenharmony_ci struct ip6gre_net *ign = net_generic(net, ip6gre_net_id); 16028c2ecf20Sopenharmony_ci struct net_device *ndev; 16038c2ecf20Sopenharmony_ci int err; 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci if (!net_has_fallback_tunnels(net)) 16068c2ecf20Sopenharmony_ci return 0; 16078c2ecf20Sopenharmony_ci ndev = alloc_netdev(sizeof(struct ip6_tnl), "ip6gre0", 16088c2ecf20Sopenharmony_ci NET_NAME_UNKNOWN, ip6gre_tunnel_setup); 16098c2ecf20Sopenharmony_ci if (!ndev) { 16108c2ecf20Sopenharmony_ci err = -ENOMEM; 16118c2ecf20Sopenharmony_ci goto err_alloc_dev; 16128c2ecf20Sopenharmony_ci } 16138c2ecf20Sopenharmony_ci ign->fb_tunnel_dev = ndev; 16148c2ecf20Sopenharmony_ci dev_net_set(ign->fb_tunnel_dev, net); 16158c2ecf20Sopenharmony_ci /* FB netdevice is special: we have one, and only one per netns. 16168c2ecf20Sopenharmony_ci * Allowing to move it to another netns is clearly unsafe. 16178c2ecf20Sopenharmony_ci */ 16188c2ecf20Sopenharmony_ci ign->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci ip6gre_fb_tunnel_init(ign->fb_tunnel_dev); 16228c2ecf20Sopenharmony_ci ign->fb_tunnel_dev->rtnl_link_ops = &ip6gre_link_ops; 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci err = register_netdev(ign->fb_tunnel_dev); 16258c2ecf20Sopenharmony_ci if (err) 16268c2ecf20Sopenharmony_ci goto err_reg_dev; 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci rcu_assign_pointer(ign->tunnels_wc[0], 16298c2ecf20Sopenharmony_ci netdev_priv(ign->fb_tunnel_dev)); 16308c2ecf20Sopenharmony_ci return 0; 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_cierr_reg_dev: 16338c2ecf20Sopenharmony_ci free_netdev(ndev); 16348c2ecf20Sopenharmony_cierr_alloc_dev: 16358c2ecf20Sopenharmony_ci return err; 16368c2ecf20Sopenharmony_ci} 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_cistatic void __net_exit ip6gre_exit_batch_net(struct list_head *net_list) 16398c2ecf20Sopenharmony_ci{ 16408c2ecf20Sopenharmony_ci struct net *net; 16418c2ecf20Sopenharmony_ci LIST_HEAD(list); 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci rtnl_lock(); 16448c2ecf20Sopenharmony_ci list_for_each_entry(net, net_list, exit_list) 16458c2ecf20Sopenharmony_ci ip6gre_destroy_tunnels(net, &list); 16468c2ecf20Sopenharmony_ci unregister_netdevice_many(&list); 16478c2ecf20Sopenharmony_ci rtnl_unlock(); 16488c2ecf20Sopenharmony_ci} 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_cistatic struct pernet_operations ip6gre_net_ops = { 16518c2ecf20Sopenharmony_ci .init = ip6gre_init_net, 16528c2ecf20Sopenharmony_ci .exit_batch = ip6gre_exit_batch_net, 16538c2ecf20Sopenharmony_ci .id = &ip6gre_net_id, 16548c2ecf20Sopenharmony_ci .size = sizeof(struct ip6gre_net), 16558c2ecf20Sopenharmony_ci}; 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_cistatic int ip6gre_tunnel_validate(struct nlattr *tb[], struct nlattr *data[], 16588c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 16598c2ecf20Sopenharmony_ci{ 16608c2ecf20Sopenharmony_ci __be16 flags; 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci if (!data) 16638c2ecf20Sopenharmony_ci return 0; 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci flags = 0; 16668c2ecf20Sopenharmony_ci if (data[IFLA_GRE_IFLAGS]) 16678c2ecf20Sopenharmony_ci flags |= nla_get_be16(data[IFLA_GRE_IFLAGS]); 16688c2ecf20Sopenharmony_ci if (data[IFLA_GRE_OFLAGS]) 16698c2ecf20Sopenharmony_ci flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]); 16708c2ecf20Sopenharmony_ci if (flags & (GRE_VERSION|GRE_ROUTING)) 16718c2ecf20Sopenharmony_ci return -EINVAL; 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci return 0; 16748c2ecf20Sopenharmony_ci} 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_cistatic int ip6gre_tap_validate(struct nlattr *tb[], struct nlattr *data[], 16778c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 16788c2ecf20Sopenharmony_ci{ 16798c2ecf20Sopenharmony_ci struct in6_addr daddr; 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci if (tb[IFLA_ADDRESS]) { 16828c2ecf20Sopenharmony_ci if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) 16838c2ecf20Sopenharmony_ci return -EINVAL; 16848c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) 16858c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 16868c2ecf20Sopenharmony_ci } 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci if (!data) 16898c2ecf20Sopenharmony_ci goto out; 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci if (data[IFLA_GRE_REMOTE]) { 16928c2ecf20Sopenharmony_ci daddr = nla_get_in6_addr(data[IFLA_GRE_REMOTE]); 16938c2ecf20Sopenharmony_ci if (ipv6_addr_any(&daddr)) 16948c2ecf20Sopenharmony_ci return -EINVAL; 16958c2ecf20Sopenharmony_ci } 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ciout: 16988c2ecf20Sopenharmony_ci return ip6gre_tunnel_validate(tb, data, extack); 16998c2ecf20Sopenharmony_ci} 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_cistatic int ip6erspan_tap_validate(struct nlattr *tb[], struct nlattr *data[], 17028c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 17038c2ecf20Sopenharmony_ci{ 17048c2ecf20Sopenharmony_ci __be16 flags = 0; 17058c2ecf20Sopenharmony_ci int ret, ver = 0; 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci if (!data) 17088c2ecf20Sopenharmony_ci return 0; 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci ret = ip6gre_tap_validate(tb, data, extack); 17118c2ecf20Sopenharmony_ci if (ret) 17128c2ecf20Sopenharmony_ci return ret; 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci /* ERSPAN should only have GRE sequence and key flag */ 17158c2ecf20Sopenharmony_ci if (data[IFLA_GRE_OFLAGS]) 17168c2ecf20Sopenharmony_ci flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]); 17178c2ecf20Sopenharmony_ci if (data[IFLA_GRE_IFLAGS]) 17188c2ecf20Sopenharmony_ci flags |= nla_get_be16(data[IFLA_GRE_IFLAGS]); 17198c2ecf20Sopenharmony_ci if (!data[IFLA_GRE_COLLECT_METADATA] && 17208c2ecf20Sopenharmony_ci flags != (GRE_SEQ | GRE_KEY)) 17218c2ecf20Sopenharmony_ci return -EINVAL; 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci /* ERSPAN Session ID only has 10-bit. Since we reuse 17248c2ecf20Sopenharmony_ci * 32-bit key field as ID, check it's range. 17258c2ecf20Sopenharmony_ci */ 17268c2ecf20Sopenharmony_ci if (data[IFLA_GRE_IKEY] && 17278c2ecf20Sopenharmony_ci (ntohl(nla_get_be32(data[IFLA_GRE_IKEY])) & ~ID_MASK)) 17288c2ecf20Sopenharmony_ci return -EINVAL; 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci if (data[IFLA_GRE_OKEY] && 17318c2ecf20Sopenharmony_ci (ntohl(nla_get_be32(data[IFLA_GRE_OKEY])) & ~ID_MASK)) 17328c2ecf20Sopenharmony_ci return -EINVAL; 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci if (data[IFLA_GRE_ERSPAN_VER]) { 17358c2ecf20Sopenharmony_ci ver = nla_get_u8(data[IFLA_GRE_ERSPAN_VER]); 17368c2ecf20Sopenharmony_ci if (ver != 1 && ver != 2) 17378c2ecf20Sopenharmony_ci return -EINVAL; 17388c2ecf20Sopenharmony_ci } 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci if (ver == 1) { 17418c2ecf20Sopenharmony_ci if (data[IFLA_GRE_ERSPAN_INDEX]) { 17428c2ecf20Sopenharmony_ci u32 index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]); 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci if (index & ~INDEX_MASK) 17458c2ecf20Sopenharmony_ci return -EINVAL; 17468c2ecf20Sopenharmony_ci } 17478c2ecf20Sopenharmony_ci } else if (ver == 2) { 17488c2ecf20Sopenharmony_ci if (data[IFLA_GRE_ERSPAN_DIR]) { 17498c2ecf20Sopenharmony_ci u16 dir = nla_get_u8(data[IFLA_GRE_ERSPAN_DIR]); 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci if (dir & ~(DIR_MASK >> DIR_OFFSET)) 17528c2ecf20Sopenharmony_ci return -EINVAL; 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci if (data[IFLA_GRE_ERSPAN_HWID]) { 17568c2ecf20Sopenharmony_ci u16 hwid = nla_get_u16(data[IFLA_GRE_ERSPAN_HWID]); 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci if (hwid & ~(HWID_MASK >> HWID_OFFSET)) 17598c2ecf20Sopenharmony_ci return -EINVAL; 17608c2ecf20Sopenharmony_ci } 17618c2ecf20Sopenharmony_ci } 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci return 0; 17648c2ecf20Sopenharmony_ci} 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_cistatic void ip6erspan_set_version(struct nlattr *data[], 17678c2ecf20Sopenharmony_ci struct __ip6_tnl_parm *parms) 17688c2ecf20Sopenharmony_ci{ 17698c2ecf20Sopenharmony_ci if (!data) 17708c2ecf20Sopenharmony_ci return; 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci parms->erspan_ver = 1; 17738c2ecf20Sopenharmony_ci if (data[IFLA_GRE_ERSPAN_VER]) 17748c2ecf20Sopenharmony_ci parms->erspan_ver = nla_get_u8(data[IFLA_GRE_ERSPAN_VER]); 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci if (parms->erspan_ver == 1) { 17778c2ecf20Sopenharmony_ci if (data[IFLA_GRE_ERSPAN_INDEX]) 17788c2ecf20Sopenharmony_ci parms->index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]); 17798c2ecf20Sopenharmony_ci } else if (parms->erspan_ver == 2) { 17808c2ecf20Sopenharmony_ci if (data[IFLA_GRE_ERSPAN_DIR]) 17818c2ecf20Sopenharmony_ci parms->dir = nla_get_u8(data[IFLA_GRE_ERSPAN_DIR]); 17828c2ecf20Sopenharmony_ci if (data[IFLA_GRE_ERSPAN_HWID]) 17838c2ecf20Sopenharmony_ci parms->hwid = nla_get_u16(data[IFLA_GRE_ERSPAN_HWID]); 17848c2ecf20Sopenharmony_ci } 17858c2ecf20Sopenharmony_ci} 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_cistatic void ip6gre_netlink_parms(struct nlattr *data[], 17888c2ecf20Sopenharmony_ci struct __ip6_tnl_parm *parms) 17898c2ecf20Sopenharmony_ci{ 17908c2ecf20Sopenharmony_ci memset(parms, 0, sizeof(*parms)); 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci if (!data) 17938c2ecf20Sopenharmony_ci return; 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci if (data[IFLA_GRE_LINK]) 17968c2ecf20Sopenharmony_ci parms->link = nla_get_u32(data[IFLA_GRE_LINK]); 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci if (data[IFLA_GRE_IFLAGS]) 17998c2ecf20Sopenharmony_ci parms->i_flags = gre_flags_to_tnl_flags( 18008c2ecf20Sopenharmony_ci nla_get_be16(data[IFLA_GRE_IFLAGS])); 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci if (data[IFLA_GRE_OFLAGS]) 18038c2ecf20Sopenharmony_ci parms->o_flags = gre_flags_to_tnl_flags( 18048c2ecf20Sopenharmony_ci nla_get_be16(data[IFLA_GRE_OFLAGS])); 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci if (data[IFLA_GRE_IKEY]) 18078c2ecf20Sopenharmony_ci parms->i_key = nla_get_be32(data[IFLA_GRE_IKEY]); 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci if (data[IFLA_GRE_OKEY]) 18108c2ecf20Sopenharmony_ci parms->o_key = nla_get_be32(data[IFLA_GRE_OKEY]); 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci if (data[IFLA_GRE_LOCAL]) 18138c2ecf20Sopenharmony_ci parms->laddr = nla_get_in6_addr(data[IFLA_GRE_LOCAL]); 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci if (data[IFLA_GRE_REMOTE]) 18168c2ecf20Sopenharmony_ci parms->raddr = nla_get_in6_addr(data[IFLA_GRE_REMOTE]); 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci if (data[IFLA_GRE_TTL]) 18198c2ecf20Sopenharmony_ci parms->hop_limit = nla_get_u8(data[IFLA_GRE_TTL]); 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci if (data[IFLA_GRE_ENCAP_LIMIT]) 18228c2ecf20Sopenharmony_ci parms->encap_limit = nla_get_u8(data[IFLA_GRE_ENCAP_LIMIT]); 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci if (data[IFLA_GRE_FLOWINFO]) 18258c2ecf20Sopenharmony_ci parms->flowinfo = nla_get_be32(data[IFLA_GRE_FLOWINFO]); 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci if (data[IFLA_GRE_FLAGS]) 18288c2ecf20Sopenharmony_ci parms->flags = nla_get_u32(data[IFLA_GRE_FLAGS]); 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci if (data[IFLA_GRE_FWMARK]) 18318c2ecf20Sopenharmony_ci parms->fwmark = nla_get_u32(data[IFLA_GRE_FWMARK]); 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_ci if (data[IFLA_GRE_COLLECT_METADATA]) 18348c2ecf20Sopenharmony_ci parms->collect_md = true; 18358c2ecf20Sopenharmony_ci} 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_cistatic int ip6gre_tap_init(struct net_device *dev) 18388c2ecf20Sopenharmony_ci{ 18398c2ecf20Sopenharmony_ci int ret; 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci ret = ip6gre_tunnel_init_common(dev); 18428c2ecf20Sopenharmony_ci if (ret) 18438c2ecf20Sopenharmony_ci return ret; 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci return 0; 18488c2ecf20Sopenharmony_ci} 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_cistatic const struct net_device_ops ip6gre_tap_netdev_ops = { 18518c2ecf20Sopenharmony_ci .ndo_init = ip6gre_tap_init, 18528c2ecf20Sopenharmony_ci .ndo_uninit = ip6gre_tunnel_uninit, 18538c2ecf20Sopenharmony_ci .ndo_start_xmit = ip6gre_tunnel_xmit, 18548c2ecf20Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 18558c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 18568c2ecf20Sopenharmony_ci .ndo_change_mtu = ip6_tnl_change_mtu, 18578c2ecf20Sopenharmony_ci .ndo_get_stats64 = ip_tunnel_get_stats64, 18588c2ecf20Sopenharmony_ci .ndo_get_iflink = ip6_tnl_get_iflink, 18598c2ecf20Sopenharmony_ci}; 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_cistatic int ip6erspan_calc_hlen(struct ip6_tnl *tunnel) 18628c2ecf20Sopenharmony_ci{ 18638c2ecf20Sopenharmony_ci int t_hlen; 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci tunnel->tun_hlen = 8; 18668c2ecf20Sopenharmony_ci tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen + 18678c2ecf20Sopenharmony_ci erspan_hdr_len(tunnel->parms.erspan_ver); 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci t_hlen = tunnel->hlen + sizeof(struct ipv6hdr); 18708c2ecf20Sopenharmony_ci tunnel->dev->needed_headroom = LL_MAX_HEADER + t_hlen; 18718c2ecf20Sopenharmony_ci return t_hlen; 18728c2ecf20Sopenharmony_ci} 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_cistatic int ip6erspan_tap_init(struct net_device *dev) 18758c2ecf20Sopenharmony_ci{ 18768c2ecf20Sopenharmony_ci struct ip6_tnl *tunnel; 18778c2ecf20Sopenharmony_ci int t_hlen; 18788c2ecf20Sopenharmony_ci int ret; 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci tunnel = netdev_priv(dev); 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci tunnel->dev = dev; 18838c2ecf20Sopenharmony_ci tunnel->net = dev_net(dev); 18848c2ecf20Sopenharmony_ci strcpy(tunnel->parms.name, dev->name); 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); 18878c2ecf20Sopenharmony_ci if (!dev->tstats) 18888c2ecf20Sopenharmony_ci return -ENOMEM; 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci ret = dst_cache_init(&tunnel->dst_cache, GFP_KERNEL); 18918c2ecf20Sopenharmony_ci if (ret) 18928c2ecf20Sopenharmony_ci goto cleanup_alloc_pcpu_stats; 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci ret = gro_cells_init(&tunnel->gro_cells, dev); 18958c2ecf20Sopenharmony_ci if (ret) 18968c2ecf20Sopenharmony_ci goto cleanup_dst_cache_init; 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci t_hlen = ip6erspan_calc_hlen(tunnel); 18998c2ecf20Sopenharmony_ci dev->mtu = ETH_DATA_LEN - t_hlen; 19008c2ecf20Sopenharmony_ci if (dev->type == ARPHRD_ETHER) 19018c2ecf20Sopenharmony_ci dev->mtu -= ETH_HLEN; 19028c2ecf20Sopenharmony_ci if (!(tunnel->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) 19038c2ecf20Sopenharmony_ci dev->mtu -= 8; 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; 19068c2ecf20Sopenharmony_ci ip6erspan_tnl_link_config(tunnel, 1); 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci dev_hold(dev); 19098c2ecf20Sopenharmony_ci return 0; 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_cicleanup_dst_cache_init: 19128c2ecf20Sopenharmony_ci dst_cache_destroy(&tunnel->dst_cache); 19138c2ecf20Sopenharmony_cicleanup_alloc_pcpu_stats: 19148c2ecf20Sopenharmony_ci free_percpu(dev->tstats); 19158c2ecf20Sopenharmony_ci dev->tstats = NULL; 19168c2ecf20Sopenharmony_ci return ret; 19178c2ecf20Sopenharmony_ci} 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_cistatic const struct net_device_ops ip6erspan_netdev_ops = { 19208c2ecf20Sopenharmony_ci .ndo_init = ip6erspan_tap_init, 19218c2ecf20Sopenharmony_ci .ndo_uninit = ip6erspan_tunnel_uninit, 19228c2ecf20Sopenharmony_ci .ndo_start_xmit = ip6erspan_tunnel_xmit, 19238c2ecf20Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 19248c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 19258c2ecf20Sopenharmony_ci .ndo_change_mtu = ip6_tnl_change_mtu, 19268c2ecf20Sopenharmony_ci .ndo_get_stats64 = ip_tunnel_get_stats64, 19278c2ecf20Sopenharmony_ci .ndo_get_iflink = ip6_tnl_get_iflink, 19288c2ecf20Sopenharmony_ci}; 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_cistatic void ip6gre_tap_setup(struct net_device *dev) 19318c2ecf20Sopenharmony_ci{ 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci ether_setup(dev); 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_ci dev->max_mtu = 0; 19368c2ecf20Sopenharmony_ci dev->netdev_ops = &ip6gre_tap_netdev_ops; 19378c2ecf20Sopenharmony_ci dev->needs_free_netdev = true; 19388c2ecf20Sopenharmony_ci dev->priv_destructor = ip6gre_dev_free; 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ci dev->priv_flags &= ~IFF_TX_SKB_SHARING; 19418c2ecf20Sopenharmony_ci dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; 19428c2ecf20Sopenharmony_ci netif_keep_dst(dev); 19438c2ecf20Sopenharmony_ci} 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_cistatic bool ip6gre_netlink_encap_parms(struct nlattr *data[], 19468c2ecf20Sopenharmony_ci struct ip_tunnel_encap *ipencap) 19478c2ecf20Sopenharmony_ci{ 19488c2ecf20Sopenharmony_ci bool ret = false; 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_ci memset(ipencap, 0, sizeof(*ipencap)); 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci if (!data) 19538c2ecf20Sopenharmony_ci return ret; 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci if (data[IFLA_GRE_ENCAP_TYPE]) { 19568c2ecf20Sopenharmony_ci ret = true; 19578c2ecf20Sopenharmony_ci ipencap->type = nla_get_u16(data[IFLA_GRE_ENCAP_TYPE]); 19588c2ecf20Sopenharmony_ci } 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci if (data[IFLA_GRE_ENCAP_FLAGS]) { 19618c2ecf20Sopenharmony_ci ret = true; 19628c2ecf20Sopenharmony_ci ipencap->flags = nla_get_u16(data[IFLA_GRE_ENCAP_FLAGS]); 19638c2ecf20Sopenharmony_ci } 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci if (data[IFLA_GRE_ENCAP_SPORT]) { 19668c2ecf20Sopenharmony_ci ret = true; 19678c2ecf20Sopenharmony_ci ipencap->sport = nla_get_be16(data[IFLA_GRE_ENCAP_SPORT]); 19688c2ecf20Sopenharmony_ci } 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci if (data[IFLA_GRE_ENCAP_DPORT]) { 19718c2ecf20Sopenharmony_ci ret = true; 19728c2ecf20Sopenharmony_ci ipencap->dport = nla_get_be16(data[IFLA_GRE_ENCAP_DPORT]); 19738c2ecf20Sopenharmony_ci } 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci return ret; 19768c2ecf20Sopenharmony_ci} 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_cistatic int ip6gre_newlink_common(struct net *src_net, struct net_device *dev, 19798c2ecf20Sopenharmony_ci struct nlattr *tb[], struct nlattr *data[], 19808c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 19818c2ecf20Sopenharmony_ci{ 19828c2ecf20Sopenharmony_ci struct ip6_tnl *nt; 19838c2ecf20Sopenharmony_ci struct ip_tunnel_encap ipencap; 19848c2ecf20Sopenharmony_ci int err; 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci nt = netdev_priv(dev); 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_ci if (ip6gre_netlink_encap_parms(data, &ipencap)) { 19898c2ecf20Sopenharmony_ci int err = ip6_tnl_encap_setup(nt, &ipencap); 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci if (err < 0) 19928c2ecf20Sopenharmony_ci return err; 19938c2ecf20Sopenharmony_ci } 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci if (dev->type == ARPHRD_ETHER && !tb[IFLA_ADDRESS]) 19968c2ecf20Sopenharmony_ci eth_hw_addr_random(dev); 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci nt->dev = dev; 19998c2ecf20Sopenharmony_ci nt->net = dev_net(dev); 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci err = register_netdevice(dev); 20028c2ecf20Sopenharmony_ci if (err) 20038c2ecf20Sopenharmony_ci goto out; 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci if (tb[IFLA_MTU]) 20068c2ecf20Sopenharmony_ci ip6_tnl_change_mtu(dev, nla_get_u32(tb[IFLA_MTU])); 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ciout: 20098c2ecf20Sopenharmony_ci return err; 20108c2ecf20Sopenharmony_ci} 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_cistatic int ip6gre_newlink(struct net *src_net, struct net_device *dev, 20138c2ecf20Sopenharmony_ci struct nlattr *tb[], struct nlattr *data[], 20148c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 20158c2ecf20Sopenharmony_ci{ 20168c2ecf20Sopenharmony_ci struct ip6_tnl *nt = netdev_priv(dev); 20178c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 20188c2ecf20Sopenharmony_ci struct ip6gre_net *ign; 20198c2ecf20Sopenharmony_ci int err; 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci ip6gre_netlink_parms(data, &nt->parms); 20228c2ecf20Sopenharmony_ci ign = net_generic(net, ip6gre_net_id); 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci if (nt->parms.collect_md) { 20258c2ecf20Sopenharmony_ci if (rtnl_dereference(ign->collect_md_tun)) 20268c2ecf20Sopenharmony_ci return -EEXIST; 20278c2ecf20Sopenharmony_ci } else { 20288c2ecf20Sopenharmony_ci if (ip6gre_tunnel_find(net, &nt->parms, dev->type)) 20298c2ecf20Sopenharmony_ci return -EEXIST; 20308c2ecf20Sopenharmony_ci } 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci err = ip6gre_newlink_common(src_net, dev, tb, data, extack); 20338c2ecf20Sopenharmony_ci if (!err) { 20348c2ecf20Sopenharmony_ci ip6gre_tnl_link_config(nt, !tb[IFLA_MTU]); 20358c2ecf20Sopenharmony_ci ip6gre_tunnel_link_md(ign, nt); 20368c2ecf20Sopenharmony_ci ip6gre_tunnel_link(net_generic(net, ip6gre_net_id), nt); 20378c2ecf20Sopenharmony_ci } 20388c2ecf20Sopenharmony_ci return err; 20398c2ecf20Sopenharmony_ci} 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_cistatic struct ip6_tnl * 20428c2ecf20Sopenharmony_ciip6gre_changelink_common(struct net_device *dev, struct nlattr *tb[], 20438c2ecf20Sopenharmony_ci struct nlattr *data[], struct __ip6_tnl_parm *p_p, 20448c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 20458c2ecf20Sopenharmony_ci{ 20468c2ecf20Sopenharmony_ci struct ip6_tnl *t, *nt = netdev_priv(dev); 20478c2ecf20Sopenharmony_ci struct net *net = nt->net; 20488c2ecf20Sopenharmony_ci struct ip6gre_net *ign = net_generic(net, ip6gre_net_id); 20498c2ecf20Sopenharmony_ci struct ip_tunnel_encap ipencap; 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci if (dev == ign->fb_tunnel_dev) 20528c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci if (ip6gre_netlink_encap_parms(data, &ipencap)) { 20558c2ecf20Sopenharmony_ci int err = ip6_tnl_encap_setup(nt, &ipencap); 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_ci if (err < 0) 20588c2ecf20Sopenharmony_ci return ERR_PTR(err); 20598c2ecf20Sopenharmony_ci } 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci ip6gre_netlink_parms(data, p_p); 20628c2ecf20Sopenharmony_ci 20638c2ecf20Sopenharmony_ci t = ip6gre_tunnel_locate(net, p_p, 0); 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci if (t) { 20668c2ecf20Sopenharmony_ci if (t->dev != dev) 20678c2ecf20Sopenharmony_ci return ERR_PTR(-EEXIST); 20688c2ecf20Sopenharmony_ci } else { 20698c2ecf20Sopenharmony_ci t = nt; 20708c2ecf20Sopenharmony_ci } 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci return t; 20738c2ecf20Sopenharmony_ci} 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_cistatic int ip6gre_changelink(struct net_device *dev, struct nlattr *tb[], 20768c2ecf20Sopenharmony_ci struct nlattr *data[], 20778c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 20788c2ecf20Sopenharmony_ci{ 20798c2ecf20Sopenharmony_ci struct ip6_tnl *t = netdev_priv(dev); 20808c2ecf20Sopenharmony_ci struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id); 20818c2ecf20Sopenharmony_ci struct __ip6_tnl_parm p; 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_ci t = ip6gre_changelink_common(dev, tb, data, &p, extack); 20848c2ecf20Sopenharmony_ci if (IS_ERR(t)) 20858c2ecf20Sopenharmony_ci return PTR_ERR(t); 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci ip6gre_tunnel_unlink_md(ign, t); 20888c2ecf20Sopenharmony_ci ip6gre_tunnel_unlink(ign, t); 20898c2ecf20Sopenharmony_ci ip6gre_tnl_change(t, &p, !tb[IFLA_MTU]); 20908c2ecf20Sopenharmony_ci ip6gre_tunnel_link_md(ign, t); 20918c2ecf20Sopenharmony_ci ip6gre_tunnel_link(ign, t); 20928c2ecf20Sopenharmony_ci return 0; 20938c2ecf20Sopenharmony_ci} 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_cistatic void ip6gre_dellink(struct net_device *dev, struct list_head *head) 20968c2ecf20Sopenharmony_ci{ 20978c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 20988c2ecf20Sopenharmony_ci struct ip6gre_net *ign = net_generic(net, ip6gre_net_id); 20998c2ecf20Sopenharmony_ci 21008c2ecf20Sopenharmony_ci if (dev != ign->fb_tunnel_dev) 21018c2ecf20Sopenharmony_ci unregister_netdevice_queue(dev, head); 21028c2ecf20Sopenharmony_ci} 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_cistatic size_t ip6gre_get_size(const struct net_device *dev) 21058c2ecf20Sopenharmony_ci{ 21068c2ecf20Sopenharmony_ci return 21078c2ecf20Sopenharmony_ci /* IFLA_GRE_LINK */ 21088c2ecf20Sopenharmony_ci nla_total_size(4) + 21098c2ecf20Sopenharmony_ci /* IFLA_GRE_IFLAGS */ 21108c2ecf20Sopenharmony_ci nla_total_size(2) + 21118c2ecf20Sopenharmony_ci /* IFLA_GRE_OFLAGS */ 21128c2ecf20Sopenharmony_ci nla_total_size(2) + 21138c2ecf20Sopenharmony_ci /* IFLA_GRE_IKEY */ 21148c2ecf20Sopenharmony_ci nla_total_size(4) + 21158c2ecf20Sopenharmony_ci /* IFLA_GRE_OKEY */ 21168c2ecf20Sopenharmony_ci nla_total_size(4) + 21178c2ecf20Sopenharmony_ci /* IFLA_GRE_LOCAL */ 21188c2ecf20Sopenharmony_ci nla_total_size(sizeof(struct in6_addr)) + 21198c2ecf20Sopenharmony_ci /* IFLA_GRE_REMOTE */ 21208c2ecf20Sopenharmony_ci nla_total_size(sizeof(struct in6_addr)) + 21218c2ecf20Sopenharmony_ci /* IFLA_GRE_TTL */ 21228c2ecf20Sopenharmony_ci nla_total_size(1) + 21238c2ecf20Sopenharmony_ci /* IFLA_GRE_ENCAP_LIMIT */ 21248c2ecf20Sopenharmony_ci nla_total_size(1) + 21258c2ecf20Sopenharmony_ci /* IFLA_GRE_FLOWINFO */ 21268c2ecf20Sopenharmony_ci nla_total_size(4) + 21278c2ecf20Sopenharmony_ci /* IFLA_GRE_FLAGS */ 21288c2ecf20Sopenharmony_ci nla_total_size(4) + 21298c2ecf20Sopenharmony_ci /* IFLA_GRE_ENCAP_TYPE */ 21308c2ecf20Sopenharmony_ci nla_total_size(2) + 21318c2ecf20Sopenharmony_ci /* IFLA_GRE_ENCAP_FLAGS */ 21328c2ecf20Sopenharmony_ci nla_total_size(2) + 21338c2ecf20Sopenharmony_ci /* IFLA_GRE_ENCAP_SPORT */ 21348c2ecf20Sopenharmony_ci nla_total_size(2) + 21358c2ecf20Sopenharmony_ci /* IFLA_GRE_ENCAP_DPORT */ 21368c2ecf20Sopenharmony_ci nla_total_size(2) + 21378c2ecf20Sopenharmony_ci /* IFLA_GRE_COLLECT_METADATA */ 21388c2ecf20Sopenharmony_ci nla_total_size(0) + 21398c2ecf20Sopenharmony_ci /* IFLA_GRE_FWMARK */ 21408c2ecf20Sopenharmony_ci nla_total_size(4) + 21418c2ecf20Sopenharmony_ci /* IFLA_GRE_ERSPAN_INDEX */ 21428c2ecf20Sopenharmony_ci nla_total_size(4) + 21438c2ecf20Sopenharmony_ci 0; 21448c2ecf20Sopenharmony_ci} 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_cistatic int ip6gre_fill_info(struct sk_buff *skb, const struct net_device *dev) 21478c2ecf20Sopenharmony_ci{ 21488c2ecf20Sopenharmony_ci struct ip6_tnl *t = netdev_priv(dev); 21498c2ecf20Sopenharmony_ci struct __ip6_tnl_parm *p = &t->parms; 21508c2ecf20Sopenharmony_ci __be16 o_flags = p->o_flags; 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci if (p->erspan_ver == 1 || p->erspan_ver == 2) { 21538c2ecf20Sopenharmony_ci if (!p->collect_md) 21548c2ecf20Sopenharmony_ci o_flags |= TUNNEL_KEY; 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_ci if (nla_put_u8(skb, IFLA_GRE_ERSPAN_VER, p->erspan_ver)) 21578c2ecf20Sopenharmony_ci goto nla_put_failure; 21588c2ecf20Sopenharmony_ci 21598c2ecf20Sopenharmony_ci if (p->erspan_ver == 1) { 21608c2ecf20Sopenharmony_ci if (nla_put_u32(skb, IFLA_GRE_ERSPAN_INDEX, p->index)) 21618c2ecf20Sopenharmony_ci goto nla_put_failure; 21628c2ecf20Sopenharmony_ci } else { 21638c2ecf20Sopenharmony_ci if (nla_put_u8(skb, IFLA_GRE_ERSPAN_DIR, p->dir)) 21648c2ecf20Sopenharmony_ci goto nla_put_failure; 21658c2ecf20Sopenharmony_ci if (nla_put_u16(skb, IFLA_GRE_ERSPAN_HWID, p->hwid)) 21668c2ecf20Sopenharmony_ci goto nla_put_failure; 21678c2ecf20Sopenharmony_ci } 21688c2ecf20Sopenharmony_ci } 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci if (nla_put_u32(skb, IFLA_GRE_LINK, p->link) || 21718c2ecf20Sopenharmony_ci nla_put_be16(skb, IFLA_GRE_IFLAGS, 21728c2ecf20Sopenharmony_ci gre_tnl_flags_to_gre_flags(p->i_flags)) || 21738c2ecf20Sopenharmony_ci nla_put_be16(skb, IFLA_GRE_OFLAGS, 21748c2ecf20Sopenharmony_ci gre_tnl_flags_to_gre_flags(o_flags)) || 21758c2ecf20Sopenharmony_ci nla_put_be32(skb, IFLA_GRE_IKEY, p->i_key) || 21768c2ecf20Sopenharmony_ci nla_put_be32(skb, IFLA_GRE_OKEY, p->o_key) || 21778c2ecf20Sopenharmony_ci nla_put_in6_addr(skb, IFLA_GRE_LOCAL, &p->laddr) || 21788c2ecf20Sopenharmony_ci nla_put_in6_addr(skb, IFLA_GRE_REMOTE, &p->raddr) || 21798c2ecf20Sopenharmony_ci nla_put_u8(skb, IFLA_GRE_TTL, p->hop_limit) || 21808c2ecf20Sopenharmony_ci nla_put_u8(skb, IFLA_GRE_ENCAP_LIMIT, p->encap_limit) || 21818c2ecf20Sopenharmony_ci nla_put_be32(skb, IFLA_GRE_FLOWINFO, p->flowinfo) || 21828c2ecf20Sopenharmony_ci nla_put_u32(skb, IFLA_GRE_FLAGS, p->flags) || 21838c2ecf20Sopenharmony_ci nla_put_u32(skb, IFLA_GRE_FWMARK, p->fwmark)) 21848c2ecf20Sopenharmony_ci goto nla_put_failure; 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci if (nla_put_u16(skb, IFLA_GRE_ENCAP_TYPE, 21878c2ecf20Sopenharmony_ci t->encap.type) || 21888c2ecf20Sopenharmony_ci nla_put_be16(skb, IFLA_GRE_ENCAP_SPORT, 21898c2ecf20Sopenharmony_ci t->encap.sport) || 21908c2ecf20Sopenharmony_ci nla_put_be16(skb, IFLA_GRE_ENCAP_DPORT, 21918c2ecf20Sopenharmony_ci t->encap.dport) || 21928c2ecf20Sopenharmony_ci nla_put_u16(skb, IFLA_GRE_ENCAP_FLAGS, 21938c2ecf20Sopenharmony_ci t->encap.flags)) 21948c2ecf20Sopenharmony_ci goto nla_put_failure; 21958c2ecf20Sopenharmony_ci 21968c2ecf20Sopenharmony_ci if (p->collect_md) { 21978c2ecf20Sopenharmony_ci if (nla_put_flag(skb, IFLA_GRE_COLLECT_METADATA)) 21988c2ecf20Sopenharmony_ci goto nla_put_failure; 21998c2ecf20Sopenharmony_ci } 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_ci return 0; 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_cinla_put_failure: 22048c2ecf20Sopenharmony_ci return -EMSGSIZE; 22058c2ecf20Sopenharmony_ci} 22068c2ecf20Sopenharmony_ci 22078c2ecf20Sopenharmony_cistatic const struct nla_policy ip6gre_policy[IFLA_GRE_MAX + 1] = { 22088c2ecf20Sopenharmony_ci [IFLA_GRE_LINK] = { .type = NLA_U32 }, 22098c2ecf20Sopenharmony_ci [IFLA_GRE_IFLAGS] = { .type = NLA_U16 }, 22108c2ecf20Sopenharmony_ci [IFLA_GRE_OFLAGS] = { .type = NLA_U16 }, 22118c2ecf20Sopenharmony_ci [IFLA_GRE_IKEY] = { .type = NLA_U32 }, 22128c2ecf20Sopenharmony_ci [IFLA_GRE_OKEY] = { .type = NLA_U32 }, 22138c2ecf20Sopenharmony_ci [IFLA_GRE_LOCAL] = { .len = sizeof_field(struct ipv6hdr, saddr) }, 22148c2ecf20Sopenharmony_ci [IFLA_GRE_REMOTE] = { .len = sizeof_field(struct ipv6hdr, daddr) }, 22158c2ecf20Sopenharmony_ci [IFLA_GRE_TTL] = { .type = NLA_U8 }, 22168c2ecf20Sopenharmony_ci [IFLA_GRE_ENCAP_LIMIT] = { .type = NLA_U8 }, 22178c2ecf20Sopenharmony_ci [IFLA_GRE_FLOWINFO] = { .type = NLA_U32 }, 22188c2ecf20Sopenharmony_ci [IFLA_GRE_FLAGS] = { .type = NLA_U32 }, 22198c2ecf20Sopenharmony_ci [IFLA_GRE_ENCAP_TYPE] = { .type = NLA_U16 }, 22208c2ecf20Sopenharmony_ci [IFLA_GRE_ENCAP_FLAGS] = { .type = NLA_U16 }, 22218c2ecf20Sopenharmony_ci [IFLA_GRE_ENCAP_SPORT] = { .type = NLA_U16 }, 22228c2ecf20Sopenharmony_ci [IFLA_GRE_ENCAP_DPORT] = { .type = NLA_U16 }, 22238c2ecf20Sopenharmony_ci [IFLA_GRE_COLLECT_METADATA] = { .type = NLA_FLAG }, 22248c2ecf20Sopenharmony_ci [IFLA_GRE_FWMARK] = { .type = NLA_U32 }, 22258c2ecf20Sopenharmony_ci [IFLA_GRE_ERSPAN_INDEX] = { .type = NLA_U32 }, 22268c2ecf20Sopenharmony_ci [IFLA_GRE_ERSPAN_VER] = { .type = NLA_U8 }, 22278c2ecf20Sopenharmony_ci [IFLA_GRE_ERSPAN_DIR] = { .type = NLA_U8 }, 22288c2ecf20Sopenharmony_ci [IFLA_GRE_ERSPAN_HWID] = { .type = NLA_U16 }, 22298c2ecf20Sopenharmony_ci}; 22308c2ecf20Sopenharmony_ci 22318c2ecf20Sopenharmony_cistatic void ip6erspan_tap_setup(struct net_device *dev) 22328c2ecf20Sopenharmony_ci{ 22338c2ecf20Sopenharmony_ci ether_setup(dev); 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_ci dev->max_mtu = 0; 22368c2ecf20Sopenharmony_ci dev->netdev_ops = &ip6erspan_netdev_ops; 22378c2ecf20Sopenharmony_ci dev->needs_free_netdev = true; 22388c2ecf20Sopenharmony_ci dev->priv_destructor = ip6gre_dev_free; 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_ci dev->priv_flags &= ~IFF_TX_SKB_SHARING; 22418c2ecf20Sopenharmony_ci dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; 22428c2ecf20Sopenharmony_ci netif_keep_dst(dev); 22438c2ecf20Sopenharmony_ci} 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_cistatic int ip6erspan_newlink(struct net *src_net, struct net_device *dev, 22468c2ecf20Sopenharmony_ci struct nlattr *tb[], struct nlattr *data[], 22478c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 22488c2ecf20Sopenharmony_ci{ 22498c2ecf20Sopenharmony_ci struct ip6_tnl *nt = netdev_priv(dev); 22508c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 22518c2ecf20Sopenharmony_ci struct ip6gre_net *ign; 22528c2ecf20Sopenharmony_ci int err; 22538c2ecf20Sopenharmony_ci 22548c2ecf20Sopenharmony_ci ip6gre_netlink_parms(data, &nt->parms); 22558c2ecf20Sopenharmony_ci ip6erspan_set_version(data, &nt->parms); 22568c2ecf20Sopenharmony_ci ign = net_generic(net, ip6gre_net_id); 22578c2ecf20Sopenharmony_ci 22588c2ecf20Sopenharmony_ci if (nt->parms.collect_md) { 22598c2ecf20Sopenharmony_ci if (rtnl_dereference(ign->collect_md_tun_erspan)) 22608c2ecf20Sopenharmony_ci return -EEXIST; 22618c2ecf20Sopenharmony_ci } else { 22628c2ecf20Sopenharmony_ci if (ip6gre_tunnel_find(net, &nt->parms, dev->type)) 22638c2ecf20Sopenharmony_ci return -EEXIST; 22648c2ecf20Sopenharmony_ci } 22658c2ecf20Sopenharmony_ci 22668c2ecf20Sopenharmony_ci err = ip6gre_newlink_common(src_net, dev, tb, data, extack); 22678c2ecf20Sopenharmony_ci if (!err) { 22688c2ecf20Sopenharmony_ci ip6erspan_tnl_link_config(nt, !tb[IFLA_MTU]); 22698c2ecf20Sopenharmony_ci ip6erspan_tunnel_link_md(ign, nt); 22708c2ecf20Sopenharmony_ci ip6gre_tunnel_link(net_generic(net, ip6gre_net_id), nt); 22718c2ecf20Sopenharmony_ci } 22728c2ecf20Sopenharmony_ci return err; 22738c2ecf20Sopenharmony_ci} 22748c2ecf20Sopenharmony_ci 22758c2ecf20Sopenharmony_cistatic void ip6erspan_tnl_link_config(struct ip6_tnl *t, int set_mtu) 22768c2ecf20Sopenharmony_ci{ 22778c2ecf20Sopenharmony_ci ip6gre_tnl_link_config_common(t); 22788c2ecf20Sopenharmony_ci ip6gre_tnl_link_config_route(t, set_mtu, ip6erspan_calc_hlen(t)); 22798c2ecf20Sopenharmony_ci} 22808c2ecf20Sopenharmony_ci 22818c2ecf20Sopenharmony_cistatic int ip6erspan_tnl_change(struct ip6_tnl *t, 22828c2ecf20Sopenharmony_ci const struct __ip6_tnl_parm *p, int set_mtu) 22838c2ecf20Sopenharmony_ci{ 22848c2ecf20Sopenharmony_ci ip6gre_tnl_copy_tnl_parm(t, p); 22858c2ecf20Sopenharmony_ci ip6erspan_tnl_link_config(t, set_mtu); 22868c2ecf20Sopenharmony_ci return 0; 22878c2ecf20Sopenharmony_ci} 22888c2ecf20Sopenharmony_ci 22898c2ecf20Sopenharmony_cistatic int ip6erspan_changelink(struct net_device *dev, struct nlattr *tb[], 22908c2ecf20Sopenharmony_ci struct nlattr *data[], 22918c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 22928c2ecf20Sopenharmony_ci{ 22938c2ecf20Sopenharmony_ci struct ip6gre_net *ign = net_generic(dev_net(dev), ip6gre_net_id); 22948c2ecf20Sopenharmony_ci struct __ip6_tnl_parm p; 22958c2ecf20Sopenharmony_ci struct ip6_tnl *t; 22968c2ecf20Sopenharmony_ci 22978c2ecf20Sopenharmony_ci t = ip6gre_changelink_common(dev, tb, data, &p, extack); 22988c2ecf20Sopenharmony_ci if (IS_ERR(t)) 22998c2ecf20Sopenharmony_ci return PTR_ERR(t); 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_ci ip6erspan_set_version(data, &p); 23028c2ecf20Sopenharmony_ci ip6gre_tunnel_unlink_md(ign, t); 23038c2ecf20Sopenharmony_ci ip6gre_tunnel_unlink(ign, t); 23048c2ecf20Sopenharmony_ci ip6erspan_tnl_change(t, &p, !tb[IFLA_MTU]); 23058c2ecf20Sopenharmony_ci ip6erspan_tunnel_link_md(ign, t); 23068c2ecf20Sopenharmony_ci ip6gre_tunnel_link(ign, t); 23078c2ecf20Sopenharmony_ci return 0; 23088c2ecf20Sopenharmony_ci} 23098c2ecf20Sopenharmony_ci 23108c2ecf20Sopenharmony_cistatic struct rtnl_link_ops ip6gre_link_ops __read_mostly = { 23118c2ecf20Sopenharmony_ci .kind = "ip6gre", 23128c2ecf20Sopenharmony_ci .maxtype = IFLA_GRE_MAX, 23138c2ecf20Sopenharmony_ci .policy = ip6gre_policy, 23148c2ecf20Sopenharmony_ci .priv_size = sizeof(struct ip6_tnl), 23158c2ecf20Sopenharmony_ci .setup = ip6gre_tunnel_setup, 23168c2ecf20Sopenharmony_ci .validate = ip6gre_tunnel_validate, 23178c2ecf20Sopenharmony_ci .newlink = ip6gre_newlink, 23188c2ecf20Sopenharmony_ci .changelink = ip6gre_changelink, 23198c2ecf20Sopenharmony_ci .dellink = ip6gre_dellink, 23208c2ecf20Sopenharmony_ci .get_size = ip6gre_get_size, 23218c2ecf20Sopenharmony_ci .fill_info = ip6gre_fill_info, 23228c2ecf20Sopenharmony_ci .get_link_net = ip6_tnl_get_link_net, 23238c2ecf20Sopenharmony_ci}; 23248c2ecf20Sopenharmony_ci 23258c2ecf20Sopenharmony_cistatic struct rtnl_link_ops ip6gre_tap_ops __read_mostly = { 23268c2ecf20Sopenharmony_ci .kind = "ip6gretap", 23278c2ecf20Sopenharmony_ci .maxtype = IFLA_GRE_MAX, 23288c2ecf20Sopenharmony_ci .policy = ip6gre_policy, 23298c2ecf20Sopenharmony_ci .priv_size = sizeof(struct ip6_tnl), 23308c2ecf20Sopenharmony_ci .setup = ip6gre_tap_setup, 23318c2ecf20Sopenharmony_ci .validate = ip6gre_tap_validate, 23328c2ecf20Sopenharmony_ci .newlink = ip6gre_newlink, 23338c2ecf20Sopenharmony_ci .changelink = ip6gre_changelink, 23348c2ecf20Sopenharmony_ci .get_size = ip6gre_get_size, 23358c2ecf20Sopenharmony_ci .fill_info = ip6gre_fill_info, 23368c2ecf20Sopenharmony_ci .get_link_net = ip6_tnl_get_link_net, 23378c2ecf20Sopenharmony_ci}; 23388c2ecf20Sopenharmony_ci 23398c2ecf20Sopenharmony_cistatic struct rtnl_link_ops ip6erspan_tap_ops __read_mostly = { 23408c2ecf20Sopenharmony_ci .kind = "ip6erspan", 23418c2ecf20Sopenharmony_ci .maxtype = IFLA_GRE_MAX, 23428c2ecf20Sopenharmony_ci .policy = ip6gre_policy, 23438c2ecf20Sopenharmony_ci .priv_size = sizeof(struct ip6_tnl), 23448c2ecf20Sopenharmony_ci .setup = ip6erspan_tap_setup, 23458c2ecf20Sopenharmony_ci .validate = ip6erspan_tap_validate, 23468c2ecf20Sopenharmony_ci .newlink = ip6erspan_newlink, 23478c2ecf20Sopenharmony_ci .changelink = ip6erspan_changelink, 23488c2ecf20Sopenharmony_ci .get_size = ip6gre_get_size, 23498c2ecf20Sopenharmony_ci .fill_info = ip6gre_fill_info, 23508c2ecf20Sopenharmony_ci .get_link_net = ip6_tnl_get_link_net, 23518c2ecf20Sopenharmony_ci}; 23528c2ecf20Sopenharmony_ci 23538c2ecf20Sopenharmony_ci/* 23548c2ecf20Sopenharmony_ci * And now the modules code and kernel interface. 23558c2ecf20Sopenharmony_ci */ 23568c2ecf20Sopenharmony_ci 23578c2ecf20Sopenharmony_cistatic int __init ip6gre_init(void) 23588c2ecf20Sopenharmony_ci{ 23598c2ecf20Sopenharmony_ci int err; 23608c2ecf20Sopenharmony_ci 23618c2ecf20Sopenharmony_ci pr_info("GRE over IPv6 tunneling driver\n"); 23628c2ecf20Sopenharmony_ci 23638c2ecf20Sopenharmony_ci err = register_pernet_device(&ip6gre_net_ops); 23648c2ecf20Sopenharmony_ci if (err < 0) 23658c2ecf20Sopenharmony_ci return err; 23668c2ecf20Sopenharmony_ci 23678c2ecf20Sopenharmony_ci err = inet6_add_protocol(&ip6gre_protocol, IPPROTO_GRE); 23688c2ecf20Sopenharmony_ci if (err < 0) { 23698c2ecf20Sopenharmony_ci pr_info("%s: can't add protocol\n", __func__); 23708c2ecf20Sopenharmony_ci goto add_proto_failed; 23718c2ecf20Sopenharmony_ci } 23728c2ecf20Sopenharmony_ci 23738c2ecf20Sopenharmony_ci err = rtnl_link_register(&ip6gre_link_ops); 23748c2ecf20Sopenharmony_ci if (err < 0) 23758c2ecf20Sopenharmony_ci goto rtnl_link_failed; 23768c2ecf20Sopenharmony_ci 23778c2ecf20Sopenharmony_ci err = rtnl_link_register(&ip6gre_tap_ops); 23788c2ecf20Sopenharmony_ci if (err < 0) 23798c2ecf20Sopenharmony_ci goto tap_ops_failed; 23808c2ecf20Sopenharmony_ci 23818c2ecf20Sopenharmony_ci err = rtnl_link_register(&ip6erspan_tap_ops); 23828c2ecf20Sopenharmony_ci if (err < 0) 23838c2ecf20Sopenharmony_ci goto erspan_link_failed; 23848c2ecf20Sopenharmony_ci 23858c2ecf20Sopenharmony_ciout: 23868c2ecf20Sopenharmony_ci return err; 23878c2ecf20Sopenharmony_ci 23888c2ecf20Sopenharmony_cierspan_link_failed: 23898c2ecf20Sopenharmony_ci rtnl_link_unregister(&ip6gre_tap_ops); 23908c2ecf20Sopenharmony_citap_ops_failed: 23918c2ecf20Sopenharmony_ci rtnl_link_unregister(&ip6gre_link_ops); 23928c2ecf20Sopenharmony_cirtnl_link_failed: 23938c2ecf20Sopenharmony_ci inet6_del_protocol(&ip6gre_protocol, IPPROTO_GRE); 23948c2ecf20Sopenharmony_ciadd_proto_failed: 23958c2ecf20Sopenharmony_ci unregister_pernet_device(&ip6gre_net_ops); 23968c2ecf20Sopenharmony_ci goto out; 23978c2ecf20Sopenharmony_ci} 23988c2ecf20Sopenharmony_ci 23998c2ecf20Sopenharmony_cistatic void __exit ip6gre_fini(void) 24008c2ecf20Sopenharmony_ci{ 24018c2ecf20Sopenharmony_ci rtnl_link_unregister(&ip6gre_tap_ops); 24028c2ecf20Sopenharmony_ci rtnl_link_unregister(&ip6gre_link_ops); 24038c2ecf20Sopenharmony_ci rtnl_link_unregister(&ip6erspan_tap_ops); 24048c2ecf20Sopenharmony_ci inet6_del_protocol(&ip6gre_protocol, IPPROTO_GRE); 24058c2ecf20Sopenharmony_ci unregister_pernet_device(&ip6gre_net_ops); 24068c2ecf20Sopenharmony_ci} 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_cimodule_init(ip6gre_init); 24098c2ecf20Sopenharmony_cimodule_exit(ip6gre_fini); 24108c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 24118c2ecf20Sopenharmony_ciMODULE_AUTHOR("D. Kozlov (xeb@mail.ru)"); 24128c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GRE over IPv6 tunneling device"); 24138c2ecf20Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("ip6gre"); 24148c2ecf20Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("ip6gretap"); 24158c2ecf20Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("ip6erspan"); 24168c2ecf20Sopenharmony_ciMODULE_ALIAS_NETDEV("ip6gre0"); 2417