cregit-Linux how code gets into the kernel

Release 4.11 net/ipv6/ip6_tunnel.c

Directory: net/ipv6
/*
 *      IPv6 tunneling device
 *      Linux INET6 implementation
 *
 *      Authors:
 *      Ville Nuorvala          <vnuorval@tcs.hut.fi>
 *      Yasuyuki Kozakai        <kozakai@linux-ipv6.org>
 *
 *      Based on:
 *      linux/net/ipv6/sit.c and linux/net/ipv4/ipip.c
 *
 *      RFC 2473
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 *
 */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/module.h>
#include <linux/capability.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/sockios.h>
#include <linux/icmp.h>
#include <linux/if.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/net.h>
#include <linux/in6.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/icmpv6.h>
#include <linux/init.h>
#include <linux/route.h>
#include <linux/rtnetlink.h>
#include <linux/netfilter_ipv6.h>
#include <linux/slab.h>
#include <linux/hash.h>
#include <linux/etherdevice.h>

#include <linux/uaccess.h>
#include <linux/atomic.h>

#include <net/icmp.h>
#include <net/ip.h>
#include <net/ip_tunnels.h>
#include <net/ipv6.h>
#include <net/ip6_route.h>
#include <net/addrconf.h>
#include <net/ip6_tunnel.h>
#include <net/xfrm.h>
#include <net/dsfield.h>
#include <net/inet_ecn.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
#include <net/dst_metadata.h>

MODULE_AUTHOR("Ville Nuorvala");
MODULE_DESCRIPTION("IPv6 tunneling device");
MODULE_LICENSE("GPL");
MODULE_ALIAS_RTNL_LINK("ip6tnl");
MODULE_ALIAS_NETDEV("ip6tnl0");


#define IP6_TUNNEL_HASH_SIZE_SHIFT  5

#define IP6_TUNNEL_HASH_SIZE (1 << IP6_TUNNEL_HASH_SIZE_SHIFT)


static bool log_ecn_error = true;
module_param(log_ecn_error, bool, 0644);
MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");


static u32 HASH(const struct in6_addr *addr1, const struct in6_addr *addr2) { u32 hash = ipv6_addr_hash(addr1) ^ ipv6_addr_hash(addr2); return hash_32(hash, IP6_TUNNEL_HASH_SIZE_SHIFT); }

Contributors

PersonTokensPropCommitsCommitProp
Eric Dumazet3589.74%133.33%
Ville Nuorvala37.69%133.33%
Jiri Kosina12.56%133.33%
Total39100.00%3100.00%

static int ip6_tnl_dev_init(struct net_device *dev); static void ip6_tnl_dev_setup(struct net_device *dev); static struct rtnl_link_ops ip6_link_ops __read_mostly; static unsigned int ip6_tnl_net_id __read_mostly; struct ip6_tnl_net { /* the IPv6 tunnel fallback device */ struct net_device *fb_tnl_dev; /* lists for storing tunnels in use */ struct ip6_tnl __rcu *tnls_r_l[IP6_TUNNEL_HASH_SIZE]; struct ip6_tnl __rcu *tnls_wc[1]; struct ip6_tnl __rcu **tnls[2]; struct ip6_tnl __rcu *collect_md_tun; };
static struct net_device_stats *ip6_get_stats(struct net_device *dev) { struct pcpu_sw_netstats tmp, sum = { 0 }; int i; for_each_possible_cpu(i) { unsigned int start; const struct pcpu_sw_netstats *tstats = per_cpu_ptr(dev->tstats, i); do { start = u64_stats_fetch_begin_irq(&tstats->syncp); tmp.rx_packets = tstats->rx_packets; tmp.rx_bytes = tstats->rx_bytes; tmp.tx_packets = tstats->tx_packets; tmp.tx_bytes = tstats->tx_bytes; } while (u64_stats_fetch_retry_irq(&tstats->syncp, start)); sum.rx_packets += tmp.rx_packets; sum.rx_bytes += tmp.rx_bytes; sum.tx_packets += tmp.tx_packets; sum.tx_bytes += tmp.tx_bytes; } dev->stats.rx_packets = sum.rx_packets; dev->stats.rx_bytes = sum.rx_bytes; dev->stats.tx_packets = sum.tx_packets; dev->stats.tx_bytes = sum.tx_bytes; return &dev->stats; }

Contributors

PersonTokensPropCommitsCommitProp
Eric Dumazet11360.43%125.00%
Li RongQing7238.50%250.00%
Eric W. Biedermann21.07%125.00%
Total187100.00%4100.00%

/** * ip6_tnl_lookup - fetch tunnel matching the end-point addresses * @remote: the address of the tunnel exit-point * @local: the address of the tunnel entry-point * * Return: * tunnel matching given end-points if found, * else fallback tunnel if its device is up, * else %NULL **/ #define for_each_ip6_tunnel_rcu(start) \ for (t = rcu_dereference(start); t; t = rcu_dereference(t->next))
static struct ip6_tnl * ip6_tnl_lookup(struct net *net, const struct in6_addr *remote, const struct in6_addr *local) { unsigned int hash = HASH(remote, local); struct ip6_tnl *t; struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); struct in6_addr any; for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) { if (ipv6_addr_equal(local, &t->parms.laddr) && ipv6_addr_equal(remote, &t->parms.raddr) && (t->dev->flags & IFF_UP)) return t; } memset(&any, 0, sizeof(any)); hash = HASH(&any, local); for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) { if (ipv6_addr_equal(local, &t->parms.laddr) && ipv6_addr_any(&t->parms.raddr) && (t->dev->flags & IFF_UP)) return t; } hash = HASH(remote, &any); for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) { if (ipv6_addr_equal(remote, &t->parms.raddr) && ipv6_addr_any(&t->parms.laddr) && (t->dev->flags & IFF_UP)) return t; } t = rcu_dereference(ip6n->collect_md_tun); if (t) return t; t = rcu_dereference(ip6n->tnls_wc[0]); if (t && (t->dev->flags & IFF_UP)) return t; return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Steffen Klassert10338.29%17.69%
Ville Nuorvala9334.57%17.69%
Pavel Emelyanov197.06%215.38%
Vadim Fedorenko186.69%17.69%
Eric Dumazet165.95%430.77%
Alexei Starovoitov165.95%17.69%
Hideaki Yoshifuji / 吉藤英明31.12%215.38%
Yasuyuki Kozakai10.37%17.69%
Total269100.00%13100.00%

/** * ip6_tnl_bucket - get head of list matching given tunnel parameters * @p: parameters containing tunnel end-points * * Description: * ip6_tnl_bucket() returns the head of the list matching the * &struct in6_addr entries laddr and raddr in @p. * * Return: head of IPv6 tunnel list **/
static struct ip6_tnl __rcu ** ip6_tnl_bucket(struct ip6_tnl_net *ip6n, const struct __ip6_tnl_parm *p) { const struct in6_addr *remote = &p->raddr; const struct in6_addr *local = &p->laddr; unsigned int h = 0; int prio = 0; if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) { prio = 1; h = HASH(remote, local); } return &ip6n->tnls[prio][h]; }

Contributors

PersonTokensPropCommitsCommitProp
Ville Nuorvala8084.21%111.11%
Pavel Emelyanov77.37%222.22%
Eric Dumazet66.32%444.44%
Yasuyuki Kozakai11.05%111.11%
Dmitry Kozlov11.05%111.11%
Total95100.00%9100.00%

/** * ip6_tnl_link - add tunnel to hash table * @t: tunnel to be added **/
static void ip6_tnl_link(struct ip6_tnl_net *ip6n, struct ip6_tnl *t) { struct ip6_tnl __rcu **tp = ip6_tnl_bucket(ip6n, &t->parms); if (t->parms.collect_md) rcu_assign_pointer(ip6n->collect_md_tun, t); rcu_assign_pointer(t->next , rtnl_dereference(*tp)); rcu_assign_pointer(*tp, t); }

Contributors

PersonTokensPropCommitsCommitProp
Ville Nuorvala3447.89%114.29%
Alexei Starovoitov1723.94%114.29%
Eric Dumazet1115.49%342.86%
Pavel Emelyanov79.86%114.29%
Yasuyuki Kozakai22.82%114.29%
Total71100.00%7100.00%

/** * ip6_tnl_unlink - remove tunnel from hash table * @t: tunnel to be removed **/
static void ip6_tnl_unlink(struct ip6_tnl_net *ip6n, struct ip6_tnl *t) { struct ip6_tnl __rcu **tp; struct ip6_tnl *iter; if (t->parms.collect_md) rcu_assign_pointer(ip6n->collect_md_tun, NULL); for (tp = ip6_tnl_bucket(ip6n, &t->parms); (iter = rtnl_dereference(*tp)) != NULL; tp = &iter->next) { if (t == iter) { rcu_assign_pointer(*tp, t->next); break; } } }

Contributors

PersonTokensPropCommitsCommitProp
Ville Nuorvala5454.55%116.67%
Eric Dumazet1919.19%233.33%
Alexei Starovoitov1717.17%116.67%
Pavel Emelyanov77.07%116.67%
Yasuyuki Kozakai22.02%116.67%
Total99100.00%6100.00%


static void ip6_dev_free(struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); gro_cells_destroy(&t->gro_cells); dst_cache_destroy(&t->dst_cache); free_percpu(dev->tstats); free_netdev(dev); }

Contributors

PersonTokensPropCommitsCommitProp
Eric Dumazet2346.94%125.00%
Martin KaFai Lau1428.57%125.00%
Tom Herbert816.33%125.00%
Paolo Abeni48.16%125.00%
Total49100.00%4100.00%


static int ip6_tnl_create2(struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); struct net *net = dev_net(dev); struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); int err; t = netdev_priv(dev); dev->rtnl_link_ops = &ip6_link_ops; err = register_netdevice(dev); if (err < 0) goto out; strcpy(t->parms.name, dev->name); dev_hold(dev); ip6_tnl_link(ip6n, t); return 0; out: return err; }

Contributors

PersonTokensPropCommitsCommitProp
Nicolas Dichtel10293.58%150.00%
Thadeu Lima de Souza Cascardo76.42%150.00%
Total109100.00%2100.00%

/** * ip6_tnl_create - create a new tunnel * @p: tunnel parameters * @pt: pointer to new tunnel * * Description: * Create tunnel matching given parameters. * * Return: * created tunnel or error pointer **/
static struct ip6_tnl *ip6_tnl_create(struct net *net, struct __ip6_tnl_parm *p) { struct net_device *dev; struct ip6_tnl *t; char name[IFNAMSIZ]; int err = -ENOMEM; if (p->name[0]) strlcpy(name, p->name, IFNAMSIZ); else sprintf(name, "ip6tnl%%d"); dev = alloc_netdev(sizeof(*t), name, NET_NAME_UNKNOWN, ip6_tnl_dev_setup); if (!dev) goto failed; dev_net_set(dev, net); t = netdev_priv(dev); t->parms = *p; t->net = dev_net(dev); err = ip6_tnl_create2(dev); if (err < 0) goto failed_free; return t; failed_free: ip6_dev_free(dev); failed: return ERR_PTR(err); }

