LCOV - code coverage report
Current view: top level - net/ipv6 - xfrm6_policy.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 29 141 20.6 %
Date: 2022-04-01 13:59:58 Functions: 4 14 28.6 %
Branches: 7 46 15.2 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0
       2                 :            : /*
       3                 :            :  * xfrm6_policy.c: based on xfrm4_policy.c
       4                 :            :  *
       5                 :            :  * Authors:
       6                 :            :  *      Mitsuru KANDA @USAGI
       7                 :            :  *      Kazunori MIYAZAWA @USAGI
       8                 :            :  *      Kunihiro Ishiguro <kunihiro@ipinfusion.com>
       9                 :            :  *              IPv6 support
      10                 :            :  *      YOSHIFUJI Hideaki
      11                 :            :  *              Split up af-specific portion
      12                 :            :  *
      13                 :            :  */
      14                 :            : 
      15                 :            : #include <linux/err.h>
      16                 :            : #include <linux/kernel.h>
      17                 :            : #include <linux/netdevice.h>
      18                 :            : #include <net/addrconf.h>
      19                 :            : #include <net/dst.h>
      20                 :            : #include <net/xfrm.h>
      21                 :            : #include <net/ip.h>
      22                 :            : #include <net/ipv6.h>
      23                 :            : #include <net/ip6_route.h>
      24                 :            : #include <net/l3mdev.h>
      25                 :            : 
      26                 :          0 : static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif,
      27                 :            :                                           const xfrm_address_t *saddr,
      28                 :            :                                           const xfrm_address_t *daddr,
      29                 :            :                                           u32 mark)
      30                 :            : {
      31                 :          0 :         struct flowi6 fl6;
      32                 :          0 :         struct dst_entry *dst;
      33                 :          0 :         int err;
      34                 :            : 
      35                 :          0 :         memset(&fl6, 0, sizeof(fl6));
      36         [ #  # ]:          0 :         fl6.flowi6_oif = l3mdev_master_ifindex_by_index(net, oif);
      37                 :          0 :         fl6.flowi6_flags = FLOWI_FLAG_SKIP_NH_OIF;
      38                 :          0 :         fl6.flowi6_mark = mark;
      39                 :          0 :         memcpy(&fl6.daddr, daddr, sizeof(fl6.daddr));
      40         [ #  # ]:          0 :         if (saddr)
      41                 :          0 :                 memcpy(&fl6.saddr, saddr, sizeof(fl6.saddr));
      42                 :            : 
      43                 :          0 :         dst = ip6_route_output(net, NULL, &fl6);
      44                 :            : 
      45                 :          0 :         err = dst->error;
      46         [ #  # ]:          0 :         if (dst->error) {
      47                 :          0 :                 dst_release(dst);
      48                 :          0 :                 dst = ERR_PTR(err);
      49                 :            :         }
      50                 :            : 
      51                 :          0 :         return dst;
      52                 :            : }
      53                 :            : 
      54                 :          0 : static int xfrm6_get_saddr(struct net *net, int oif,
      55                 :            :                            xfrm_address_t *saddr, xfrm_address_t *daddr,
      56                 :            :                            u32 mark)
      57                 :            : {
      58                 :          0 :         struct dst_entry *dst;
      59                 :          0 :         struct net_device *dev;
      60                 :            : 
      61                 :          0 :         dst = xfrm6_dst_lookup(net, 0, oif, NULL, daddr, mark);
      62         [ #  # ]:          0 :         if (IS_ERR(dst))
      63                 :            :                 return -EHOSTUNREACH;
      64                 :            : 
      65                 :          0 :         dev = ip6_dst_idev(dst)->dev;
      66                 :          0 :         ipv6_dev_get_saddr(dev_net(dev), dev, &daddr->in6, 0, &saddr->in6);
      67                 :          0 :         dst_release(dst);
      68                 :          0 :         return 0;
      69                 :            : }
      70                 :            : 
      71                 :          0 : static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
      72                 :            :                           const struct flowi *fl)
      73                 :            : {
      74                 :          0 :         struct rt6_info *rt = (struct rt6_info *)xdst->route;
      75                 :            : 
      76                 :          0 :         xdst->u.dst.dev = dev;
      77                 :          0 :         dev_hold(dev);
      78                 :            : 
      79                 :          0 :         xdst->u.rt6.rt6i_idev = in6_dev_get(dev);
      80         [ #  # ]:          0 :         if (!xdst->u.rt6.rt6i_idev) {
      81                 :          0 :                 dev_put(dev);
      82                 :          0 :                 return -ENODEV;
      83                 :            :         }
      84                 :            : 
      85                 :            :         /* Sheit... I remember I did this right. Apparently,
      86                 :            :          * it was magically lost, so this code needs audit */
      87                 :          0 :         xdst->u.rt6.rt6i_flags = rt->rt6i_flags & (RTF_ANYCAST |
      88                 :            :                                                    RTF_LOCAL);
      89                 :          0 :         xdst->route_cookie = rt6_get_cookie(rt);
      90                 :          0 :         xdst->u.rt6.rt6i_gateway = rt->rt6i_gateway;
      91                 :          0 :         xdst->u.rt6.rt6i_dst = rt->rt6i_dst;
      92                 :          0 :         xdst->u.rt6.rt6i_src = rt->rt6i_src;
      93                 :          0 :         INIT_LIST_HEAD(&xdst->u.rt6.rt6i_uncached);
      94                 :          0 :         rt6_uncached_list_add(&xdst->u.rt6);
      95                 :          0 :         atomic_inc(&dev_net(dev)->ipv6.rt6_stats->fib_rt_uncache);
      96                 :            : 
      97                 :          0 :         return 0;
      98                 :            : }
      99                 :            : 
     100                 :          0 : static void xfrm6_update_pmtu(struct dst_entry *dst, struct sock *sk,
     101                 :            :                               struct sk_buff *skb, u32 mtu,
     102                 :            :                               bool confirm_neigh)
     103                 :            : {
     104                 :          0 :         struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
     105                 :          0 :         struct dst_entry *path = xdst->route;
     106                 :            : 
     107                 :          0 :         path->ops->update_pmtu(path, sk, skb, mtu, confirm_neigh);
     108                 :          0 : }
     109                 :            : 
     110                 :          0 : static void xfrm6_redirect(struct dst_entry *dst, struct sock *sk,
     111                 :            :                            struct sk_buff *skb)
     112                 :            : {
     113                 :          0 :         struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
     114                 :          0 :         struct dst_entry *path = xdst->route;
     115                 :            : 
     116                 :          0 :         path->ops->redirect(path, sk, skb);
     117                 :          0 : }
     118                 :            : 
     119                 :          0 : static void xfrm6_dst_destroy(struct dst_entry *dst)
     120                 :            : {
     121                 :          0 :         struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
     122                 :            : 
     123         [ #  # ]:          0 :         if (likely(xdst->u.rt6.rt6i_idev))
     124                 :          0 :                 in6_dev_put(xdst->u.rt6.rt6i_idev);
     125         [ #  # ]:          0 :         dst_destroy_metrics_generic(dst);
     126         [ #  # ]:          0 :         if (xdst->u.rt6.rt6i_uncached_list)
     127                 :          0 :                 rt6_uncached_list_del(&xdst->u.rt6);
     128                 :          0 :         xfrm_dst_destroy(xdst);
     129                 :          0 : }
     130                 :            : 
     131                 :          0 : static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
     132                 :            :                              int unregister)
     133                 :            : {
     134                 :          0 :         struct xfrm_dst *xdst;
     135                 :            : 
     136         [ #  # ]:          0 :         if (!unregister)
     137                 :            :                 return;
     138                 :            : 
     139                 :          0 :         xdst = (struct xfrm_dst *)dst;
     140         [ #  # ]:          0 :         if (xdst->u.rt6.rt6i_idev->dev == dev) {
     141                 :          0 :                 struct inet6_dev *loopback_idev =
     142                 :          0 :                         in6_dev_get(dev_net(dev)->loopback_dev);
     143                 :            : 
     144                 :          0 :                 do {
     145                 :          0 :                         in6_dev_put(xdst->u.rt6.rt6i_idev);
     146                 :          0 :                         xdst->u.rt6.rt6i_idev = loopback_idev;
     147                 :          0 :                         in6_dev_hold(loopback_idev);
     148         [ #  # ]:          0 :                         xdst = (struct xfrm_dst *)xfrm_dst_child(&xdst->u.dst);
     149         [ #  # ]:          0 :                 } while (xdst->u.dst.xfrm);
     150                 :            : 
     151                 :          0 :                 __in6_dev_put(loopback_idev);
     152                 :            :         }
     153                 :            : 
     154                 :          0 :         xfrm_dst_ifdown(dst, dev);
     155                 :            : }
     156                 :            : 
     157                 :            : static struct dst_ops xfrm6_dst_ops_template = {
     158                 :            :         .family =               AF_INET6,
     159                 :            :         .update_pmtu =          xfrm6_update_pmtu,
     160                 :            :         .redirect =             xfrm6_redirect,
     161                 :            :         .cow_metrics =          dst_cow_metrics_generic,
     162                 :            :         .destroy =              xfrm6_dst_destroy,
     163                 :            :         .ifdown =               xfrm6_dst_ifdown,
     164                 :            :         .local_out =            __ip6_local_out,
     165                 :            :         .gc_thresh =            32768,
     166                 :            : };
     167                 :            : 
     168                 :            : static const struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
     169                 :            :         .dst_ops =              &xfrm6_dst_ops_template,
     170                 :            :         .dst_lookup =           xfrm6_dst_lookup,
     171                 :            :         .get_saddr =            xfrm6_get_saddr,
     172                 :            :         .fill_dst =             xfrm6_fill_dst,
     173                 :            :         .blackhole_route =      ip6_blackhole_route,
     174                 :            : };
     175                 :            : 
     176                 :         78 : static int __init xfrm6_policy_init(void)
     177                 :            : {
     178                 :         78 :         return xfrm_policy_register_afinfo(&xfrm6_policy_afinfo, AF_INET6);
     179                 :            : }
     180                 :            : 
     181                 :          0 : static void xfrm6_policy_fini(void)
     182                 :            : {
     183                 :          0 :         xfrm_policy_unregister_afinfo(&xfrm6_policy_afinfo);
     184                 :            : }
     185                 :            : 
     186                 :            : #ifdef CONFIG_SYSCTL
     187                 :            : static struct ctl_table xfrm6_policy_table[] = {
     188                 :            :         {
     189                 :            :                 .procname       = "xfrm6_gc_thresh",
     190                 :            :                 .data           = &init_net.xfrm.xfrm6_dst_ops.gc_thresh,
     191                 :            :                 .maxlen         = sizeof(int),
     192                 :            :                 .mode           = 0644,
     193                 :            :                 .proc_handler   = proc_dointvec,
     194                 :            :         },
     195                 :            :         { }
     196                 :            : };
     197                 :            : 
     198                 :         78 : static int __net_init xfrm6_net_sysctl_init(struct net *net)
     199                 :            : {
     200                 :         78 :         struct ctl_table *table;
     201                 :         78 :         struct ctl_table_header *hdr;
     202                 :            : 
     203                 :         78 :         table = xfrm6_policy_table;
     204         [ -  + ]:         78 :         if (!net_eq(net, &init_net)) {
     205                 :          0 :                 table = kmemdup(table, sizeof(xfrm6_policy_table), GFP_KERNEL);
     206         [ #  # ]:          0 :                 if (!table)
     207                 :          0 :                         goto err_alloc;
     208                 :            : 
     209                 :          0 :                 table[0].data = &net->xfrm.xfrm6_dst_ops.gc_thresh;
     210                 :            :         }
     211                 :            : 
     212                 :         78 :         hdr = register_net_sysctl(net, "net/ipv6", table);
     213         [ -  + ]:         78 :         if (!hdr)
     214                 :          0 :                 goto err_reg;
     215                 :            : 
     216                 :         78 :         net->ipv6.sysctl.xfrm6_hdr = hdr;
     217                 :         78 :         return 0;
     218                 :            : 
     219                 :            : err_reg:
     220         [ #  # ]:          0 :         if (!net_eq(net, &init_net))
     221                 :          0 :                 kfree(table);
     222                 :          0 : err_alloc:
     223                 :            :         return -ENOMEM;
     224                 :            : }
     225                 :            : 
     226                 :          0 : static void __net_exit xfrm6_net_sysctl_exit(struct net *net)
     227                 :            : {
     228                 :          0 :         struct ctl_table *table;
     229                 :            : 
     230         [ #  # ]:          0 :         if (!net->ipv6.sysctl.xfrm6_hdr)
     231                 :            :                 return;
     232                 :            : 
     233                 :          0 :         table = net->ipv6.sysctl.xfrm6_hdr->ctl_table_arg;
     234                 :          0 :         unregister_net_sysctl_table(net->ipv6.sysctl.xfrm6_hdr);
     235         [ #  # ]:          0 :         if (!net_eq(net, &init_net))
     236                 :          0 :                 kfree(table);
     237                 :            : }
     238                 :            : #else /* CONFIG_SYSCTL */
     239                 :            : static inline int xfrm6_net_sysctl_init(struct net *net)
     240                 :            : {
     241                 :            :         return 0;
     242                 :            : }
     243                 :            : 
     244                 :            : static inline void xfrm6_net_sysctl_exit(struct net *net)
     245                 :            : {
     246                 :            : }
     247                 :            : #endif
     248                 :            : 
     249                 :         78 : static int __net_init xfrm6_net_init(struct net *net)
     250                 :            : {
     251                 :         78 :         int ret;
     252                 :            : 
     253                 :         78 :         memcpy(&net->xfrm.xfrm6_dst_ops, &xfrm6_dst_ops_template,
     254                 :            :                sizeof(xfrm6_dst_ops_template));
     255                 :         78 :         ret = dst_entries_init(&net->xfrm.xfrm6_dst_ops);
     256         [ +  - ]:         78 :         if (ret)
     257                 :            :                 return ret;
     258                 :            : 
     259                 :         78 :         ret = xfrm6_net_sysctl_init(net);
     260         [ -  + ]:         78 :         if (ret)
     261                 :          0 :                 dst_entries_destroy(&net->xfrm.xfrm6_dst_ops);
     262                 :            : 
     263                 :            :         return ret;
     264                 :            : }
     265                 :            : 
     266                 :          0 : static void __net_exit xfrm6_net_exit(struct net *net)
     267                 :            : {
     268                 :          0 :         xfrm6_net_sysctl_exit(net);
     269                 :          0 :         dst_entries_destroy(&net->xfrm.xfrm6_dst_ops);
     270                 :          0 : }
     271                 :            : 
     272                 :            : static struct pernet_operations xfrm6_net_ops = {
     273                 :            :         .init   = xfrm6_net_init,
     274                 :            :         .exit   = xfrm6_net_exit,
     275                 :            : };
     276                 :            : 
     277                 :         78 : int __init xfrm6_init(void)
     278                 :            : {
     279                 :         78 :         int ret;
     280                 :            : 
     281                 :         78 :         ret = xfrm6_policy_init();
     282         [ -  + ]:         78 :         if (ret)
     283                 :          0 :                 goto out;
     284                 :         78 :         ret = xfrm6_state_init();
     285         [ -  + ]:         78 :         if (ret)
     286                 :          0 :                 goto out_policy;
     287                 :            : 
     288                 :         78 :         ret = xfrm6_protocol_init();
     289         [ -  + ]:         78 :         if (ret)
     290                 :          0 :                 goto out_state;
     291                 :            : 
     292                 :         78 :         register_pernet_subsys(&xfrm6_net_ops);
     293                 :         78 : out:
     294                 :         78 :         return ret;
     295                 :            : out_state:
     296                 :          0 :         xfrm6_state_fini();
     297                 :          0 : out_policy:
     298                 :          0 :         xfrm6_policy_fini();
     299                 :          0 :         goto out;
     300                 :            : }
     301                 :            : 
     302                 :          0 : void xfrm6_fini(void)
     303                 :            : {
     304                 :          0 :         unregister_pernet_subsys(&xfrm6_net_ops);
     305                 :          0 :         xfrm6_protocol_fini();
     306                 :          0 :         xfrm6_policy_fini();
     307                 :          0 :         xfrm6_state_fini();
     308                 :          0 : }

Generated by: LCOV version 1.14