LCOV - code coverage report
Current view: top level - net/ipv6 - xfrm6_policy.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 128 0.0 %
Date: 2014-04-07 Functions: 0 17 0.0 %
Branches: 0 65 0.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * xfrm6_policy.c: based on xfrm4_policy.c
       3                 :            :  *
       4                 :            :  * Authors:
       5                 :            :  *      Mitsuru KANDA @USAGI
       6                 :            :  *      Kazunori MIYAZAWA @USAGI
       7                 :            :  *      Kunihiro Ishiguro <kunihiro@ipinfusion.com>
       8                 :            :  *              IPv6 support
       9                 :            :  *      YOSHIFUJI Hideaki
      10                 :            :  *              Split up af-specific portion
      11                 :            :  *
      12                 :            :  */
      13                 :            : 
      14                 :            : #include <linux/err.h>
      15                 :            : #include <linux/kernel.h>
      16                 :            : #include <linux/netdevice.h>
      17                 :            : #include <net/addrconf.h>
      18                 :            : #include <net/dst.h>
      19                 :            : #include <net/xfrm.h>
      20                 :            : #include <net/ip.h>
      21                 :            : #include <net/ipv6.h>
      22                 :            : #include <net/ip6_route.h>
      23                 :            : #if IS_ENABLED(CONFIG_IPV6_MIP6)
      24                 :            : #include <net/mip6.h>
      25                 :            : #endif
      26                 :            : 
      27                 :            : static struct xfrm_policy_afinfo xfrm6_policy_afinfo;
      28                 :            : 
      29                 :          0 : static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos,
      30                 :            :                                           const xfrm_address_t *saddr,
      31                 :            :                                           const xfrm_address_t *daddr)
      32                 :            : {
      33                 :            :         struct flowi6 fl6;
      34                 :            :         struct dst_entry *dst;
      35                 :            :         int err;
      36                 :            : 
      37                 :          0 :         memset(&fl6, 0, sizeof(fl6));
      38                 :          0 :         memcpy(&fl6.daddr, daddr, sizeof(fl6.daddr));
      39         [ #  # ]:          0 :         if (saddr)
      40                 :          0 :                 memcpy(&fl6.saddr, saddr, sizeof(fl6.saddr));
      41                 :            : 
      42                 :          0 :         dst = ip6_route_output(net, NULL, &fl6);
      43                 :            : 
      44                 :          0 :         err = dst->error;
      45         [ #  # ]:          0 :         if (dst->error) {
      46                 :          0 :                 dst_release(dst);
      47                 :            :                 dst = ERR_PTR(err);
      48                 :            :         }
      49                 :            : 
      50                 :          0 :         return dst;
      51                 :            : }
      52                 :            : 
      53                 :          0 : static int xfrm6_get_saddr(struct net *net,
      54                 :            :                            xfrm_address_t *saddr, xfrm_address_t *daddr)
      55                 :            : {
      56                 :            :         struct dst_entry *dst;
      57                 :            :         struct net_device *dev;
      58                 :            : 
      59                 :          0 :         dst = xfrm6_dst_lookup(net, 0, NULL, daddr);
      60         [ #  # ]:          0 :         if (IS_ERR(dst))
      61                 :            :                 return -EHOSTUNREACH;
      62                 :            : 
      63                 :          0 :         dev = ip6_dst_idev(dst)->dev;
      64                 :          0 :         ipv6_dev_get_saddr(dev_net(dev), dev,
      65                 :          0 :                            (struct in6_addr *)&daddr->a6, 0,
      66                 :          0 :                            (struct in6_addr *)&saddr->a6);
      67                 :          0 :         dst_release(dst);
      68                 :          0 :         return 0;
      69                 :            : }
      70                 :            : 
      71                 :          0 : static int xfrm6_get_tos(const struct flowi *fl)
      72                 :            : {
      73                 :          0 :         return 0;
      74                 :            : }
      75                 :            : 
      76                 :          0 : static void xfrm6_init_dst(struct net *net, struct xfrm_dst *xdst)
      77                 :            : {
      78                 :            :         struct rt6_info *rt = (struct rt6_info *)xdst;
      79                 :            : 
      80                 :          0 :         rt6_init_peer(rt, net->ipv6.peers);
      81                 :          0 : }
      82                 :            : 
      83                 :          0 : static int xfrm6_init_path(struct xfrm_dst *path, struct dst_entry *dst,
      84                 :            :                            int nfheader_len)
      85                 :            : {
      86         [ #  # ]:          0 :         if (dst->ops->family == AF_INET6) {
      87                 :            :                 struct rt6_info *rt = (struct rt6_info*)dst;
      88         [ #  # ]:          0 :                 if (rt->rt6i_node)
      89                 :          0 :                         path->path_cookie = rt->rt6i_node->fn_sernum;
      90                 :            :         }
      91                 :            : 
      92                 :          0 :         path->u.rt6.rt6i_nfheader_len = nfheader_len;
      93                 :            : 
      94                 :          0 :         return 0;
      95                 :            : }
      96                 :            : 
      97                 :          0 : static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
      98                 :            :                           const struct flowi *fl)
      99                 :            : {
     100                 :          0 :         struct rt6_info *rt = (struct rt6_info*)xdst->route;
     101                 :            : 
     102                 :          0 :         xdst->u.dst.dev = dev;
     103                 :            :         dev_hold(dev);
     104                 :            : 
     105                 :          0 :         xdst->u.rt6.rt6i_idev = in6_dev_get(dev);
     106         [ #  # ]:          0 :         if (!xdst->u.rt6.rt6i_idev) {
     107                 :            :                 dev_put(dev);
     108                 :          0 :                 return -ENODEV;
     109                 :            :         }
     110                 :            : 
     111                 :            :         rt6_transfer_peer(&xdst->u.rt6, rt);
     112                 :            : 
     113                 :            :         /* Sheit... I remember I did this right. Apparently,
     114                 :            :          * it was magically lost, so this code needs audit */
     115                 :          0 :         xdst->u.rt6.rt6i_flags = rt->rt6i_flags & (RTF_ANYCAST |
     116                 :            :                                                    RTF_LOCAL);
     117                 :          0 :         xdst->u.rt6.rt6i_metric = rt->rt6i_metric;
     118                 :          0 :         xdst->u.rt6.rt6i_node = rt->rt6i_node;
     119         [ #  # ]:          0 :         if (rt->rt6i_node)
     120                 :          0 :                 xdst->route_cookie = rt->rt6i_node->fn_sernum;
     121                 :          0 :         xdst->u.rt6.rt6i_gateway = rt->rt6i_gateway;
     122                 :          0 :         xdst->u.rt6.rt6i_dst = rt->rt6i_dst;
     123                 :          0 :         xdst->u.rt6.rt6i_src = rt->rt6i_src;
     124                 :            : 
     125                 :          0 :         return 0;
     126                 :            : }
     127                 :            : 
     128                 :            : static inline void
     129                 :          0 : _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)
     130                 :            : {
     131                 :          0 :         struct flowi6 *fl6 = &fl->u.ip6;
     132                 :            :         int onlyproto = 0;
     133                 :          0 :         u16 offset = skb_network_header_len(skb);
     134                 :            :         const struct ipv6hdr *hdr = ipv6_hdr(skb);
     135                 :            :         struct ipv6_opt_hdr *exthdr;
     136                 :            :         const unsigned char *nh = skb_network_header(skb);
     137                 :          0 :         u8 nexthdr = nh[IP6CB(skb)->nhoff];
     138                 :            :         int oif = 0;
     139                 :            : 
     140         [ #  # ]:          0 :         if (skb_dst(skb))
     141                 :          0 :                 oif = skb_dst(skb)->dev->ifindex;
     142                 :            : 
     143                 :          0 :         memset(fl6, 0, sizeof(struct flowi6));
     144                 :          0 :         fl6->flowi6_mark = skb->mark;
     145         [ #  # ]:          0 :         fl6->flowi6_oif = reverse ? skb->skb_iif : oif;
     146                 :            : 
     147         [ #  # ]:          0 :         fl6->daddr = reverse ? hdr->saddr : hdr->daddr;
     148         [ #  # ]:          0 :         fl6->saddr = reverse ? hdr->daddr : hdr->saddr;
     149                 :            : 
     150 [ #  # ][ #  # ]:          0 :         while (nh + offset + 1 < skb->data ||
     151                 :          0 :                pskb_may_pull(skb, nh + offset + 1 - skb->data)) {
     152                 :            :                 nh = skb_network_header(skb);
     153                 :          0 :                 exthdr = (struct ipv6_opt_hdr *)(nh + offset);
     154                 :            : 
     155   [ #  #  #  #  :          0 :                 switch (nexthdr) {
                      # ]
     156                 :            :                 case NEXTHDR_FRAGMENT:
     157                 :            :                         onlyproto = 1;
     158                 :            :                 case NEXTHDR_ROUTING:
     159                 :            :                 case NEXTHDR_HOP:
     160                 :            :                 case NEXTHDR_DEST:
     161                 :          0 :                         offset += ipv6_optlen(exthdr);
     162                 :          0 :                         nexthdr = exthdr->nexthdr;
     163                 :            :                         exthdr = (struct ipv6_opt_hdr *)(nh + offset);
     164                 :          0 :                         break;
     165                 :            : 
     166                 :            :                 case IPPROTO_UDP:
     167                 :            :                 case IPPROTO_UDPLITE:
     168                 :            :                 case IPPROTO_TCP:
     169                 :            :                 case IPPROTO_SCTP:
     170                 :            :                 case IPPROTO_DCCP:
     171 [ #  # ][ #  # ]:          0 :                         if (!onlyproto && (nh + offset + 4 < skb->data ||
                 [ #  # ]
     172                 :          0 :                              pskb_may_pull(skb, nh + offset + 4 - skb->data))) {
     173                 :            :                                 __be16 *ports = (__be16 *)exthdr;
     174                 :            : 
     175         [ #  # ]:          0 :                                 fl6->fl6_sport = ports[!!reverse];
     176         [ #  # ]:          0 :                                 fl6->fl6_dport = ports[!reverse];
     177                 :            :                         }
     178                 :          0 :                         fl6->flowi6_proto = nexthdr;
     179                 :          0 :                         return;
     180                 :            : 
     181                 :            :                 case IPPROTO_ICMPV6:
     182 [ #  # ][ #  # ]:          0 :                         if (!onlyproto && pskb_may_pull(skb, nh + offset + 2 - skb->data)) {
     183                 :            :                                 u8 *icmp = (u8 *)exthdr;
     184                 :            : 
     185                 :          0 :                                 fl6->fl6_icmp_type = icmp[0];
     186                 :          0 :                                 fl6->fl6_icmp_code = icmp[1];
     187                 :            :                         }
     188                 :          0 :                         fl6->flowi6_proto = nexthdr;
     189                 :          0 :                         return;
     190                 :            : 
     191                 :            : #if IS_ENABLED(CONFIG_IPV6_MIP6)
     192                 :            :                 case IPPROTO_MH:
     193                 :            :                         if (!onlyproto && pskb_may_pull(skb, nh + offset + 3 - skb->data)) {
     194                 :            :                                 struct ip6_mh *mh;
     195                 :            :                                 mh = (struct ip6_mh *)exthdr;
     196                 :            : 
     197                 :            :                                 fl6->fl6_mh_type = mh->ip6mh_type;
     198                 :            :                         }
     199                 :            :                         fl6->flowi6_proto = nexthdr;
     200                 :            :                         return;
     201                 :            : #endif
     202                 :            : 
     203                 :            :                 /* XXX Why are there these headers? */
     204                 :            :                 case IPPROTO_AH:
     205                 :            :                 case IPPROTO_ESP:
     206                 :            :                 case IPPROTO_COMP:
     207                 :            :                 default:
     208                 :          0 :                         fl6->fl6_ipsec_spi = 0;
     209                 :          0 :                         fl6->flowi6_proto = nexthdr;
     210                 :          0 :                         return;
     211                 :            :                 }
     212                 :            :         }
     213                 :            : }
     214                 :            : 
     215                 :          0 : static inline int xfrm6_garbage_collect(struct dst_ops *ops)
     216                 :            : {
     217                 :          0 :         struct net *net = container_of(ops, struct net, xfrm.xfrm6_dst_ops);
     218                 :            : 
     219                 :          0 :         xfrm6_policy_afinfo.garbage_collect(net);
     220                 :          0 :         return dst_entries_get_fast(ops) > ops->gc_thresh * 2;
     221                 :            : }
     222                 :            : 
     223                 :          0 : static void xfrm6_update_pmtu(struct dst_entry *dst, struct sock *sk,
     224                 :            :                               struct sk_buff *skb, u32 mtu)
     225                 :            : {
     226                 :            :         struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
     227                 :          0 :         struct dst_entry *path = xdst->route;
     228                 :            : 
     229                 :          0 :         path->ops->update_pmtu(path, sk, skb, mtu);
     230                 :          0 : }
     231                 :            : 
     232                 :          0 : static void xfrm6_redirect(struct dst_entry *dst, struct sock *sk,
     233                 :            :                            struct sk_buff *skb)
     234                 :            : {
     235                 :            :         struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
     236                 :          0 :         struct dst_entry *path = xdst->route;
     237                 :            : 
     238                 :          0 :         path->ops->redirect(path, sk, skb);
     239                 :          0 : }
     240                 :            : 
     241                 :          0 : static void xfrm6_dst_destroy(struct dst_entry *dst)
     242                 :            : {
     243                 :            :         struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
     244                 :            : 
     245         [ #  # ]:          0 :         if (likely(xdst->u.rt6.rt6i_idev))
     246                 :            :                 in6_dev_put(xdst->u.rt6.rt6i_idev);
     247                 :            :         dst_destroy_metrics_generic(dst);
     248         [ #  # ]:          0 :         if (rt6_has_peer(&xdst->u.rt6)) {
     249                 :            :                 struct inet_peer *peer = rt6_peer_ptr(&xdst->u.rt6);
     250                 :          0 :                 inet_putpeer(peer);
     251                 :            :         }
     252                 :            :         xfrm_dst_destroy(xdst);
     253                 :          0 : }
     254                 :            : 
     255                 :          0 : static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
     256                 :            :                              int unregister)
     257                 :            : {
     258                 :            :         struct xfrm_dst *xdst;
     259                 :            : 
     260         [ #  # ]:          0 :         if (!unregister)
     261                 :          0 :                 return;
     262                 :            : 
     263                 :            :         xdst = (struct xfrm_dst *)dst;
     264         [ #  # ]:          0 :         if (xdst->u.rt6.rt6i_idev->dev == dev) {
     265                 :            :                 struct inet6_dev *loopback_idev =
     266                 :          0 :                         in6_dev_get(dev_net(dev)->loopback_dev);
     267         [ #  # ]:          0 :                 BUG_ON(!loopback_idev);
     268                 :            : 
     269                 :            :                 do {
     270                 :          0 :                         in6_dev_put(xdst->u.rt6.rt6i_idev);
     271                 :          0 :                         xdst->u.rt6.rt6i_idev = loopback_idev;
     272                 :            :                         in6_dev_hold(loopback_idev);
     273                 :          0 :                         xdst = (struct xfrm_dst *)xdst->u.dst.child;
     274         [ #  # ]:          0 :                 } while (xdst->u.dst.xfrm);
     275                 :            : 
     276                 :            :                 __in6_dev_put(loopback_idev);
     277                 :            :         }
     278                 :            : 
     279                 :          0 :         xfrm_dst_ifdown(dst, dev);
     280                 :            : }
     281                 :            : 
     282                 :            : static struct dst_ops xfrm6_dst_ops = {
     283                 :            :         .family =               AF_INET6,
     284                 :            :         .protocol =             cpu_to_be16(ETH_P_IPV6),
     285                 :            :         .gc =                   xfrm6_garbage_collect,
     286                 :            :         .update_pmtu =          xfrm6_update_pmtu,
     287                 :            :         .redirect =             xfrm6_redirect,
     288                 :            :         .cow_metrics =          dst_cow_metrics_generic,
     289                 :            :         .destroy =              xfrm6_dst_destroy,
     290                 :            :         .ifdown =               xfrm6_dst_ifdown,
     291                 :            :         .local_out =            __ip6_local_out,
     292                 :            :         .gc_thresh =            32768,
     293                 :            : };
     294                 :            : 
     295                 :            : static struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
     296                 :            :         .family =               AF_INET6,
     297                 :            :         .dst_ops =              &xfrm6_dst_ops,
     298                 :            :         .dst_lookup =           xfrm6_dst_lookup,
     299                 :            :         .get_saddr =            xfrm6_get_saddr,
     300                 :            :         .decode_session =       _decode_session6,
     301                 :            :         .get_tos =              xfrm6_get_tos,
     302                 :            :         .init_dst =             xfrm6_init_dst,
     303                 :            :         .init_path =            xfrm6_init_path,
     304                 :            :         .fill_dst =             xfrm6_fill_dst,
     305                 :            :         .blackhole_route =      ip6_blackhole_route,
     306                 :            : };
     307                 :            : 
     308                 :          0 : static int __init xfrm6_policy_init(void)
     309                 :            : {
     310                 :          0 :         return xfrm_policy_register_afinfo(&xfrm6_policy_afinfo);
     311                 :            : }
     312                 :            : 
     313                 :            : static void xfrm6_policy_fini(void)
     314                 :            : {
     315                 :          0 :         xfrm_policy_unregister_afinfo(&xfrm6_policy_afinfo);
     316                 :            : }
     317                 :            : 
     318                 :            : #ifdef CONFIG_SYSCTL
     319                 :            : static struct ctl_table xfrm6_policy_table[] = {
     320                 :            :         {
     321                 :            :                 .procname       = "xfrm6_gc_thresh",
     322                 :            :                 .data           = &init_net.xfrm.xfrm6_dst_ops.gc_thresh,
     323                 :            :                 .maxlen         = sizeof(int),
     324                 :            :                 .mode           = 0644,
     325                 :            :                 .proc_handler   = proc_dointvec,
     326                 :            :         },
     327                 :            :         { }
     328                 :            : };
     329                 :            : 
     330                 :          0 : static int __net_init xfrm6_net_init(struct net *net)
     331                 :            : {
     332                 :            :         struct ctl_table *table;
     333                 :            :         struct ctl_table_header *hdr;
     334                 :            : 
     335                 :            :         table = xfrm6_policy_table;
     336                 :            :         if (!net_eq(net, &init_net)) {
     337                 :            :                 table = kmemdup(table, sizeof(xfrm6_policy_table), GFP_KERNEL);
     338                 :            :                 if (!table)
     339                 :            :                         goto err_alloc;
     340                 :            : 
     341                 :            :                 table[0].data = &net->xfrm.xfrm6_dst_ops.gc_thresh;
     342                 :            :         }
     343                 :            : 
     344                 :          0 :         hdr = register_net_sysctl(net, "net/ipv6", table);
     345         [ #  # ]:          0 :         if (!hdr)
     346                 :            :                 goto err_reg;
     347                 :            : 
     348                 :          0 :         net->ipv6.sysctl.xfrm6_hdr = hdr;
     349                 :          0 :         return 0;
     350                 :            : 
     351                 :            : err_reg:
     352                 :            :         if (!net_eq(net, &init_net))
     353                 :            :                 kfree(table);
     354                 :            : err_alloc:
     355                 :            :         return -ENOMEM;
     356                 :            : }
     357                 :            : 
     358                 :          0 : static void __net_exit xfrm6_net_exit(struct net *net)
     359                 :            : {
     360                 :            :         struct ctl_table *table;
     361                 :            : 
     362         [ #  # ]:          0 :         if (net->ipv6.sysctl.xfrm6_hdr == NULL)
     363                 :          0 :                 return;
     364                 :            : 
     365                 :            :         table = net->ipv6.sysctl.xfrm6_hdr->ctl_table_arg;
     366                 :          0 :         unregister_net_sysctl_table(net->ipv6.sysctl.xfrm6_hdr);
     367                 :            :         if (!net_eq(net, &init_net))
     368                 :            :                 kfree(table);
     369                 :            : }
     370                 :            : 
     371                 :            : static struct pernet_operations xfrm6_net_ops = {
     372                 :            :         .init   = xfrm6_net_init,
     373                 :            :         .exit   = xfrm6_net_exit,
     374                 :            : };
     375                 :            : #endif
     376                 :            : 
     377                 :          0 : int __init xfrm6_init(void)
     378                 :            : {
     379                 :            :         int ret;
     380                 :            : 
     381                 :            :         dst_entries_init(&xfrm6_dst_ops);
     382                 :            : 
     383                 :          0 :         ret = xfrm6_policy_init();
     384         [ #  # ]:          0 :         if (ret) {
     385                 :            :                 dst_entries_destroy(&xfrm6_dst_ops);
     386                 :            :                 goto out;
     387                 :            :         }
     388                 :          0 :         ret = xfrm6_state_init();
     389         [ #  # ]:          0 :         if (ret)
     390                 :            :                 goto out_policy;
     391                 :            : 
     392                 :            : #ifdef CONFIG_SYSCTL
     393                 :          0 :         register_pernet_subsys(&xfrm6_net_ops);
     394                 :            : #endif
     395                 :            : out:
     396                 :          0 :         return ret;
     397                 :            : out_policy:
     398                 :            :         xfrm6_policy_fini();
     399                 :            :         goto out;
     400                 :            : }
     401                 :            : 
     402                 :          0 : void xfrm6_fini(void)
     403                 :            : {
     404                 :            : #ifdef CONFIG_SYSCTL
     405                 :          0 :         unregister_pernet_subsys(&xfrm6_net_ops);
     406                 :            : #endif
     407                 :            :         xfrm6_policy_fini();
     408                 :          0 :         xfrm6_state_fini();
     409                 :            :         dst_entries_destroy(&xfrm6_dst_ops);
     410                 :          0 : }

Generated by: LCOV version 1.9