Contributors

PersonTokensPropCommitsCommitProp
Ville Nuorvala9963.06%317.65%
Pavel Emelyanov1912.10%423.53%
Nicolas Dichtel1710.83%317.65%
Eric Dumazet106.37%15.88%
Noriaki Takamiya42.55%15.88%
Patrick McHardy31.91%15.88%
Tom Gundersen21.27%15.88%
Dmitry Kozlov10.64%15.88%
Yasuyuki Kozakai10.64%15.88%
Ian Morris10.64%15.88%
Total157100.00%17100.00%

/** * ip6_tnl_locate - find or create tunnel matching given parameters * @p: tunnel parameters * @create: != 0 if allowed to create new tunnel if no match found * * Description: * ip6_tnl_locate() first tries to locate an existing tunnel * based on @parms. If this is unsuccessful, but @create is set a new * tunnel device is created and registered for use. * * Return: * matching tunnel or error pointer **/
static struct ip6_tnl *ip6_tnl_locate(struct net *net, struct __ip6_tnl_parm *p, int create) { const struct in6_addr *remote = &p->raddr; const struct in6_addr *local = &p->laddr; struct ip6_tnl __rcu **tp; struct ip6_tnl *t; struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); for (tp = ip6_tnl_bucket(ip6n, p); (t = rtnl_dereference(*tp)) != NULL; tp = &t->next) { if (ipv6_addr_equal(local, &t->parms.laddr) && ipv6_addr_equal(remote, &t->parms.raddr)) { if (create) return ERR_PTR(-EEXIST); return t; } } if (!create) return ERR_PTR(-ENODEV); return ip6_tnl_create(net, p); }

Contributors

PersonTokensPropCommitsCommitProp
Ville Nuorvala9559.01%220.00%
Eric Dumazet2213.66%220.00%
Pavel Emelyanov2113.04%110.00%
Nicolas Dichtel106.21%110.00%
Steffen Klassert84.97%110.00%
Hideaki Yoshifuji / 吉藤英明21.24%110.00%
Yasuyuki Kozakai21.24%110.00%
Dmitry Kozlov10.62%110.00%
Total161100.00%10100.00%

/** * ip6_tnl_dev_uninit - tunnel device uninitializer * @dev: the device to be destroyed * * Description: * ip6_tnl_dev_uninit() removes tunnel from its list **/
static void ip6_tnl_dev_uninit(struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); struct net *net = t->net; struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); if (dev == ip6n->fb_tnl_dev) RCU_INIT_POINTER(ip6n->tnls_wc[0], NULL); else ip6_tnl_unlink(ip6n, t); dst_cache_reset(&t->dst_cache); dev_put(dev); }

Contributors

PersonTokensPropCommitsCommitProp
Ville Nuorvala4453.01%325.00%
Pavel Emelyanov2530.12%325.00%
Paolo Abeni44.82%18.33%
Patrick McHardy33.61%18.33%
Nicolas Dichtel33.61%18.33%
Yasuyuki Kozakai22.41%18.33%
Stephen Hemminger11.20%18.33%
Eric Dumazet11.20%18.33%
Total83100.00%12100.00%

/** * parse_tvl_tnl_enc_lim - handle encapsulation limit option * @skb: received socket buffer * * Return: * 0 if none was found, * else index to encapsulation limit **/
__u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw) { const struct ipv6hdr *ipv6h = (const struct ipv6hdr *)raw; unsigned int nhoff = raw - skb->data; unsigned int off = nhoff + sizeof(*ipv6h); u8 next, nexthdr = ipv6h->nexthdr; while (ipv6_ext_hdr(nexthdr) && nexthdr != NEXTHDR_NONE) { struct ipv6_opt_hdr *hdr; u16 optlen; if (!pskb_may_pull(skb, off + sizeof(*hdr))) break; hdr = (struct ipv6_opt_hdr *)(skb->data + off); if (nexthdr == NEXTHDR_FRAGMENT) { struct frag_hdr *frag_hdr = (struct frag_hdr *) hdr; if (frag_hdr->frag_off) break; optlen = 8; } else if (nexthdr == NEXTHDR_AUTH) { optlen = (hdr->hdrlen + 2) << 2; } else { optlen = ipv6_optlen(hdr); } /* cache hdr->nexthdr, since pskb_may_pull() might * invalidate hdr */ next = hdr->nexthdr; if (nexthdr == NEXTHDR_DEST) { u16 i = 2; /* Remember : hdr is no longer valid at this point. */ if (!pskb_may_pull(skb, off + optlen)) break; while (1) { struct ipv6_tlv_tnl_enc_lim *tel; /* No more room for encapsulation limit */ if (i + sizeof(*tel) > optlen) break; tel = (struct ipv6_tlv_tnl_enc_lim *)(skb->data + off + i); /* return index of option if found and valid */ if (tel->type == IPV6_TLV_TNL_ENCAP_LIMIT && tel->length == 1) return i + off - nhoff; /* else jump to next option */ if (tel->type) i += tel->length + 2; else i++; } } nexthdr = next; off += optlen; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Ville Nuorvala23878.81%120.00%
Eric Dumazet6120.20%240.00%
Dan Carpenter20.66%120.00%
Dmitry Kozlov10.33%120.00%
Total302100.00%5100.00%

EXPORT_SYMBOL(ip6_tnl_parse_tlv_enc_lim); /** * ip6_tnl_err - tunnel error handler * * Description: * ip6_tnl_err() should handle errors in the tunnel according * to the specifications in RFC 2473. **/
static int ip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt, u8 *type, u8 *code, int *msg, __u32 *info, int offset) { const struct ipv6hdr *ipv6h = (const struct ipv6hdr *) skb->data; struct ip6_tnl *t; int rel_msg = 0; u8 rel_type = ICMPV6_DEST_UNREACH; u8 rel_code = ICMPV6_ADDR_UNREACH; u8 tproto; __u32 rel_info = 0; __u16 len; int err = -ENOENT; /* If the packet doesn't contain the original IPv6 header we are in trouble since we might need the source address for further processing of the error. */ rcu_read_lock(); t = ip6_tnl_lookup(dev_net(skb->dev), &ipv6h->daddr, &ipv6h->saddr); if (!t) goto out; tproto = ACCESS_ONCE(t->parms.proto); if (tproto != ipproto && tproto != 0) goto out; err = 0; switch (*type) { __u32 teli; struct ipv6_tlv_tnl_enc_lim *tel; __u32 mtu; case ICMPV6_DEST_UNREACH: net_dbg_ratelimited("%s: Path to destination invalid or inactive!\n", t->parms.name); rel_msg = 1; break; case ICMPV6_TIME_EXCEED: if ((*code) == ICMPV6_EXC_HOPLIMIT) { net_dbg_ratelimited("%s: Too small hop limit or routing loop in tunnel!\n", t->parms.name); rel_msg = 1; } break; case ICMPV6_PARAMPROB: teli = 0; if ((*code) == ICMPV6_HDR_FIELD) teli = ip6_tnl_parse_tlv_enc_lim(skb, skb->data); if (teli && teli == *info - 2) { tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli]; if (tel->encap_limit == 0) { net_dbg_ratelimited("%s: Too small encapsulation limit or routing loop in tunnel!\n", t->parms.name); rel_msg = 1; } } else { net_dbg_ratelimited("%s: Recipient unable to parse tunneled packet!\n", t->parms.name); } break; case ICMPV6_PKT_TOOBIG: mtu = *info - offset; if (mtu < IPV6_MIN_MTU) mtu = IPV6_MIN_MTU; t->dev->mtu = mtu; len = sizeof(*ipv6h) + ntohs(ipv6h->payload_len); if (len > mtu) { rel_type = ICMPV6_PKT_TOOBIG; rel_code = 0; rel_info = mtu; rel_msg = 1; } break; } *type = rel_type; *code = rel_code; *info = rel_info; *msg = rel_msg; out: rcu_read_unlock(); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Ville Nuorvala28168.37%417.39%
Yasuyuki Kozakai6315.33%313.04%
Alexey Andriyanov133.16%14.35%
Herbert Xu112.68%14.35%
Ian Morris92.19%28.70%
Pavel Emelyanov81.95%28.70%
Eric Dumazet61.46%28.70%
Joe Perches51.22%14.35%
Brian Haley40.97%14.35%
Matt Bennett40.97%14.35%
Al Viro40.97%28.70%
Hideaki Yoshifuji / 吉藤英明20.49%28.70%
Dmitry Kozlov10.24%14.35%
Total411100.00%23100.00%


static int ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, u8 type, u8 code, int offset, __be32 info) { int rel_msg = 0; u8 rel_type = type; u8 rel_code = code; __u32 rel_info = ntohl(info); int err; struct sk_buff *skb2; const struct iphdr *eiph; struct rtable *rt; struct flowi4 fl4; err = ip6_tnl_err(skb, IPPROTO_IPIP, opt, &rel_type, &rel_code, &rel_msg, &rel_info, offset); if (err < 0) return err; if (rel_msg == 0) return 0; switch (rel_type) { case ICMPV6_DEST_UNREACH: if (rel_code != ICMPV6_ADDR_UNREACH) return 0; rel_type = ICMP_DEST_UNREACH; rel_code = ICMP_HOST_UNREACH; break; case ICMPV6_PKT_TOOBIG: if (rel_code != 0) return 0; rel_type = ICMP_DEST_UNREACH; rel_code = ICMP_FRAG_NEEDED; break; case NDISC_REDIRECT: rel_type = ICMP_REDIRECT; rel_code = ICMP_REDIR_HOST; default: return 0; } if (!pskb_may_pull(skb, offset + sizeof(struct iphdr))) return 0; skb2 = skb_clone(skb, GFP_ATOMIC); if (!skb2) return 0; skb_dst_drop(skb2); skb_pull(skb2, offset); skb_reset_network_header(skb2); eiph = ip_hdr(skb2); /* Try to guess incoming interface */ rt = ip_route_output_ports(dev_net(skb->dev), &fl4, NULL, eiph->saddr, 0, 0, 0, IPPROTO_IPIP, RT_TOS(eiph->tos), 0); if (IS_ERR(rt)) goto out; skb2->dev = rt->dst.dev; /* route "incoming" packet */ if (rt->rt_flags & RTCF_LOCAL) { ip_rt_put(rt); rt = NULL; rt = ip_route_output_ports(dev_net(skb->dev), &fl4, NULL, eiph->daddr, eiph->saddr, 0, 0, IPPROTO_IPIP, RT_TOS(eiph->tos), 0); if (IS_ERR(rt) || rt->dst.dev->type != ARPHRD_TUNNEL) { if (!IS_ERR(rt)) ip_rt_put(rt); goto out; } skb_dst_set(skb2, &rt->dst); } else { ip_rt_put(rt); if (ip_route_input(skb2, eiph->daddr, eiph->saddr, eiph->tos, skb2->dev) || skb_dst(skb2)->dev->type != ARPHRD_TUNNEL) goto out; } /* change mtu on this route */ if (rel_type == ICMP_DEST_UNREACH && rel_code == ICMP_FRAG_NEEDED) { if (rel_info > dst_mtu(skb_dst(skb2))) goto out; skb_dst(skb2)->ops->update_pmtu(skb_dst(skb2), NULL, skb2, rel_info); } if (rel_type == ICMP_REDIRECT) skb_dst(skb2)->ops->redirect(skb_dst(skb2), NULL, skb2); icmp_send(skb2, rel_type, rel_code, htonl(rel_info)); out: kfree_skb(skb2); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Yasuyuki Kozakai33861.79%420.00%
David S. Miller11821.57%525.00%
Ville Nuorvala498.96%15.00%
Eric Dumazet183.29%210.00%
Al Viro71.28%15.00%
Arnaldo Carvalho de Melo61.10%210.00%
Denis V. Lunev50.91%210.00%
Brian Haley40.73%15.00%
Herbert Xu10.18%15.00%
Pavel Emelyanov10.18%15.00%
Total547100.00%20100.00%


static int ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, u8 type, u8 code, int offset, __be32 info) { int rel_msg = 0; u8 rel_type = type; u8 rel_code = code; __u32 rel_info = ntohl(info); int err; err = ip6_tnl_err(skb, IPPROTO_IPV6, opt, &rel_type, &rel_code, &rel_msg, &rel_info, offset); if (err < 0) return err; if (rel_msg && pskb_may_pull(skb, offset + sizeof(struct ipv6hdr))) { struct rt6_info *rt; struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); if (!skb2) return 0; skb_dst_drop(skb2); skb_pull(skb2, offset); skb_reset_network_header(skb2); /* Try to guess incoming interface */ rt = rt6_lookup(dev_net(skb->dev), &ipv6_hdr(skb2)->saddr, NULL, 0, 0); if (rt && rt->dst.dev) skb2->dev = rt->dst.dev; icmpv6_send(skb2, rel_type, rel_code, rel_info); ip6_rt_put(rt); kfree_skb(skb2); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Yasuyuki Kozakai19086.36%218.18%
Arnaldo Carvalho de Melo62.73%218.18%
Pavel Emelyanov62.73%19.09%
David S. Miller62.73%19.09%
Brian Haley41.82%19.09%
Al Viro41.82%19.09%
Daniel Lezcano20.91%19.09%
Américo Wang10.45%19.09%
Eric Dumazet10.45%19.09%
Total220100.00%11100.00%


static int ip4ip6_dscp_ecn_decapsulate(const struct ip6_tnl *t, const struct ipv6hdr *ipv6h, struct sk_buff *skb) { __u8 dsfield = ipv6_get_dsfield(ipv6h) & ~INET_ECN_MASK; if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY) ipv4_change_dsfield(ip_hdr(skb), INET_ECN_MASK, dsfield); return IP6_ECN_decapsulate(ipv6h, skb); }

