linux/net/ipv6/output_core.c
<<
>>
Prefs
   1/*
   2 * IPv6 library code, needed by static components when full IPv6 support is
   3 * not configured or static.  These functions are needed by GSO/GRO implementation.
   4 */
   5#include <linux/export.h>
   6#include <net/ip.h>
   7#include <net/ipv6.h>
   8#include <net/ip6_fib.h>
   9#include <net/addrconf.h>
  10#include <net/secure_seq.h>
  11#include <linux/netfilter.h>
  12
  13static u32 __ipv6_select_ident(struct net *net, u32 hashrnd,
  14                               const struct in6_addr *dst,
  15                               const struct in6_addr *src)
  16{
  17        u32 hash, id;
  18
  19        hash = __ipv6_addr_jhash(dst, hashrnd);
  20        hash = __ipv6_addr_jhash(src, hash);
  21        hash ^= net_hash_mix(net);
  22
  23        /* Treat id of 0 as unset and if we get 0 back from ip_idents_reserve,
  24         * set the hight order instead thus minimizing possible future
  25         * collisions.
  26         */
  27        id = ip_idents_reserve(hash, 1);
  28        if (unlikely(!id))
  29                id = 1 << 31;
  30
  31        return id;
  32}
  33
  34/* This function exists only for tap drivers that must support broken
  35 * clients requesting UFO without specifying an IPv6 fragment ID.
  36 *
  37 * This is similar to ipv6_select_ident() but we use an independent hash
  38 * seed to limit information leakage.
  39 *
  40 * The network header must be set before calling this.
  41 */
  42void ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb)
  43{
  44        static u32 ip6_proxy_idents_hashrnd __read_mostly;
  45        struct in6_addr buf[2];
  46        struct in6_addr *addrs;
  47        u32 id;
  48
  49        addrs = skb_header_pointer(skb,
  50                                   skb_network_offset(skb) +
  51                                   offsetof(struct ipv6hdr, saddr),
  52                                   sizeof(buf), buf);
  53        if (!addrs)
  54                return;
  55
  56        net_get_random_once(&ip6_proxy_idents_hashrnd,
  57                            sizeof(ip6_proxy_idents_hashrnd));
  58
  59        id = __ipv6_select_ident(net, ip6_proxy_idents_hashrnd,
  60                                 &addrs[1], &addrs[0]);
  61        skb_shinfo(skb)->ip6_frag_id = htonl(id);
  62}
  63EXPORT_SYMBOL_GPL(ipv6_proxy_select_ident);
  64
  65__be32 ipv6_select_ident(struct net *net,
  66                         const struct in6_addr *daddr,
  67                         const struct in6_addr *saddr)
  68{
  69        static u32 ip6_idents_hashrnd __read_mostly;
  70        u32 id;
  71
  72        net_get_random_once(&ip6_idents_hashrnd, sizeof(ip6_idents_hashrnd));
  73
  74        id = __ipv6_select_ident(net, ip6_idents_hashrnd, daddr, saddr);
  75        return htonl(id);
  76}
  77EXPORT_SYMBOL(ipv6_select_ident);
  78
  79int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
  80{
  81        unsigned int offset = sizeof(struct ipv6hdr);
  82        unsigned int packet_len = skb_tail_pointer(skb) -
  83                skb_network_header(skb);
  84        int found_rhdr = 0;
  85        *nexthdr = &ipv6_hdr(skb)->nexthdr;
  86
  87        while (offset <= packet_len) {
  88                struct ipv6_opt_hdr *exthdr;
  89
  90                switch (**nexthdr) {
  91
  92                case NEXTHDR_HOP:
  93                        break;
  94                case NEXTHDR_ROUTING:
  95                        found_rhdr = 1;
  96                        break;
  97                case NEXTHDR_DEST:
  98#if IS_ENABLED(CONFIG_IPV6_MIP6)
  99                        if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0)
 100                                break;
 101#endif
 102                        if (found_rhdr)
 103                                return offset;
 104                        break;
 105                default:
 106                        return offset;
 107                }
 108
 109                if (offset + sizeof(struct ipv6_opt_hdr) > packet_len)
 110                        return -EINVAL;
 111
 112                exthdr = (struct ipv6_opt_hdr *)(skb_network_header(skb) +
 113                                                 offset);
 114                offset += ipv6_optlen(exthdr);
 115                if (offset > IPV6_MAXPLEN)
 116                        return -EINVAL;
 117                *nexthdr = &exthdr->nexthdr;
 118        }
 119
 120        return -EINVAL;
 121}
 122EXPORT_SYMBOL(ip6_find_1stfragopt);
 123
 124#if IS_ENABLED(CONFIG_IPV6)
 125int ip6_dst_hoplimit(struct dst_entry *dst)
 126{
 127        int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
 128        if (hoplimit == 0) {
 129                struct net_device *dev = dst->dev;
 130                struct inet6_dev *idev;
 131
 132                rcu_read_lock();
 133                idev = __in6_dev_get(dev);
 134                if (idev)
 135                        hoplimit = idev->cnf.hop_limit;
 136                else
 137                        hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
 138                rcu_read_unlock();
 139        }
 140        return hoplimit;
 141}
 142EXPORT_SYMBOL(ip6_dst_hoplimit);
 143#endif
 144
 145int __ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
 146{
 147        int len;
 148
 149        len = skb->len - sizeof(struct ipv6hdr);
 150        if (len > IPV6_MAXPLEN)
 151                len = 0;
 152        ipv6_hdr(skb)->payload_len = htons(len);
 153        IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
 154
 155        /* if egress device is enslaved to an L3 master device pass the
 156         * skb to its handler for processing
 157         */
 158        skb = l3mdev_ip6_out(sk, skb);
 159        if (unlikely(!skb))
 160                return 0;
 161
 162        skb->protocol = htons(ETH_P_IPV6);
 163
 164        return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
 165                       net, sk, skb, NULL, skb_dst(skb)->dev,
 166                       dst_output);
 167}
 168EXPORT_SYMBOL_GPL(__ip6_local_out);
 169
 170int ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
 171{
 172        int err;
 173
 174        err = __ip6_local_out(net, sk, skb);
 175        if (likely(err == 1))
 176                err = dst_output(net, sk, skb);
 177
 178        return err;
 179}
 180EXPORT_SYMBOL_GPL(ip6_local_out);
 181