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