Contributors

PersonTokensPropCommitsCommitProp
Yasuyuki Kozakai5179.69%125.00%
Nicolas Dichtel69.38%125.00%
Arnaldo Carvalho de Melo57.81%125.00%
Eric Dumazet23.12%125.00%
Total64100.00%4100.00%


static int ip6ip6_dscp_ecn_decapsulate(const struct ip6_tnl *t, const struct ipv6hdr *ipv6h, struct sk_buff *skb) { if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY) ipv6_copy_dscp(ipv6_get_dsfield(ipv6h), ipv6_hdr(skb)); return IP6_ECN_decapsulate(ipv6h, skb); }

Contributors

PersonTokensPropCommitsCommitProp
Yasuyuki Kozakai3972.22%228.57%
Nicolas Dichtel59.26%114.29%
Herbert Xu47.41%228.57%
Arnaldo Carvalho de Melo47.41%114.29%
Eric Dumazet23.70%114.29%
Total54100.00%7100.00%


__u32 ip6_tnl_get_cap(struct ip6_tnl *t, const struct in6_addr *laddr, const struct in6_addr *raddr) { struct __ip6_tnl_parm *p = &t->parms; int ltype = ipv6_addr_type(laddr); int rtype = ipv6_addr_type(raddr); __u32 flags = 0; if (ltype == IPV6_ADDR_ANY || rtype == IPV6_ADDR_ANY) { flags = IP6_TNL_F_CAP_PER_PACKET; } else if (ltype & (IPV6_ADDR_UNICAST|IPV6_ADDR_MULTICAST) && rtype & (IPV6_ADDR_UNICAST|IPV6_ADDR_MULTICAST) && !((ltype|rtype) & IPV6_ADDR_LOOPBACK) && (!((ltype|rtype) & IPV6_ADDR_LINKLOCAL) || p->link)) { if (ltype&IPV6_ADDR_UNICAST) flags |= IP6_TNL_F_CAP_XMIT; if (rtype&IPV6_ADDR_UNICAST) flags |= IP6_TNL_F_CAP_RCV; } return flags; }

Contributors

PersonTokensPropCommitsCommitProp
Ville Nuorvala14099.29%150.00%
Dmitry Kozlov10.71%150.00%
Total141100.00%2100.00%

EXPORT_SYMBOL(ip6_tnl_get_cap); /* called with rcu_read_lock() */
int ip6_tnl_rcv_ctl(struct ip6_tnl *t, const struct in6_addr *laddr, const struct in6_addr *raddr) { struct __ip6_tnl_parm *p = &t->parms; int ret = 0; struct net *net = t->net; if ((p->flags & IP6_TNL_F_CAP_RCV) || ((p->flags & IP6_TNL_F_CAP_PER_PACKET) && (ip6_tnl_get_cap(t, laddr, raddr) & IP6_TNL_F_CAP_RCV))) { struct net_device *ldev = NULL; if (p->link) ldev = dev_get_by_index_rcu(net, p->link); if ((ipv6_addr_is_multicast(laddr) || likely(ipv6_chk_addr(net, laddr, ldev, 0))) && likely(!ipv6_chk_addr(net, raddr, NULL, 0))) ret = 1; } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Ville Nuorvala13388.67%225.00%
Pavel Emelyanov117.33%112.50%
Daniel Lezcano21.33%112.50%
Eric Dumazet10.67%112.50%
Dmitry Kozlov10.67%112.50%
Eric W. Biedermann10.67%112.50%
Nicolas Dichtel10.67%112.50%
Total150100.00%8100.00%

EXPORT_SYMBOL_GPL(ip6_tnl_rcv_ctl);
static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb, const struct tnl_ptk_info *tpi, struct metadata_dst *tun_dst, int (*dscp_ecn_decapsulate)(const struct ip6_tnl *t, const struct ipv6hdr *ipv6h, struct sk_buff *skb), bool log_ecn_err) { struct pcpu_sw_netstats *tstats; const struct ipv6hdr *ipv6h = ipv6_hdr(skb); int err; if ((!(tpi->flags & TUNNEL_CSUM) && (tunnel->parms.i_flags & TUNNEL_CSUM)) || ((tpi->flags & TUNNEL_CSUM) && !(tunnel->parms.i_flags & TUNNEL_CSUM))) { tunnel->dev->stats.rx_crc_errors++; tunnel->dev->stats.rx_errors++; goto drop; } if (tunnel->parms.i_flags & TUNNEL_SEQ) { if (!(tpi->flags & TUNNEL_SEQ) || (tunnel->i_seqno && (s32)(ntohl(tpi->seq) - tunnel->i_seqno) < 0)) { tunnel->dev->stats.rx_fifo_errors++; tunnel->dev->stats.rx_errors++; goto drop; } tunnel->i_seqno = ntohl(tpi->seq) + 1; } skb->protocol = tpi->proto; /* Warning: All skb pointers will be invalidated! */ if (tunnel->dev->type == ARPHRD_ETHER) { if (!pskb_may_pull(skb, ETH_HLEN)) { tunnel->dev->stats.rx_length_errors++; tunnel->dev->stats.rx_errors++; goto drop; } ipv6h = ipv6_hdr(skb); skb->protocol = eth_type_trans(skb, tunnel->dev); skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); } else { skb->dev = tunnel->dev; } skb_reset_network_header(skb); memset(skb->cb, 0, sizeof(struct inet6_skb_parm)); __skb_tunnel_rx(skb, tunnel->dev, tunnel->net); err = dscp_ecn_decapsulate(tunnel, ipv6h, skb); if (unlikely(err)) { if (log_ecn_err) net_info_ratelimited("non-ECT from %pI6 with DS=%#x\n", &ipv6h->saddr, ipv6_get_dsfield(ipv6h)); if (err > 1) { ++tunnel->dev->stats.rx_frame_errors; ++tunnel->dev->stats.rx_errors; goto drop; } } tstats = this_cpu_ptr(tunnel->dev->tstats); u64_stats_update_begin(&tstats->syncp); tstats->rx_packets++; tstats->rx_bytes += skb->len; u64_stats_update_end(&tstats->syncp); skb_scrub_packet(skb, !net_eq(tunnel->net, dev_net(tunnel->dev))); if (tun_dst) skb_dst_set(skb, (struct dst_entry *)tun_dst); gro_cells_receive(&tunnel->gro_cells, skb); return 0; drop: kfree_skb(skb); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Tom Herbert45286.92%110.00%
Yasuyuki Kozakai234.42%220.00%
Alexei Starovoitov163.08%110.00%
Ville Nuorvala122.31%110.00%
Arnaldo Carvalho de Melo81.54%110.00%
Nicolas Dichtel40.77%110.00%
Eric Dumazet30.58%110.00%
Herbert Xu10.19%110.00%
Hideaki Yoshifuji / 吉藤英明10.19%110.00%
Total520100.00%10100.00%


int ip6_tnl_rcv(struct ip6_tnl *t, struct sk_buff *skb, const struct tnl_ptk_info *tpi, struct metadata_dst *tun_dst, bool log_ecn_err) { return __ip6_tnl_rcv(t, skb, tpi, NULL, ip6ip6_dscp_ecn_decapsulate, log_ecn_err); }

Contributors

PersonTokensPropCommitsCommitProp
Tom Herbert45100.00%1100.00%
Total45100.00%1100.00%

