linux/net/ipv6/xfrm6_output.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * xfrm6_output.c - Common IPsec encapsulation code for IPv6.
   4 * Copyright (C) 2002 USAGI/WIDE Project
   5 * Copyright (c) 2004 Herbert Xu <herbert@gondor.apana.org.au>
   6 */
   7
   8#include <linux/if_ether.h>
   9#include <linux/kernel.h>
  10#include <linux/module.h>
  11#include <linux/skbuff.h>
  12#include <linux/icmpv6.h>
  13#include <linux/netfilter_ipv6.h>
  14#include <net/dst.h>
  15#include <net/ipv6.h>
  16#include <net/ip6_route.h>
  17#include <net/xfrm.h>
  18
  19int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb,
  20                          u8 **prevhdr)
  21{
  22        return ip6_find_1stfragopt(skb, prevhdr);
  23}
  24EXPORT_SYMBOL(xfrm6_find_1stfragopt);
  25
  26void xfrm6_local_rxpmtu(struct sk_buff *skb, u32 mtu)
  27{
  28        struct flowi6 fl6;
  29        struct sock *sk = skb->sk;
  30
  31        fl6.flowi6_oif = sk->sk_bound_dev_if;
  32        fl6.daddr = ipv6_hdr(skb)->daddr;
  33
  34        ipv6_local_rxpmtu(sk, &fl6, mtu);
  35}
  36
  37void xfrm6_local_error(struct sk_buff *skb, u32 mtu)
  38{
  39        struct flowi6 fl6;
  40        const struct ipv6hdr *hdr;
  41        struct sock *sk = skb->sk;
  42
  43        hdr = skb->encapsulation ? inner_ipv6_hdr(skb) : ipv6_hdr(skb);
  44        fl6.fl6_dport = inet_sk(sk)->inet_dport;
  45        fl6.daddr = hdr->daddr;
  46
  47        ipv6_local_error(sk, EMSGSIZE, &fl6, mtu);
  48}
  49
  50static int __xfrm6_output_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
  51{
  52        return xfrm_output(sk, skb);
  53}
  54
  55static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
  56{
  57        struct dst_entry *dst = skb_dst(skb);
  58        struct xfrm_state *x = dst->xfrm;
  59        int mtu;
  60        bool toobig;
  61
  62#ifdef CONFIG_NETFILTER
  63        if (!x) {
  64                IP6CB(skb)->flags |= IP6SKB_REROUTED;
  65                return dst_output(net, sk, skb);
  66        }
  67#endif
  68
  69        if (x->props.mode != XFRM_MODE_TUNNEL)
  70                goto skip_frag;
  71
  72        if (skb->protocol == htons(ETH_P_IPV6))
  73                mtu = ip6_skb_dst_mtu(skb);
  74        else
  75                mtu = dst_mtu(skb_dst(skb));
  76
  77        toobig = skb->len > mtu && !skb_is_gso(skb);
  78
  79        if (toobig && xfrm6_local_dontfrag(skb->sk)) {
  80                xfrm6_local_rxpmtu(skb, mtu);
  81                kfree_skb(skb);
  82                return -EMSGSIZE;
  83        } else if (!skb->ignore_df && toobig && skb->sk) {
  84                xfrm_local_error(skb, mtu);
  85                kfree_skb(skb);
  86                return -EMSGSIZE;
  87        }
  88
  89        if (toobig || dst_allfrag(skb_dst(skb)))
  90                return ip6_fragment(net, sk, skb,
  91                                    __xfrm6_output_finish);
  92
  93skip_frag:
  94        return xfrm_output(sk, skb);
  95}
  96
  97int xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
  98{
  99        return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING,
 100                            net, sk, skb,  skb->dev, skb_dst(skb)->dev,
 101                            __xfrm6_output,
 102                            !(IP6CB(skb)->flags & IP6SKB_REROUTED));
 103}
 104