LCOV - code coverage report
Current view: top level - net/ipv6 - xfrm6_protocol.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 29 114 25.4 %
Date: 2022-04-01 13:59:58 Functions: 2 11 18.2 %
Branches: 12 86 14.0 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0-or-later
       2                 :            : /* xfrm6_protocol.c - Generic xfrm protocol multiplexer for ipv6.
       3                 :            :  *
       4                 :            :  * Copyright (C) 2013 secunet Security Networks AG
       5                 :            :  *
       6                 :            :  * Author:
       7                 :            :  * Steffen Klassert <steffen.klassert@secunet.com>
       8                 :            :  *
       9                 :            :  * Based on:
      10                 :            :  * net/ipv4/xfrm4_protocol.c
      11                 :            :  */
      12                 :            : 
      13                 :            : #include <linux/init.h>
      14                 :            : #include <linux/mutex.h>
      15                 :            : #include <linux/skbuff.h>
      16                 :            : #include <linux/icmpv6.h>
      17                 :            : #include <net/ipv6.h>
      18                 :            : #include <net/protocol.h>
      19                 :            : #include <net/xfrm.h>
      20                 :            : 
      21                 :            : static struct xfrm6_protocol __rcu *esp6_handlers __read_mostly;
      22                 :            : static struct xfrm6_protocol __rcu *ah6_handlers __read_mostly;
      23                 :            : static struct xfrm6_protocol __rcu *ipcomp6_handlers __read_mostly;
      24                 :            : static DEFINE_MUTEX(xfrm6_protocol_mutex);
      25                 :            : 
      26                 :        468 : static inline struct xfrm6_protocol __rcu **proto_handlers(u8 protocol)
      27                 :            : {
      28                 :        468 :         switch (protocol) {
      29                 :            :         case IPPROTO_ESP:
      30                 :            :                 return &esp6_handlers;
      31                 :        156 :         case IPPROTO_AH:
      32                 :        156 :                 return &ah6_handlers;
      33                 :          0 :         case IPPROTO_COMP:
      34                 :          0 :                 return &ipcomp6_handlers;
      35                 :            :         }
      36                 :            : 
      37                 :          0 :         return NULL;
      38                 :            : }
      39                 :            : 
      40                 :            : #define for_each_protocol_rcu(head, handler)            \
      41                 :            :         for (handler = rcu_dereference(head);           \
      42                 :            :              handler != NULL;                           \
      43                 :            :              handler = rcu_dereference(handler->next))       \
      44                 :            : 
      45                 :          0 : static int xfrm6_rcv_cb(struct sk_buff *skb, u8 protocol, int err)
      46                 :            : {
      47                 :          0 :         int ret;
      48                 :          0 :         struct xfrm6_protocol *handler;
      49         [ #  # ]:          0 :         struct xfrm6_protocol __rcu **head = proto_handlers(protocol);
      50                 :            : 
      51                 :          0 :         if (!head)
      52                 :            :                 return 0;
      53                 :            : 
      54   [ #  #  #  #  :          0 :         for_each_protocol_rcu(*proto_handlers(protocol), handler)
                   #  # ]
      55         [ #  # ]:          0 :                 if ((ret = handler->cb_handler(skb, err)) <= 0)
      56                 :          0 :                         return ret;
      57                 :            : 
      58                 :            :         return 0;
      59                 :            : }
      60                 :            : 
      61                 :          0 : static int xfrm6_esp_rcv(struct sk_buff *skb)
      62                 :            : {
      63                 :          0 :         int ret;
      64                 :          0 :         struct xfrm6_protocol *handler;
      65                 :            : 
      66                 :          0 :         XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
      67                 :            : 
      68         [ #  # ]:          0 :         for_each_protocol_rcu(esp6_handlers, handler)
      69         [ #  # ]:          0 :                 if ((ret = handler->handler(skb)) != -EINVAL)
      70                 :          0 :                         return ret;
      71                 :            : 
      72                 :          0 :         icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
      73                 :            : 
      74                 :          0 :         kfree_skb(skb);
      75                 :          0 :         return 0;
      76                 :            : }
      77                 :            : 
      78                 :          0 : static int xfrm6_esp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
      79                 :            :                           u8 type, u8 code, int offset, __be32 info)
      80                 :            : {
      81                 :          0 :         struct xfrm6_protocol *handler;
      82                 :            : 
      83         [ #  # ]:          0 :         for_each_protocol_rcu(esp6_handlers, handler)
      84         [ #  # ]:          0 :                 if (!handler->err_handler(skb, opt, type, code, offset, info))
      85                 :            :                         return 0;
      86                 :            : 
      87                 :            :         return -ENOENT;
      88                 :            : }
      89                 :            : 
      90                 :          0 : static int xfrm6_ah_rcv(struct sk_buff *skb)
      91                 :            : {
      92                 :          0 :         int ret;
      93                 :          0 :         struct xfrm6_protocol *handler;
      94                 :            : 
      95                 :          0 :         XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
      96                 :            : 
      97         [ #  # ]:          0 :         for_each_protocol_rcu(ah6_handlers, handler)
      98         [ #  # ]:          0 :                 if ((ret = handler->handler(skb)) != -EINVAL)
      99                 :          0 :                         return ret;
     100                 :            : 
     101                 :          0 :         icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
     102                 :            : 
     103                 :          0 :         kfree_skb(skb);
     104                 :          0 :         return 0;
     105                 :            : }
     106                 :            : 
     107                 :          0 : static int xfrm6_ah_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
     108                 :            :                          u8 type, u8 code, int offset, __be32 info)
     109                 :            : {
     110                 :          0 :         struct xfrm6_protocol *handler;
     111                 :            : 
     112         [ #  # ]:          0 :         for_each_protocol_rcu(ah6_handlers, handler)
     113         [ #  # ]:          0 :                 if (!handler->err_handler(skb, opt, type, code, offset, info))
     114                 :            :                         return 0;
     115                 :            : 
     116                 :            :         return -ENOENT;
     117                 :            : }
     118                 :            : 
     119                 :          0 : static int xfrm6_ipcomp_rcv(struct sk_buff *skb)
     120                 :            : {
     121                 :          0 :         int ret;
     122                 :          0 :         struct xfrm6_protocol *handler;
     123                 :            : 
     124                 :          0 :         XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
     125                 :            : 
     126         [ #  # ]:          0 :         for_each_protocol_rcu(ipcomp6_handlers, handler)
     127         [ #  # ]:          0 :                 if ((ret = handler->handler(skb)) != -EINVAL)
     128                 :          0 :                         return ret;
     129                 :            : 
     130                 :          0 :         icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
     131                 :            : 
     132                 :          0 :         kfree_skb(skb);
     133                 :          0 :         return 0;
     134                 :            : }
     135                 :            : 
     136                 :          0 : static int xfrm6_ipcomp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
     137                 :            :                              u8 type, u8 code, int offset, __be32 info)
     138                 :            : {
     139                 :          0 :         struct xfrm6_protocol *handler;
     140                 :            : 
     141         [ #  # ]:          0 :         for_each_protocol_rcu(ipcomp6_handlers, handler)
     142         [ #  # ]:          0 :                 if (!handler->err_handler(skb, opt, type, code, offset, info))
     143                 :            :                         return 0;
     144                 :            : 
     145                 :            :         return -ENOENT;
     146                 :            : }
     147                 :            : 
     148                 :            : static const struct inet6_protocol esp6_protocol = {
     149                 :            :         .handler        =       xfrm6_esp_rcv,
     150                 :            :         .err_handler    =       xfrm6_esp_err,
     151                 :            :         .flags          =       INET6_PROTO_NOPOLICY,
     152                 :            : };
     153                 :            : 
     154                 :            : static const struct inet6_protocol ah6_protocol = {
     155                 :            :         .handler        =       xfrm6_ah_rcv,
     156                 :            :         .err_handler    =       xfrm6_ah_err,
     157                 :            :         .flags          =       INET6_PROTO_NOPOLICY,
     158                 :            : };
     159                 :            : 
     160                 :            : static const struct inet6_protocol ipcomp6_protocol = {
     161                 :            :         .handler        =       xfrm6_ipcomp_rcv,
     162                 :            :         .err_handler    =       xfrm6_ipcomp_err,
     163                 :            :         .flags          =       INET6_PROTO_NOPOLICY,
     164                 :            : };
     165                 :            : 
     166                 :            : static const struct xfrm_input_afinfo xfrm6_input_afinfo = {
     167                 :            :         .family         =       AF_INET6,
     168                 :            :         .callback       =       xfrm6_rcv_cb,
     169                 :            : };
     170                 :            : 
     171                 :        312 : static inline const struct inet6_protocol *netproto(unsigned char protocol)
     172                 :            : {
     173   [ -  -  +  - ]:        156 :         switch (protocol) {
     174                 :            :         case IPPROTO_ESP:
     175                 :            :                 return &esp6_protocol;
     176                 :         78 :         case IPPROTO_AH:
     177                 :         78 :                 return &ah6_protocol;
     178                 :          0 :         case IPPROTO_COMP:
     179                 :          0 :                 return &ipcomp6_protocol;
     180                 :            :         }
     181                 :            : 
     182                 :          0 :         return NULL;
     183                 :            : }
     184                 :            : 
     185                 :        156 : int xfrm6_protocol_register(struct xfrm6_protocol *handler,
     186                 :            :                             unsigned char protocol)
     187                 :            : {
     188                 :        156 :         struct xfrm6_protocol __rcu **pprev;
     189                 :        156 :         struct xfrm6_protocol *t;
     190                 :        156 :         bool add_netproto = false;
     191                 :        156 :         int ret = -EEXIST;
     192                 :        156 :         int priority = handler->priority;
     193                 :            : 
     194         [ +  - ]:        156 :         if (!proto_handlers(protocol) || !netproto(protocol))
     195                 :            :                 return -EINVAL;
     196                 :            : 
     197                 :        156 :         mutex_lock(&xfrm6_protocol_mutex);
     198                 :            : 
     199   [ +  -  -  +  :        234 :         if (!rcu_dereference_protected(*proto_handlers(protocol),
                   +  - ]
     200                 :            :                                        lockdep_is_held(&xfrm6_protocol_mutex)))
     201                 :        156 :                 add_netproto = true;
     202                 :            : 
     203   [ +  -  +  - ]:        156 :         for (pprev = proto_handlers(protocol);
     204         [ -  + ]:        156 :              (t = rcu_dereference_protected(*pprev,
     205                 :            :                         lockdep_is_held(&xfrm6_protocol_mutex))) != NULL;
     206                 :          0 :              pprev = &t->next) {
     207         [ #  # ]:          0 :                 if (t->priority < priority)
     208                 :            :                         break;
     209         [ #  # ]:          0 :                 if (t->priority == priority)
     210                 :          0 :                         goto err;
     211                 :            :         }
     212                 :            : 
     213                 :        156 :         handler->next = *pprev;
     214                 :        156 :         rcu_assign_pointer(*pprev, handler);
     215                 :            : 
     216                 :        156 :         ret = 0;
     217                 :            : 
     218                 :        156 : err:
     219                 :        156 :         mutex_unlock(&xfrm6_protocol_mutex);
     220                 :            : 
     221         [ +  - ]:        156 :         if (add_netproto) {
     222   [ +  -  -  +  :        234 :                 if (inet6_add_protocol(netproto(protocol), protocol)) {
                   -  + ]
     223                 :          0 :                         pr_err("%s: can't add protocol\n", __func__);
     224                 :          0 :                         ret = -EAGAIN;
     225                 :            :                 }
     226                 :            :         }
     227                 :            : 
     228                 :            :         return ret;
     229                 :            : }
     230                 :            : EXPORT_SYMBOL(xfrm6_protocol_register);
     231                 :            : 
     232                 :          0 : int xfrm6_protocol_deregister(struct xfrm6_protocol *handler,
     233                 :            :                               unsigned char protocol)
     234                 :            : {
     235                 :          0 :         struct xfrm6_protocol __rcu **pprev;
     236                 :          0 :         struct xfrm6_protocol *t;
     237                 :          0 :         int ret = -ENOENT;
     238                 :            : 
     239         [ #  # ]:          0 :         if (!proto_handlers(protocol) || !netproto(protocol))
     240                 :            :                 return -EINVAL;
     241                 :            : 
     242                 :          0 :         mutex_lock(&xfrm6_protocol_mutex);
     243                 :            : 
     244   [ #  #  #  # ]:          0 :         for (pprev = proto_handlers(protocol);
     245         [ #  # ]:          0 :              (t = rcu_dereference_protected(*pprev,
     246                 :            :                         lockdep_is_held(&xfrm6_protocol_mutex))) != NULL;
     247                 :          0 :              pprev = &t->next) {
     248         [ #  # ]:          0 :                 if (t == handler) {
     249                 :          0 :                         *pprev = handler->next;
     250                 :          0 :                         ret = 0;
     251                 :          0 :                         break;
     252                 :            :                 }
     253                 :            :         }
     254                 :            : 
     255   [ #  #  #  #  :          0 :         if (!rcu_dereference_protected(*proto_handlers(protocol),
                   #  # ]
     256                 :            :                                        lockdep_is_held(&xfrm6_protocol_mutex))) {
     257   [ #  #  #  #  :          0 :                 if (inet6_del_protocol(netproto(protocol), protocol) < 0) {
                   #  # ]
     258                 :          0 :                         pr_err("%s: can't remove protocol\n", __func__);
     259                 :          0 :                         ret = -EAGAIN;
     260                 :            :                 }
     261                 :            :         }
     262                 :            : 
     263                 :          0 :         mutex_unlock(&xfrm6_protocol_mutex);
     264                 :            : 
     265                 :          0 :         synchronize_net();
     266                 :            : 
     267                 :          0 :         return ret;
     268                 :            : }
     269                 :            : EXPORT_SYMBOL(xfrm6_protocol_deregister);
     270                 :            : 
     271                 :         78 : int __init xfrm6_protocol_init(void)
     272                 :            : {
     273                 :         78 :         return xfrm_input_register_afinfo(&xfrm6_input_afinfo);
     274                 :            : }
     275                 :            : 
     276                 :          0 : void xfrm6_protocol_fini(void)
     277                 :            : {
     278                 :          0 :         xfrm_input_unregister_afinfo(&xfrm6_input_afinfo);
     279                 :          0 : }

Generated by: LCOV version 1.14