EXPORT_SYMBOL(ip6_tnl_rcv); static const struct tnl_ptk_info tpi_v6 = { /* no tunnel info required for ipxip6. */ .proto = htons(ETH_P_IPV6), }; static const struct tnl_ptk_info tpi_v4 = { /* no tunnel info required for ipxip6. */ .proto = htons(ETH_P_IP), };
static int ipxip6_rcv(struct sk_buff *skb, u8 ipproto, const struct tnl_ptk_info *tpi, int (*dscp_ecn_decapsulate)(const struct ip6_tnl *t, const struct ipv6hdr *ipv6h, struct sk_buff *skb)) { struct ip6_tnl *t; const struct ipv6hdr *ipv6h = ipv6_hdr(skb); struct metadata_dst *tun_dst = NULL; int ret = -1; rcu_read_lock(); t = ip6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr, &ipv6h->daddr); if (t) { u8 tproto = ACCESS_ONCE(t->parms.proto); if (tproto != ipproto && tproto != 0) goto drop; if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) goto drop; if (!ip6_tnl_rcv_ctl(t, &ipv6h->daddr, &ipv6h->saddr)) goto drop; if (iptunnel_pull_header(skb, 0, tpi->proto, false)) goto drop; if (t->parms.collect_md) { tun_dst = ipv6_tun_rx_dst(skb, 0, 0, 0); if (!tun_dst) return 0; } ret = __ip6_tnl_rcv(t, skb, tpi, tun_dst, dscp_ecn_decapsulate, log_ecn_error); } rcu_read_unlock(); return ret; drop: rcu_read_unlock(); kfree_skb(skb); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Tom Herbert9136.40%16.25%
Ville Nuorvala4919.60%318.75%
Alexei Starovoitov3915.60%16.25%
Yasuyuki Kozakai166.40%212.50%
Nicolas Dichtel135.20%212.50%
Hideaki Yoshifuji / 吉藤英明124.80%16.25%
Pavel Emelyanov83.20%212.50%
Alexey Andriyanov83.20%16.25%
Eric Dumazet62.40%16.25%
Ian Morris41.60%16.25%
Herbert Xu41.60%16.25%
Total250100.00%16100.00%


static int ip4ip6_rcv(struct sk_buff *skb) { return ipxip6_rcv(skb, IPPROTO_IPIP, &tpi_v4, ip4ip6_dscp_ecn_decapsulate); }

Contributors

PersonTokensPropCommitsCommitProp
Yasuyuki Kozakai2083.33%250.00%
Tom Herbert312.50%125.00%
Nicolas Dichtel14.17%125.00%
Total24100.00%4100.00%


static int ip6ip6_rcv(struct sk_buff *skb) { return ipxip6_rcv(skb, IPPROTO_IPV6, &tpi_v6, ip6ip6_dscp_ecn_decapsulate); }

Contributors

PersonTokensPropCommitsCommitProp
Yasuyuki Kozakai2083.33%266.67%
Tom Herbert416.67%133.33%
Total24100.00%3100.00%

struct ipv6_tel_txoption { struct ipv6_txoptions ops; __u8 dst_opt[8]; };
static void init_tel_txopt(struct ipv6_tel_txoption *opt, __u8 encap_limit) { memset(opt, 0, sizeof(struct ipv6_tel_txoption)); opt->dst_opt[2] = IPV6_TLV_TNL_ENCAP_LIMIT; opt->dst_opt[3] = 1; opt->dst_opt[4] = encap_limit; opt->dst_opt[5] = IPV6_TLV_PADN; opt->dst_opt[6] = 1; opt->ops.dst0opt = (struct ipv6_opt_hdr *) opt->dst_opt; opt->ops.opt_nflen = 8; }

Contributors

PersonTokensPropCommitsCommitProp
Ville Nuorvala95100.00%3100.00%
Total95100.00%3100.00%

/** * ip6_tnl_addr_conflict - compare packet addresses to tunnel's own * @t: the outgoing tunnel device * @hdr: IPv6 header from the incoming packet * * Description: * Avoid trivial tunneling loop by checking that tunnel exit-point * doesn't match source of incoming packet. * * Return: * 1 if conflict, * 0 else **/
static inline bool ip6_tnl_addr_conflict(const struct ip6_tnl *t, const struct ipv6hdr *hdr) { return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr); }

Contributors

PersonTokensPropCommitsCommitProp
Ville Nuorvala3085.71%342.86%
Eric Dumazet38.57%228.57%
Yasuyuki Kozakai12.86%114.29%
Hideaki Yoshifuji / 吉藤英明12.86%114.29%
Total35100.00%7100.00%


int ip6_tnl_xmit_ctl(struct ip6_tnl *t, const struct in6_addr *laddr, const struct in6_addr *raddr) { struct __ip6_tnl_parm *p = &t->parms; int ret = 0; struct net *net = t->net; if ((p->flags & IP6_TNL_F_CAP_XMIT) || ((p->flags & IP6_TNL_F_CAP_PER_PACKET) && (ip6_tnl_get_cap(t, laddr, raddr) & IP6_TNL_F_CAP_XMIT))) { struct net_device *ldev = NULL; rcu_read_lock(); if (p->link) ldev = dev_get_by_index_rcu(net, p->link); if (unlikely(!ipv6_chk_addr(net, laddr, ldev, 0))) pr_warn("%s xmit: Local address not yet configured!\n", p->name); else if (!ipv6_addr_is_multicast(raddr) && unlikely(ipv6_chk_addr(net, raddr, NULL, 0))) pr_warn("%s xmit: Routing loop! Remote address found on this node!\n", p->name); else ret = 1; rcu_read_unlock(); } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Ville Nuorvala11564.97%111.11%
Steffen Klassert3720.90%111.11%
Pavel Emelyanov116.21%111.11%
Eric Dumazet63.39%111.11%
Joe Perches31.69%111.11%
Daniel Lezcano21.13%111.11%
Eric W. Biedermann10.56%111.11%
Nicolas Dichtel10.56%111.11%
Dmitry Kozlov10.56%111.11%
Total177100.00%9100.00%

EXPORT_SYMBOL_GPL(ip6_tnl_xmit_ctl); /** * ip6_tnl_xmit - encapsulate packet and send * @skb: the outgoing socket buffer * @dev: the outgoing tunnel device * @dsfield: dscp code for outer header * @fl6: flow of tunneled packet * @encap_limit: encapsulation limit * @pmtu: Path MTU is stored if packet is too big * @proto: next header value * * Description: * Build new header and do some sanity checks on the packet before sending * it. * * Return: * 0 on success * -1 fail * %-EMSGSIZE message too big. return mtu in this case. **/
int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield, struct flowi6 *fl6, int encap_limit, __u32 *pmtu, __u8 proto) { struct ip6_tnl *t = netdev_priv(dev); struct net *net = t->net; struct net_device_stats *stats = &t->dev->stats; struct ipv6hdr *ipv6h; struct ipv6_tel_txoption opt; struct dst_entry *dst = NULL, *ndst = NULL; struct net_device *tdev; int mtu; unsigned int psh_hlen = sizeof(struct ipv6hdr) + t->encap_hlen; unsigned int max_headroom = psh_hlen; bool use_cache = false; u8 hop_limit; int err = -1; if (t->parms.collect_md) { hop_limit = skb_tunnel_info(skb)->key.ttl; goto route_lookup; } else { hop_limit = t->parms.hop_limit; } /* NBMA tunnel */ if (ipv6_addr_any(&t->parms.raddr)) { if (skb->protocol == htons(ETH_P_IPV6)) { struct in6_addr *addr6; struct neighbour *neigh; int addr_type; if (!skb_dst(skb)) goto tx_err_link_failure; neigh = dst_neigh_lookup(skb_dst(skb), &ipv6_hdr(skb)->daddr); if (!neigh) goto tx_err_link_failure; addr6 = (struct in6_addr *)&neigh->primary_key; addr_type = ipv6_addr_type(addr6); if (addr_type == IPV6_ADDR_ANY) addr6 = &ipv6_hdr(skb)->daddr; memcpy(&fl6->daddr, addr6, sizeof(fl6->daddr)); neigh_release(neigh); } } else if (!(t->parms.flags & (IP6_TNL_F_USE_ORIG_TCLASS | IP6_TNL_F_USE_ORIG_FWMARK))) { /* enable the cache only only if the routing decision does * not depend on the current inner header value */ use_cache = true; } if (use_cache) dst = dst_cache_get(&t->dst_cache); if (!ip6_tnl_xmit_ctl(t, &fl6->saddr, &fl6->daddr)) goto tx_err_link_failure; if (!dst) { route_lookup: dst = ip6_route_output(net, NULL, fl6); if (dst->error) goto tx_err_link_failure; dst = xfrm_lookup(net, dst, flowi6_to_flowi(fl6), NULL, 0); if (IS_ERR(dst)) { err = PTR_ERR(dst); dst = NULL; goto tx_err_link_failure; } if (t->parms.collect_md && ipv6_dev_get_saddr(net, ip6_dst_idev(dst)->dev, &fl6->daddr, 0, &fl6->saddr)) goto tx_err_link_failure; ndst = dst; } tdev = dst->dev; if (tdev == dev) { stats->collisions++; net_warn_ratelimited("%s: Local routing loop detected!\n", t->parms.name); goto tx_err_dst_release; } mtu = dst_mtu(dst) - psh_hlen - t->tun_hlen; if (encap_limit >= 0) { max_headroom += 8; mtu -= 8; } if (mtu < IPV6_MIN_MTU) mtu = IPV6_MIN_MTU; if (skb_dst(skb) && !t->parms.collect_md) skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu); if (skb->len - t->tun_hlen > mtu && !skb_is_gso(skb)) { *pmtu = mtu; err = -EMSGSIZE; goto tx_err_dst_release; } if (t->err_count > 0) { if (time_before(jiffies, t->err_time + IP6TUNNEL_ERR_TIMEO)) { t->err_count--; dst_link_failure(skb); } else { t->err_count = 0; } } skb_scrub_packet(skb, !net_eq(t->net, dev_net(dev))); /* * Okay, now see if we can stuff it in the buffer as-is. */ max_headroom += LL_RESERVED_SPACE(tdev); if (skb_headroom(skb) < max_headroom || skb_shared(skb) || (skb_cloned(skb) && !skb_clone_writable(skb, 0))) { struct sk_buff *new_skb; new_skb = skb_realloc_headroom(skb, max_headroom); if (!new_skb) goto tx_err_dst_release; if (skb->sk) skb_set_owner_w(new_skb, skb->sk); consume_skb(skb); skb = new_skb; } if (t->parms.collect_md) { if (t->encap.type != TUNNEL_ENCAP_NONE) goto tx_err_dst_release; } else { if (use_cache && ndst) dst_cache_set_ip6(&t->dst_cache, ndst, &fl6->saddr); } skb_dst_set(skb, dst); if (encap_limit >= 0) { init_tel_txopt(&opt, encap_limit); ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL, NULL); } /* Calculate max headroom for all the headers and adjust * needed_headroom if necessary. */ max_headroom = LL_RESERVED_SPACE(dst->dev) + sizeof(struct ipv6hdr) + dst->header_len + t->hlen; if (max_headroom > dev->needed_headroom) dev->needed_headroom = max_headroom; err = ip6_tnl_encap(skb, t, &proto, fl6); if (err) return err; skb_push(skb, sizeof(struct ipv6hdr)); skb_reset_network_header(skb); ipv6h = ipv6_hdr(skb); ip6_flow_hdr(ipv6h, INET_ECN_encapsulate(0, dsfield), ip6_make_flowlabel(net, skb, fl6->flowlabel, true, fl6)); ipv6h->hop_limit = hop_limit; ipv6h->nexthdr = proto; ipv6h->saddr = fl6->saddr; ipv6h->daddr = fl6->daddr; ip6tunnel_xmit(NULL, skb, dev); return 0; tx_err_link_failure: stats->tx_carrier_errors++; dst_link_failure(skb); tx_err_dst_release: dst_release(dst); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Ville Nuorvala23323.05%48.89%
Yasuyuki Kozakai14514.34%36.67%
Steffen Klassert14414.24%24.44%
Tom Herbert13613.45%511.11%
Alexei Starovoitov10810.68%12.22%
Paolo Abeni424.15%24.44%
Eric Dumazet403.96%511.11%
David S. Miller393.86%48.89%
Nicolas Dichtel262.57%24.44%
Américo Wang151.48%24.44%
Martin KaFai Lau151.48%12.22%
Alexey Dobriyan121.19%24.44%
Patrick McHardy121.19%12.22%
Herbert Xu90.89%12.22%
Arnaldo Carvalho de Melo80.79%24.44%
Jakub Sitnicki80.79%12.22%
Hideaki Yoshifuji / 吉藤英明60.59%12.22%
Ian Morris50.49%12.22%
Pavel Emelyanov30.30%12.22%
David Lebrun20.20%12.22%
Chuck Lever10.10%12.22%
Daniel Lezcano10.10%12.22%
Joe Perches10.10%12.22%
Total1011100.00%45100.00%

EXPORT_SYMBOL(ip6_tnl_xmit);
static inline int ip4ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); const struct iphdr *iph = ip_hdr(skb); int encap_limit = -1; struct flowi6 fl6; __u8 dsfield; __u32 mtu; u8 tproto; int err; memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); tproto = ACCESS_ONCE(t->parms.proto); if (tproto != IPPROTO_IPIP && tproto != 0) return -1; dsfield = ipv4_get_dsfield(iph); if (t->parms.collect_md) { struct ip_tunnel_info *tun_info; const struct ip_tunnel_key *key; tun_info = skb_tunnel_info(skb); if (unlikely(!tun_info || !(tun_info->mode & IP_TUNNEL_INFO_TX) || ip_tunnel_info_af(tun_info) != AF_INET6)) return -1; key = &tun_info->key; memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_proto = IPPROTO_IPIP; fl6.daddr = key->u.ipv6.dst; fl6.flowlabel = key->label; } else { if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) encap_limit = t->parms.encap_limit; memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6)); fl6.flowi6_proto = IPPROTO_IPIP; if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS) fl6.flowlabel |= htonl((__u32)iph->tos << IPV6_TCLASS_SHIFT) & IPV6_TCLASS_MASK; if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK) fl6.flowi6_mark = skb->mark; } fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL); if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6)) return -1; skb_set_inner_ipproto(skb, IPPROTO_IPIP); err = ip6_tnl_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu, IPPROTO_IPIP); if (err != 0) { /* XXX: send ICMP error even if DF is not set. */ if (err == -EMSGSIZE) icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu)); return -1; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alexei Starovoitov15337.59%17.14%
Yasuyuki Kozakai15036.86%321.43%
Bernie Harris256.14%17.14%
Tom Herbert235.65%214.29%
Eric Dumazet194.67%214.29%
Lorenzo Colitti143.44%17.14%
Alexey Andriyanov133.19%17.14%
David S. Miller51.23%17.14%
Arnaldo Carvalho de Melo30.74%17.14%
Al Viro20.49%17.14%
Total407100.00%14100.00%


static inline int ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); struct ipv6hdr *ipv6h = ipv6_hdr(skb); int encap_limit = -1; __u16 offset; struct flowi6 fl6; __u8 dsfield; __u32 mtu; u8 tproto; int err; tproto = ACCESS_ONCE(t->parms.proto); if ((tproto != IPPROTO_IPV6 && tproto != 0) || ip6_tnl_addr_conflict(t, ipv6h)) return -1; dsfield = ipv6_get_dsfield(ipv6h); if (t->parms.collect_md) { struct ip_tunnel_info *tun_info; const struct ip_tunnel_key *key; tun_info = skb_tunnel_info(skb); if (unlikely(!tun_info || !(tun_info->mode & IP_TUNNEL_INFO_TX) || ip_tunnel_info_af(tun_info) != AF_INET6)) return -1; key = &tun_info->key; memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_proto = IPPROTO_IPV6; fl6.daddr = key->u.ipv6.dst; fl6.flowlabel = key->label; } else { offset = ip6_tnl_parse_tlv_enc_lim(skb, skb_network_header(skb)); /* ip6_tnl_parse_tlv_enc_lim() might have reallocated skb->head */ ipv6h = ipv6_hdr(skb); if (offset > 0) { struct ipv6_tlv_tnl_enc_lim *tel; tel = (void *)&skb_network_header(skb)[offset]; if (tel->encap_limit == 0) { icmpv6_send(skb, ICMPV6_PARAMPROB, ICMPV6_HDR_FIELD, offset + 2); return -1; } encap_limit = tel->encap_limit - 1; } else if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) { encap_limit = t->parms.encap_limit; } memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6)); fl6.flowi6_proto = IPPROTO_IPV6; if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS) fl6.flowlabel |= (*(__be32 *)ipv6h & IPV6_TCLASS_MASK); if (t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) fl6.flowlabel |= ip6_flowlabel(ipv6h); if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK) fl6.flowi6_mark = skb->mark; } fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL); if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6)) return -1; skb_set_inner_ipproto(skb, IPPROTO_IPV6); err = ip6_tnl_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu, IPPROTO_IPV6); if (err != 0) { if (err == -EMSGSIZE) icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); return -1; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Yasuyuki Kozakai23547.86%315.79%
Alexei Starovoitov11523.42%15.26%
Ville Nuorvala275.50%210.53%
Eric Dumazet265.30%210.53%
Tom Herbert234.68%210.53%
Lorenzo Colitti142.85%15.26%
Alexey Andriyanov142.85%15.26%
David S. Miller132.65%15.26%
Arnaldo Carvalho de Melo132.65%210.53%
Eli Cooper61.22%15.26%
Florent Fourcot30.61%15.26%
Dmitry Kozlov10.20%15.26%
Gerrit Renker10.20%15.26%
Total491100.00%19100.00%


static netdev_tx_t ip6_tnl_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); struct net_device_stats *stats = &t->dev->stats; int ret; switch (skb->protocol) { case htons(ETH_P_IP): ret = ip4ip6_tnl_xmit(skb, dev); break; case htons(ETH_P_IPV6): ret = ip6ip6_tnl_xmit(skb, dev); break; default: goto tx_err; } if (ret < 0) goto tx_err; return NETDEV_TX_OK; tx_err: stats->tx_errors++; stats->tx_dropped++; kfree_skb(skb); return NETDEV_TX_OK; }

Contributors

PersonTokensPropCommitsCommitProp
Yasuyuki Kozakai7967.52%222.22%
Ville Nuorvala2924.79%222.22%
Pavel Emelyanov32.56%111.11%
Patrick McHardy21.71%111.11%
Arnaldo Carvalho de Melo21.71%111.11%
Tom Herbert10.85%111.11%
Stephen Hemminger10.85%111.11%
Total117100.00%9100.00%


static void ip6_tnl_link_config(struct ip6_tnl *t) { struct net_device *dev = t->dev; struct __ip6_tnl_parm *p = &t->parms; struct flowi6 *fl6 = &t->fl.u.ip6; int t_hlen; memcpy(dev->dev_addr, &p->laddr, sizeof(struct in6_addr)); memcpy(dev->broadcast, &p->raddr, sizeof(struct in6_addr)); /* Set up flowi template */ fl6->saddr = p->laddr; fl6->daddr = p->raddr; fl6->flowi6_oif = p->link; fl6->flowlabel = 0; if (!(p->flags&IP6_TNL_F_USE_ORIG_TCLASS)) fl6->flowlabel |= IPV6_TCLASS_MASK & p->flowinfo; if (!(p->flags&IP6_TNL_F_USE_ORIG_FLOWLABEL)) fl6->flowlabel |= IPV6_FLOWLABEL_MASK & p->flowinfo; p->flags &= ~(IP6_TNL_F_CAP_XMIT|IP6_TNL_F_CAP_RCV|IP6_TNL_F_CAP_PER_PACKET); p->flags |= ip6_tnl_get_cap(t, &p->laddr, &p->raddr); if (p->flags&IP6_TNL_F_CAP_XMIT && p->flags&IP6_TNL_F_CAP_RCV) dev->flags |= IFF_POINTOPOINT; else dev->flags &= ~IFF_POINTOPOINT; t->tun_hlen = 0; t->hlen = t->encap_hlen + t->tun_hlen; t_hlen = t->hlen + sizeof(struct ipv6hdr); if (p->flags & IP6_TNL_F_CAP_XMIT) { int strict = (ipv6_addr_type(&p->raddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL)); struct rt6_info *rt = rt6_lookup(t->net, &p->raddr, &p->laddr, p->link, strict); if (!rt) return; if (rt->dst.dev) { dev->hard_header_len = rt->dst.dev->hard_header_len + t_hlen; dev->mtu = rt->dst.dev->mtu - t_hlen; if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) dev->mtu -= 8; if (dev->mtu < IPV6_MIN_MTU) dev->mtu = IPV6_MIN_MTU; } ip6_rt_put(rt); } }

Contributors

PersonTokensPropCommitsCommitProp
Ville Nuorvala29376.10%635.29%
Tom Herbert359.09%15.88%
David S. Miller277.01%211.76%
Anders Franzen194.94%15.88%
Nicolas Dichtel30.78%15.88%
Daniel Lezcano20.52%15.88%
Alexey Dobriyan20.52%15.88%
Dmitry Kozlov10.26%15.88%
Américo Wang10.26%15.88%
Ian Morris10.26%15.88%
Yasuyuki Kozakai10.26%15.88%
Total385100.00%17100.00%

/** * ip6_tnl_change - update the tunnel parameters * @t: tunnel to be changed * @p: tunnel configuration parameters * * Description: * ip6_tnl_change() updates the tunnel parameters **/
static int ip6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p) { t->parms.laddr = p->laddr; t->parms.raddr = p->raddr; t->parms.flags = p->flags; t->parms.hop_limit = p->hop_limit; t->parms.encap_limit = p->encap_limit; t->parms.flowinfo = p->flowinfo; t->parms.link = p->link; t->parms.proto = p->proto; dst_cache_reset(&t->dst_cache); ip6_tnl_link_config(t); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Ville Nuorvala7969.91%112.50%
Yasuyuki Kozakai1210.62%225.00%
Gabor Fekete108.85%112.50%
Hugo Santos43.54%112.50%
Paolo Abeni43.54%112.50%
Dmitry Kozlov21.77%112.50%
Alexey Dobriyan21.77%112.50%
Total113100.00%8100.00%


static int ip6_tnl_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p) { struct net *net = t->net; struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); int err; ip6_tnl_unlink(ip6n, t); synchronize_net(); err = ip6_tnl_change(t, p); ip6_tnl_link(ip6n, t); netdev_state_change(t->dev); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Nicolas Dichtel6382.89%266.67%
Dmitry Kozlov1317.11%133.33%
Total76100.00%3100.00%


static int ip6_tnl0_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p) { /* for default tnl0 device allow to change only the proto */ t->parms.proto = p->proto; netdev_state_change(t->dev); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alexey Andriyanov37100.00%1100.00%
Total37100.00%1100.00%


static void ip6_tnl_parm_from_user(struct __ip6_tnl_parm *p, const struct ip6_tnl_parm *u) { p->laddr = u->laddr; p->raddr = u->raddr; p->flags = u->flags; p->hop_limit = u->hop_limit; p->encap_limit = u->encap_limit; p->flowinfo = u->flowinfo; p->link = u->link; p->proto = u->proto; memcpy(p->name, u->name, sizeof(u->name)); }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Kozlov5858.59%150.00%
Nicolas Dichtel4141.41%150.00%
Total99100.00%2100.00%


static void ip6_tnl_parm_to_user(struct ip6_tnl_parm *u, const struct __ip6_tnl_parm *p) { u->laddr = p->laddr; u->raddr = p->raddr; u->flags = p->flags; u->hop_limit = p->hop_limit; u->encap_limit = p->encap_limit; u->flowinfo = p->flowinfo; u->link = p->link; u->proto = p->proto; memcpy(u->name, p->name, sizeof(u->name)); }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Kozlov99100.00%1100.00%
Total99100.00%1100.00%

/** * ip6_tnl_ioctl - configure ipv6 tunnels from userspace * @dev: virtual device associated with tunnel * @ifr: parameters passed from userspace * @cmd: command to be performed * * Description: * ip6_tnl_ioctl() is used for managing IPv6 tunnels * from userspace. * * The possible commands are the following: * %SIOCGETTUNNEL: get tunnel parameters for device * %SIOCADDTUNNEL: add tunnel matching given tunnel parameters * %SIOCCHGTUNNEL: change tunnel parameters to those given * %SIOCDELTUNNEL: delete tunnel * * The fallback device "ip6tnl0", created during module * initialization, can be used for creating other tunnel devices. * * Return: * 0 on success, * %-EFAULT if unable to copy data to or from userspace, * %-EPERM if current process hasn't %CAP_NET_ADMIN set * %-EINVAL if passed tunnel parameters are invalid, * %-EEXIST if changing a tunnel's parameters would cause a conflict * %-ENODEV if attempting to change or delete a nonexisting device **/
static int ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { int err = 0; struct ip6_tnl_parm p; struct __ip6_tnl_parm p1; struct ip6_tnl *t = netdev_priv(dev); struct net *net = t->net; struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); memset(&p1, 0, sizeof(p1)); switch (cmd) { case SIOCGETTUNNEL: if (dev == ip6n->fb_tnl_dev) { if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) { err = -EFAULT; break; } ip6_tnl_parm_from_user(&p1, &p); t = ip6_tnl_locate(net, &p1, 0); if (IS_ERR(t)) t = netdev_priv(dev); } else { memset(&p, 0, sizeof(p)); } ip6_tnl_parm_to_user(&p, &t->parms); if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p))) { err = -EFAULT; } break; case SIOCADDTUNNEL: case SIOCCHGTUNNEL: err = -EPERM; if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) break; err = -EFAULT; if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) break; err = -EINVAL; if (p.proto != IPPROTO_IPV6 && p.proto != IPPROTO_IPIP && p.proto != 0) break; ip6_tnl_parm_from_user(&p1, &p); t = ip6_tnl_locate(net, &p1, cmd == SIOCADDTUNNEL); if (cmd == SIOCCHGTUNNEL) { if (!IS_ERR(t)) { if (t->dev != dev) { err = -EEXIST; break; } } else t = netdev_priv(dev); if (dev == ip6n->fb_tnl_dev) err = ip6_tnl0_update(t, &p1); else err = ip6_tnl_update(t, &p1); } if (!IS_ERR(t)) { err = 0; ip6_tnl_parm_to_user(&p, &t->parms); if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p))) err = -EFAULT; } else { err = PTR_ERR(t); } break; case SIOCDELTUNNEL: err = -EPERM; if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) break; if (dev == ip6n->fb_tnl_dev) { err = -EFAULT; if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) break; err = -ENOENT; ip6_tnl_parm_from_user(&p1, &p); t = ip6_tnl_locate(net, &p1, 0); if (IS_ERR(t)) break; err = -EPERM; if (t->dev == ip6n->fb_tnl_dev) break; dev = t->dev; } err = 0; unregister_netdevice(dev); break; default: err = -EINVAL; } return err; }

Contributors

PersonTokensPropCommitsCommitProp
Ville Nuorvala37164.97%318.75%
Dmitry Kozlov529.11%16.25%
Nicolas Dichtel396.83%318.75%
Pavel Emelyanov335.78%212.50%
Alexey Andriyanov193.33%16.25%
Dan Carpenter162.80%16.25%
Yasuyuki Kozakai162.80%212.50%
Tom Herbert132.28%16.25%
Eric W. Biedermann101.75%16.25%
Stephen Hemminger20.35%16.25%
Total571100.00%16100.00%

/** * ip6_tnl_change_mtu - change mtu manually for tunnel device * @dev: virtual device associated with tunnel * @new_mtu: the new mtu * * Return: * 0 on success, * %-EINVAL if mtu too small **/
int ip6_tnl_change_mtu(struct net_device *dev, int new_mtu) { struct ip6_tnl *tnl = netdev_priv(dev); if (tnl->parms.proto == IPPROTO_IPIP) { if (new_mtu < ETH_MIN_MTU) return -EINVAL; } else { if (new_mtu < IPV6_MIN_MTU) return -EINVAL; } if (new_mtu > 0xFFF8 - dev->hard_header_len) return -EINVAL; dev->mtu = new_mtu; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Oussama Ghorbel4758.02%120.00%
Ville Nuorvala3138.27%120.00%
Tom Herbert11.23%120.00%
Yasuyuki Kozakai11.23%120.00%
Jarod Wilson11.23%120.00%
Total81100.00%5100.00%

EXPORT_SYMBOL(ip6_tnl_change_mtu);
int ip6_tnl_get_iflink(const struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); return t->parms.link; }

Contributors

PersonTokensPropCommitsCommitProp
Nicolas Dichtel28100.00%1100.00%
Total28100.00%1100.00%

EXPORT_SYMBOL(ip6_tnl_get_iflink);
int ip6_tnl_encap_add_ops(const struct ip6_tnl_encap_ops *ops, unsigned int num) { if (num >= MAX_IPTUN_ENCAP_OPS) return -ERANGE; return !cmpxchg((const struct ip6_tnl_encap_ops **) &ip6tun_encaps[num], NULL, ops) ? 0 : -1; }

Contributors

PersonTokensPropCommitsCommitProp
Tom Herbert52100.00%1100.00%
Total52100.00%1100.00%

EXPORT_SYMBOL(ip6_tnl_encap_add_ops);
int ip6_tnl_encap_del_ops(const struct ip6_tnl_encap_ops *ops, unsigned int num) { int ret; if (num >= MAX_IPTUN_ENCAP_OPS) return -ERANGE; ret = (cmpxchg((const struct ip6_tnl_encap_ops **) &ip6tun_encaps[num], ops, NULL) == ops) ? 0 : -1; synchronize_net(); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Tom Herbert65100.00%1100.00%
Total65100.00%1100.00%

EXPORT_SYMBOL(ip6_tnl_encap_del_ops);
int ip6_tnl_encap_setup(struct ip6_tnl *t, struct ip_tunnel_encap *ipencap) { int hlen; memset(&t->encap, 0, sizeof(t->encap)); hlen = ip6_encap_hlen(ipencap); if (hlen < 0) return hlen; t->encap.type = ipencap->type; t->encap.sport = ipencap->sport; t->encap.dport = ipencap->dport; t->encap.flags = ipencap->flags; t->encap_hlen = hlen; t->hlen = t->encap_hlen + t->tun_hlen; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Tom Herbert112100.00%1100.00%
Total112100.00%1100.00%

EXPORT_SYMBOL_GPL(ip6_tnl_encap_setup); static const struct net_device_ops ip6_tnl_netdev_ops = { .ndo_init = ip6_tnl_dev_init, .ndo_uninit = ip6_tnl_dev_uninit, .ndo_start_xmit = ip6_tnl_start_xmit, .ndo_do_ioctl = ip6_tnl_ioctl, .ndo_change_mtu = ip6_tnl_change_mtu, .ndo_get_stats = ip6_get_stats, .ndo_get_iflink = ip6_tnl_get_iflink, }; #define IPXIPX_FEATURES (NETIF_F_SG | \ NETIF_F_FRAGLIST | \ NETIF_F_HIGHDMA | \ NETIF_F_GSO_SOFTWARE | \ NETIF_F_HW_CSUM) /** * ip6_tnl_dev_setup - setup virtual tunnel device * @dev: virtual device associated with tunnel * * Description: * Initialize function pointers and device parameters **/
static void ip6_tnl_dev_setup(struct net_device *dev) { dev->netdev_ops = &ip6_tnl_netdev_ops; dev->destructor = ip6_dev_free; dev->type = ARPHRD_TUNNEL6; dev->flags |= IFF_NOARP; dev->addr_len = sizeof(struct in6_addr); dev->features |= NETIF_F_LLTX; netif_keep_dst(dev); dev->features |= IPXIPX_FEATURES; dev->hw_features |= IPXIPX_FEATURES; /* This perm addr will be used as interface identifier by IPv6 */ dev->addr_assign_type = NET_ADDR_RANDOM; eth_random_addr(dev->perm_addr); }

Contributors

PersonTokensPropCommitsCommitProp
Ville Nuorvala4149.40%325.00%
Tom Herbert1821.69%216.67%
Nicolas Dichtel1416.87%18.33%
Eric Dumazet44.82%216.67%
Stephen Hemminger33.61%18.33%
Pavel Emelyanov11.20%18.33%
Anders Franzen11.20%18.33%
Yasuyuki Kozakai11.20%18.33%
Total83100.00%12100.00%

/** * ip6_tnl_dev_init_gen - general initializer for all tunnel devices * @dev: virtual device associated with tunnel **/
static inline int ip6_tnl_dev_init_gen(struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); int ret; int t_hlen; t->dev = dev; t->net = dev_net(dev); dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); if (!dev->tstats) return -ENOMEM; ret = dst_cache_init(&t->dst_cache, GFP_KERNEL); if (ret) goto free_stats; ret = gro_cells_init(&t->gro_cells, dev); if (ret) goto destroy_dst; t->tun_hlen = 0; t->hlen = t->encap_hlen + t->tun_hlen; t_hlen = t->hlen + sizeof(struct ipv6hdr); dev->type = ARPHRD_TUNNEL6; dev->hard_header_len = LL_MAX_HEADER + t_hlen; dev->mtu = ETH_DATA_LEN - t_hlen; if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) dev->mtu -= 8; dev->min_mtu = ETH_MIN_MTU; dev->max_mtu = 0xFFF8 - dev->hard_header_len; return 0; destroy_dst: dst_cache_destroy(&t->dst_cache); free_stats: free_percpu(dev->tstats); dev->tstats = NULL; return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Tom Herbert11150.45%323.08%
Martin KaFai Lau3013.64%17.69%
Ville Nuorvala2210.00%17.69%
Eric Dumazet209.09%17.69%
Jarod Wilson167.27%17.69%
Nicolas Dichtel94.09%17.69%
Paolo Abeni62.73%17.69%
Patrick McHardy31.36%17.69%
Li RongQing10.45%17.69%
Yasuyuki Kozakai10.45%17.69%
Américo Wang10.45%17.69%
Total220100.00%13100.00%

/** * ip6_tnl_dev_init - initializer for all non fallback tunnel devices * @dev: virtual device associated with tunnel **/
static int ip6_tnl_dev_init(struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); int err = ip6_tnl_dev_init_gen(dev); if (err) return err; ip6_tnl_link_config(t); if (t->parms.collect_md) { dev->features |= NETIF_F_NETNS_LOCAL; netif_keep_dst(dev); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Ville Nuorvala2233.85%116.67%
Alexei Starovoitov2132.31%116.67%
Eric Dumazet1523.08%116.67%
Yasuyuki Kozakai34.62%116.67%
Patrick McHardy34.62%116.67%
Stephen Hemminger11.54%116.67%
Total65100.00%6100.00%

/** * ip6_fb_tnl_dev_init - initializer for fallback tunnel device * @dev: fallback device * * Return: 0 **/
static int __net_init ip6_fb_tnl_dev_init(struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); struct net *net = dev_net(dev); struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); t->parms.proto = IPPROTO_IPV6; dev_hold(dev); rcu_assign_pointer(ip6n->tnls_wc[0], t); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Ville Nuorvala2636.11%218.18%
Pavel Emelyanov2433.33%19.09%
Yasuyuki Kozakai912.50%218.18%
Eric Dumazet811.11%327.27%
Patrick McHardy34.17%19.09%
Alexey Dobriyan11.39%19.09%
Stephen Hemminger11.39%19.09%
Total72100.00%11100.00%


static int ip6_tnl_validate(struct nlattr *tb[], struct nlattr *data[]) { u8 proto; if (!data || !data[IFLA_IPTUN_PROTO]) return 0; proto = nla_get_u8(data[IFLA_IPTUN_PROTO]); if (proto != IPPROTO_IPV6 && proto != IPPROTO_IPIP && proto != 0) return -EINVAL; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Nicolas Dichtel6090.91%375.00%
Susant Sahani69.09%125.00%
Total66100.00%4100.00%


static void ip6_tnl_netlink_parms(struct nlattr *data[], struct __ip6_tnl_parm *parms) { memset(parms, 0, sizeof(*parms)); if (!data) return; if (data[IFLA_IPTUN_LINK]) parms->link = nla_get_u32(data[IFLA_IPTUN_LINK]); if (data[IFLA_IPTUN_LOCAL]) parms->laddr = nla_get_in6_addr(data[IFLA_IPTUN_LOCAL]); if (data[IFLA_IPTUN_REMOTE]) parms->raddr = nla_get_in6_addr(data[IFLA_IPTUN_REMOTE]); if (data[IFLA_IPTUN_TTL]) parms->hop_limit = nla_get_u8(data[IFLA_IPTUN_TTL]); if (data[IFLA_IPTUN_ENCAP_LIMIT]) parms->encap_limit = nla_get_u8(data[IFLA_IPTUN_ENCAP_LIMIT]); if (data[IFLA_IPTUN_FLOWINFO]) parms->flowinfo = nla_get_be32(data[IFLA_IPTUN_FLOWINFO]); if (data[IFLA_IPTUN_FLAGS]) parms->flags = nla_get_u32(data[IFLA_IPTUN_FLAGS]); if (data[IFLA_IPTUN_PROTO]) parms->proto = nla_get_u8(data[IFLA_IPTUN_PROTO]); if (data[IFLA_IPTUN_COLLECT_METADATA]) parms->collect_md = true; }

Contributors

PersonTokensPropCommitsCommitProp
Nicolas Dichtel18290.55%360.00%
Alexei Starovoitov136.47%120.00%
Jiri Benc62.99%120.00%
Total201100.00%5100.00%


static bool ip6_tnl_netlink_encap_parms(struct nlattr *data[], struct ip_tunnel_encap *ipencap) { bool ret = false; memset(ipencap, 0, sizeof(*ipencap)); if (!data) return ret; if (data[IFLA_IPTUN_ENCAP_TYPE]) { ret = true; ipencap->type = nla_get_u16(data[IFLA_IPTUN_ENCAP_TYPE]); } if (data[IFLA_IPTUN_ENCAP_FLAGS]) { ret = true; ipencap->flags = nla_get_u16(data[IFLA_IPTUN_ENCAP_FLAGS]); } if (data[IFLA_IPTUN_ENCAP_SPORT]) { ret = true; ipencap->sport = nla_get_be16(data[IFLA_IPTUN_ENCAP_SPORT]); } if (data[IFLA_IPTUN_ENCAP_DPORT]) { ret = true; ipencap->dport = nla_get_be16(data[IFLA_IPTUN_ENCAP_DPORT]); } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Tom Herbert146100.00%1100.00%
Total146100.00%1100.00%


static int ip6_tnl_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { struct net *net = dev_net(dev); struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); struct ip6_tnl *nt, *t; struct ip_tunnel_encap ipencap; nt = netdev_priv(dev); if (ip6_tnl_netlink_encap_parms(data, &ipencap)) { int err = ip6_tnl_encap_setup(nt, &ipencap); if (err < 0) return err; } ip6_tnl_netlink_parms(data, &nt->parms); if (nt->parms.collect_md) { if (rtnl_dereference(ip6n->collect_md_tun)) return -EEXIST; } else { t = ip6_tnl_locate(net, &nt->parms, 0); if (!IS_ERR(t)) return -EEXIST; } return ip6_tnl_create2(dev); }

Contributors

PersonTokensPropCommitsCommitProp
Nicolas Dichtel9556.21%250.00%
Alexei Starovoitov3822.49%125.00%
Tom Herbert3621.30%125.00%
Total169100.00%4100.00%


static int ip6_tnl_changelink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { struct ip6_tnl *t = netdev_priv(dev); struct __ip6_tnl_parm p; struct net *net = t->net; struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); struct ip_tunnel_encap ipencap; if (dev == ip6n->fb_tnl_dev) return -EINVAL; if (ip6_tnl_netlink_encap_parms(data, &ipencap)) { int err = ip6_tnl_encap_setup(t, &ipencap); if (err < 0) return err; } ip6_tnl_netlink_parms(data, &p); if (p.collect_md) return -EINVAL; t = ip6_tnl_locate(net, &p, 0); if (!IS_ERR(t)) { if (t->dev != dev) return -EEXIST; } else t = netdev_priv(dev); return ip6_tnl_update(t, &p); }

Contributors

PersonTokensPropCommitsCommitProp
Nicolas Dichtel12973.71%360.00%
Tom Herbert3620.57%120.00%
Alexei Starovoitov105.71%120.00%
Total175100.00%5100.00%


static void ip6_tnl_dellink(struct net_device *dev, struct list_head *head) { struct net *net = dev_net(dev); struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); if (dev != ip6n->fb_tnl_dev) unregister_netdevice_queue(dev, head); }

Contributors

PersonTokensPropCommitsCommitProp
Nicolas Dichtel53100.00%1100.00%
Total53100.00%1100.00%


static size_t ip6_tnl_get_size(const struct net_device *dev) { return /* IFLA_IPTUN_LINK */ nla_total_size(4) + /* IFLA_IPTUN_LOCAL */ nla_total_size(sizeof(struct in6_addr)) + /* IFLA_IPTUN_REMOTE */ nla_total_size(sizeof(struct in6_addr)) + /* IFLA_IPTUN_TTL */ nla_total_size(1) + /* IFLA_IPTUN_ENCAP_LIMIT */ nla_total_size(1) + /* IFLA_IPTUN_FLOWINFO */ nla_total_size(4) + /* IFLA_IPTUN_FLAGS */ nla_total_size(4) + /* IFLA_IPTUN_PROTO */ nla_total_size(1) + /* IFLA_IPTUN_ENCAP_TYPE */ nla_total_size(2) + /* IFLA_IPTUN_ENCAP_FLAGS */ nla_total_size(2) + /* IFLA_IPTUN_ENCAP_SPORT */ nla_total_size(2) + /* IFLA_IPTUN_ENCAP_DPORT */ nla_total_size(2) + /* IFLA_IPTUN_COLLECT_METADATA */ nla_total_size(0) + 0; }

Contributors

PersonTokensPropCommitsCommitProp
Nicolas Dichtel7170.30%133.33%
Tom Herbert2423.76%133.33%
Alexei Starovoitov65.94%133.33%
Total101100.00%3100.00%


static int ip6_tnl_fill_info(struct sk_buff *skb, const struct net_device *dev) { struct ip6_tnl *tunnel = netdev_priv(dev); struct __ip6_tnl_parm *parm = &tunnel->parms; if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) || nla_put_in6_addr(skb, IFLA_IPTUN_LOCAL, &parm->laddr) || nla_put_in6_addr(skb, IFLA_IPTUN_REMOTE, &parm->raddr) || nla_put_u8(skb, IFLA_IPTUN_TTL, parm->hop_limit) || nla_put_u8(skb, IFLA_IPTUN_ENCAP_LIMIT, parm->encap_limit) || nla_put_be32(skb, IFLA_IPTUN_FLOWINFO, parm->flowinfo) || nla_put_u32(skb, IFLA_IPTUN_FLAGS, parm->flags) || nla_put_u8(skb, IFLA_IPTUN_PROTO, parm->proto)) goto nla_put_failure; if (nla_put_u16(skb, IFLA_IPTUN_ENCAP_TYPE, tunnel->encap.type) || nla_put_be16(skb, IFLA_IPTUN_ENCAP_SPORT, tunnel->encap.sport) || nla_put_be16(skb, IFLA_IPTUN_ENCAP_DPORT, tunnel->encap.dport) || nla_put_u16(skb, IFLA_IPTUN_ENCAP_FLAGS, tunnel->encap.flags)) goto nla_put_failure; if (parm->collect_md) if (nla_put_flag(skb, IFLA_IPTUN_COLLECT_METADATA)) goto nla_put_failure; return 0; nla_put_failure: return -EMSGSIZE; }

Contributors

PersonTokensPropCommitsCommitProp
Nicolas Dichtel13763.43%342.86%
Tom Herbert5726.39%114.29%
Alexei Starovoitov188.33%114.29%
Jiri Benc20.93%114.29%
Ding Zhi20.93%114.29%
Total216100.00%7100.00%


struct net *ip6_tnl_get_link_net(const struct net_device *dev) { struct ip6_tnl *tunnel = netdev_priv(dev); return tunnel->net; }

Contributors

PersonTokensPropCommitsCommitProp
Nicolas Dichtel28100.00%1100.00%
Total28100.00%1100.00%

EXPORT_SYMBOL(ip6_tnl_get_link_net); static const struct nla_policy ip6_tnl_policy[IFLA_IPTUN_MAX + 1] = { [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, [IFLA_IPTUN_LOCAL] = { .len = sizeof(struct in6_addr) }, [IFLA_IPTUN_REMOTE] = { .len = sizeof(struct in6_addr) }, [IFLA_IPTUN_TTL] = { .type = NLA_U8 }, [IFLA_IPTUN_ENCAP_LIMIT] = { .type = NLA_U8 }, [IFLA_IPTUN_FLOWINFO] = { .type = NLA_U32 }, [IFLA_IPTUN_FLAGS] = { .type = NLA_U32 }, [IFLA_IPTUN_PROTO] = { .type = NLA_U8 }, [IFLA_IPTUN_ENCAP_TYPE] = { .type = NLA_U16 }, [IFLA_IPTUN_ENCAP_FLAGS] = { .type = NLA_U16 }, [IFLA_IPTUN_ENCAP_SPORT] = { .type = NLA_U16 }, [IFLA_IPTUN_ENCAP_DPORT] = { .type = NLA_U16 }, [IFLA_IPTUN_COLLECT_METADATA] = { .type = NLA_FLAG }, }; static struct rtnl_link_ops ip6_link_ops __read_mostly = { .kind = "ip6tnl", .maxtype = IFLA_IPTUN_MAX, .policy = ip6_tnl_policy, .priv_size = sizeof(struct ip6_tnl), .setup = ip6_tnl_dev_setup, .validate = ip6_tnl_validate, .newlink = ip6_tnl_newlink, .changelink = ip6_tnl_changelink, .dellink = ip6_tnl_dellink, .get_size = ip6_tnl_get_size, .fill_info = ip6_tnl_fill_info, .get_link_net = ip6_tnl_get_link_net, }; static struct xfrm6_tunnel ip4ip6_handler __read_mostly = { .handler = ip4ip6_rcv, .err_handler = ip4ip6_err, .priority = 1, }; static struct xfrm6_tunnel ip6ip6_handler __read_mostly = { .handler = ip6ip6_rcv, .err_handler = ip6ip6_err, .priority = 1, };
static void __net_exit ip6_tnl_destroy_tunnels(struct net *net) { struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); struct net_device *dev, *aux; int h; struct ip6_tnl *t; LIST_HEAD(list); for_each_netdev_safe(net, dev, aux) if (dev->rtnl_link_ops == &ip6_link_ops) unregister_netdevice_queue(dev, &list); for (h = 0; h < IP6_TUNNEL_HASH_SIZE; h++) { t = rtnl_dereference(ip6n->tnls_r_l[h]); while (t) { /* If dev is in the same netns, it has already * been added to the list by the previous loop. */ if (!net_eq(dev_net(t->dev), net)) unregister_netdevice_queue(t->dev, &list); t = rtnl_dereference(t->next); } } unregister_netdevice_many(&list); }

Contributors

PersonTokensPropCommitsCommitProp
Nicolas Dichtel6343.75%228.57%
Pavel Emelyanov4833.33%114.29%
Eric Dumazet3121.53%228.57%
Alexey Dobriyan10.69%114.29%
Jiri Kosina10.69%114.29%
Total144100.00%7100.00%


static int __net_init ip6_tnl_init_net(struct net *net) { struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); struct ip6_tnl *t = NULL; int err; ip6n->tnls[0] = ip6n->tnls_wc; ip6n->tnls[1] = ip6n->tnls_r_l; err = -ENOMEM; ip6n->fb_tnl_dev = alloc_netdev(sizeof(struct ip6_tnl), "ip6tnl0", NET_NAME_UNKNOWN, ip6_tnl_dev_setup); if (!ip6n->fb_tnl_dev) goto err_alloc_dev; dev_net_set(ip6n->fb_tnl_dev, net); ip6n->fb_tnl_dev->rtnl_link_ops = &ip6_link_ops; /* FB netdevice is special: we have one, and only one per netns. * Allowing to move it to another netns is clearly unsafe. */ ip6n->fb_tnl_dev->features |= NETIF_F_NETNS_LOCAL; err = ip6_fb_tnl_dev_init(ip6n->fb_tnl_dev); if (err < 0) goto err_register; err = register_netdev(ip6n->fb_tnl_dev); if (err < 0) goto err_register; t = netdev_priv(ip6n->fb_tnl_dev); strcpy(t->parms.name, ip6n->fb_tnl_dev->name); return 0; err_register: ip6_dev_free(ip6n->fb_tnl_dev); err_alloc_dev: return err; }

Contributors

PersonTokensPropCommitsCommitProp
Pavel Emelyanov11961.34%325.00%
Josh Boyer3115.98%18.33%
Nicolas Dichtel189.28%216.67%
Eric Dumazet126.19%18.33%
Eric W. Biedermann52.58%18.33%
Alexey Dobriyan52.58%216.67%
Tom Gundersen21.03%18.33%
Stephen Hemminger21.03%18.33%
Total194100.00%12100.00%


static void __net_exit ip6_tnl_exit_net(struct net *net) { rtnl_lock(); ip6_tnl_destroy_tunnels(net); rtnl_unlock(); }

Contributors

PersonTokensPropCommitsCommitProp
Pavel Emelyanov2191.30%250.00%
Alexey Dobriyan14.35%125.00%
Nicolas Dichtel14.35%125.00%
Total23100.00%4100.00%

static struct pernet_operations ip6_tnl_net_ops = { .init = ip6_tnl_init_net, .exit = ip6_tnl_exit_net, .id = &ip6_tnl_net_id, .size = sizeof(struct ip6_tnl_net), }; /** * ip6_tunnel_init - register protocol and reserve needed resources * * Return: 0 on success **/
static int __init ip6_tunnel_init(void) { int err; err = register_pernet_device(&ip6_tnl_net_ops); if (err < 0) goto out_pernet; err = xfrm6_tunnel_register(&ip4ip6_handler, AF_INET); if (err < 0) { pr_err("%s: can't register ip4ip6\n", __func__); goto out_ip4ip6; } err = xfrm6_tunnel_register(&ip6ip6_handler, AF_INET6); if (err < 0) { pr_err("%s: can't register ip6ip6\n", __func__); goto out_ip6ip6; } err = rtnl_link_register(&ip6_link_ops); if (err < 0) goto rtnl_link_failed; return 0; rtnl_link_failed: xfrm6_tunnel_deregister(&ip6ip6_handler, AF_INET6); out_ip6ip6: xfrm6_tunnel_deregister(&ip4ip6_handler, AF_INET); out_ip4ip6: unregister_pernet_device(&ip6_tnl_net_ops); out_pernet: return err; }

Contributors

PersonTokensPropCommitsCommitProp
Alexey Dobriyan4028.99%110.00%
Nicolas Dichtel2719.57%110.00%
Ville Nuorvala2618.84%220.00%
Yasuyuki Kozakai2316.67%110.00%
Herbert Xu96.52%110.00%
Joe Perches85.80%110.00%
Kazunori Miyazawa32.17%110.00%
Pavel Emelyanov10.72%110.00%
Al Viro10.72%110.00%
Total138100.00%10100.00%

/** * ip6_tunnel_cleanup - free resources and unregister protocol **/
static void __exit ip6_tunnel_cleanup(void) { rtnl_link_unregister(&ip6_link_ops); if (xfrm6_tunnel_deregister(&ip4ip6_handler, AF_INET)) pr_info("%s: can't deregister ip4ip6\n", __func__); if (xfrm6_tunnel_deregister(&ip6ip6_handler, AF_INET6)) pr_info("%s: can't deregister ip6ip6\n", __func__); unregister_pernet_device(&ip6_tnl_net_ops); }

Contributors

PersonTokensPropCommitsCommitProp
Yasuyuki Kozakai1323.64%110.00%
Ville Nuorvala916.36%110.00%
Joe Perches814.55%110.00%
Nicolas Dichtel610.91%110.00%
Pavel Emelyanov59.09%110.00%
Herbert Xu59.09%110.00%
Hideaki Yoshifuji / 吉藤英明47.27%110.00%
Al Viro23.64%110.00%
Kazunori Miyazawa23.64%110.00%
Eric W. Biedermann11.82%110.00%
Total55100.00%10100.00%

module_init(ip6_tunnel_init); module_exit(ip6_tunnel_cleanup);

Overall Contributors

PersonTokensPropCommitsCommitProp
Ville Nuorvala302527.43%136.91%
Tom Herbert160614.56%115.85%
Yasuyuki Kozakai150513.65%73.72%
Nicolas Dichtel148513.46%179.04%
Alexei Starovoitov6075.50%10.53%
Eric Dumazet5655.12%1910.11%
Pavel Emelyanov4414.00%105.32%
Steffen Klassert2972.69%42.13%
Dmitry Kozlov2542.30%10.53%
David S. Miller2081.89%94.79%
Alexey Andriyanov1040.94%10.53%
Li RongQing730.66%21.06%
Alexey Dobriyan650.59%63.19%
Paolo Abeni600.54%21.06%
Martin KaFai Lau590.53%10.53%
Herbert Xu550.50%52.66%
Arnaldo Carvalho de Melo550.50%63.19%
Oussama Ghorbel470.43%10.53%
Stephen Hemminger430.39%52.66%
Eric W. Biedermann350.32%42.13%
Patrick McHardy340.31%42.13%
Hideaki Yoshifuji / 吉藤英明340.31%63.19%
Joe Perches320.29%21.06%
Josh Boyer310.28%10.53%
Lorenzo Colitti280.25%10.53%
Bernie Harris250.23%10.53%
Al Viro200.18%42.13%
Ian Morris200.18%21.06%
Anders Franzen200.18%21.06%
Dan Carpenter180.16%21.06%
Vadim Fedorenko180.16%10.53%
Américo Wang180.16%42.13%
Jarod Wilson170.15%10.53%
Brian Haley120.11%10.53%
Gabor Fekete100.09%10.53%
Tom Gundersen90.08%21.06%
Daniel Lezcano90.08%31.60%
Jiri Benc80.07%21.06%
Jakub Sitnicki80.07%10.53%
Thadeu Lima de Souza Cascardo70.06%10.53%
Susant Sahani60.05%10.53%
Eli Cooper60.05%10.53%
Jiri Kosina60.05%10.53%
Denis V. Lunev50.05%21.06%
Kazunori Miyazawa50.05%10.53%
Matt Bennett40.04%10.53%
Hugo Santos40.04%10.53%
Noriaki Takamiya40.04%10.53%
Florent Fourcot30.03%10.53%
Tejun Heo30.03%10.53%
Pravin B Shelar30.03%10.53%
Randy Dunlap30.03%10.53%
Ding Zhi20.02%10.53%
David Lebrun20.02%10.53%
Chuck Lever10.01%10.53%
Qinghuang Feng10.01%10.53%
Arun Sharma10.01%10.53%
Linus Torvalds10.01%10.53%
Gerrit Renker10.01%10.53%
Adrian Bunk10.01%10.53%
Total11029100.00%188100.00%
Directory: net/ipv6
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.