LCOV - code coverage report
Current view: top level - net/sunrpc - svcauth_unix.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 20 461 4.3 %
Date: 2022-04-01 14:35:51 Functions: 2 36 5.6 %
Branches: 4 219 1.8 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0-only
       2                 :            : #include <linux/types.h>
       3                 :            : #include <linux/sched.h>
       4                 :            : #include <linux/module.h>
       5                 :            : #include <linux/sunrpc/types.h>
       6                 :            : #include <linux/sunrpc/xdr.h>
       7                 :            : #include <linux/sunrpc/svcsock.h>
       8                 :            : #include <linux/sunrpc/svcauth.h>
       9                 :            : #include <linux/sunrpc/gss_api.h>
      10                 :            : #include <linux/sunrpc/addr.h>
      11                 :            : #include <linux/err.h>
      12                 :            : #include <linux/seq_file.h>
      13                 :            : #include <linux/hash.h>
      14                 :            : #include <linux/string.h>
      15                 :            : #include <linux/slab.h>
      16                 :            : #include <net/sock.h>
      17                 :            : #include <net/ipv6.h>
      18                 :            : #include <linux/kernel.h>
      19                 :            : #include <linux/user_namespace.h>
      20                 :            : #define RPCDBG_FACILITY RPCDBG_AUTH
      21                 :            : 
      22                 :            : 
      23                 :            : #include "netns.h"
      24                 :            : 
      25                 :            : /*
      26                 :            :  * AUTHUNIX and AUTHNULL credentials are both handled here.
      27                 :            :  * AUTHNULL is treated just like AUTHUNIX except that the uid/gid
      28                 :            :  * are always nobody (-2).  i.e. we do the same IP address checks for
      29                 :            :  * AUTHNULL as for AUTHUNIX, and that is done here.
      30                 :            :  */
      31                 :            : 
      32                 :            : 
      33                 :            : struct unix_domain {
      34                 :            :         struct auth_domain      h;
      35                 :            :         /* other stuff later */
      36                 :            : };
      37                 :            : 
      38                 :            : extern struct auth_ops svcauth_null;
      39                 :            : extern struct auth_ops svcauth_unix;
      40                 :            : 
      41                 :          0 : static void svcauth_unix_domain_release_rcu(struct rcu_head *head)
      42                 :            : {
      43                 :          0 :         struct auth_domain *dom = container_of(head, struct auth_domain, rcu_head);
      44                 :          0 :         struct unix_domain *ud = container_of(dom, struct unix_domain, h);
      45                 :            : 
      46                 :          0 :         kfree(dom->name);
      47                 :          0 :         kfree(ud);
      48                 :          0 : }
      49                 :            : 
      50                 :          0 : static void svcauth_unix_domain_release(struct auth_domain *dom)
      51                 :            : {
      52                 :          0 :         call_rcu(&dom->rcu_head, svcauth_unix_domain_release_rcu);
      53                 :          0 : }
      54                 :            : 
      55                 :          0 : struct auth_domain *unix_domain_find(char *name)
      56                 :            : {
      57                 :          0 :         struct auth_domain *rv;
      58                 :          0 :         struct unix_domain *new = NULL;
      59                 :            : 
      60                 :          0 :         rv = auth_domain_find(name);
      61                 :          0 :         while(1) {
      62         [ #  # ]:          0 :                 if (rv) {
      63   [ #  #  #  # ]:          0 :                         if (new && rv != &new->h)
      64                 :          0 :                                 svcauth_unix_domain_release(&new->h);
      65                 :            : 
      66         [ #  # ]:          0 :                         if (rv->flavour != &svcauth_unix) {
      67                 :          0 :                                 auth_domain_put(rv);
      68                 :          0 :                                 return NULL;
      69                 :            :                         }
      70                 :            :                         return rv;
      71                 :            :                 }
      72                 :            : 
      73                 :          0 :                 new = kmalloc(sizeof(*new), GFP_KERNEL);
      74         [ #  # ]:          0 :                 if (new == NULL)
      75                 :            :                         return NULL;
      76                 :          0 :                 kref_init(&new->h.ref);
      77                 :          0 :                 new->h.name = kstrdup(name, GFP_KERNEL);
      78         [ #  # ]:          0 :                 if (new->h.name == NULL) {
      79                 :          0 :                         kfree(new);
      80                 :          0 :                         return NULL;
      81                 :            :                 }
      82                 :          0 :                 new->h.flavour = &svcauth_unix;
      83                 :          0 :                 rv = auth_domain_lookup(name, &new->h);
      84                 :            :         }
      85                 :            : }
      86                 :            : EXPORT_SYMBOL_GPL(unix_domain_find);
      87                 :            : 
      88                 :            : 
      89                 :            : /**************************************************
      90                 :            :  * cache for IP address to unix_domain
      91                 :            :  * as needed by AUTH_UNIX
      92                 :            :  */
      93                 :            : #define IP_HASHBITS     8
      94                 :            : #define IP_HASHMAX      (1<<IP_HASHBITS)
      95                 :            : 
      96                 :            : struct ip_map {
      97                 :            :         struct cache_head       h;
      98                 :            :         char                    m_class[8]; /* e.g. "nfsd" */
      99                 :            :         struct in6_addr         m_addr;
     100                 :            :         struct unix_domain      *m_client;
     101                 :            :         struct rcu_head         m_rcu;
     102                 :            : };
     103                 :            : 
     104                 :          0 : static void ip_map_put(struct kref *kref)
     105                 :            : {
     106                 :          0 :         struct cache_head *item = container_of(kref, struct cache_head, ref);
     107                 :          0 :         struct ip_map *im = container_of(item, struct ip_map,h);
     108                 :            : 
     109   [ #  #  #  # ]:          0 :         if (test_bit(CACHE_VALID, &item->flags) &&
     110                 :            :             !test_bit(CACHE_NEGATIVE, &item->flags))
     111                 :          0 :                 auth_domain_put(&im->m_client->h);
     112         [ #  # ]:          0 :         kfree_rcu(im, m_rcu);
     113                 :          0 : }
     114                 :            : 
     115                 :          0 : static inline int hash_ip6(const struct in6_addr *ip)
     116                 :            : {
     117                 :          0 :         return hash_32(ipv6_addr_hash(ip), IP_HASHBITS);
     118                 :            : }
     119                 :          0 : static int ip_map_match(struct cache_head *corig, struct cache_head *cnew)
     120                 :            : {
     121                 :          0 :         struct ip_map *orig = container_of(corig, struct ip_map, h);
     122                 :          0 :         struct ip_map *new = container_of(cnew, struct ip_map, h);
     123   [ #  #  #  # ]:          0 :         return strcmp(orig->m_class, new->m_class) == 0 &&
     124                 :            :                ipv6_addr_equal(&orig->m_addr, &new->m_addr);
     125                 :            : }
     126                 :          0 : static void ip_map_init(struct cache_head *cnew, struct cache_head *citem)
     127                 :            : {
     128                 :          0 :         struct ip_map *new = container_of(cnew, struct ip_map, h);
     129                 :          0 :         struct ip_map *item = container_of(citem, struct ip_map, h);
     130                 :            : 
     131                 :          0 :         strcpy(new->m_class, item->m_class);
     132                 :          0 :         new->m_addr = item->m_addr;
     133                 :          0 : }
     134                 :          0 : static void update(struct cache_head *cnew, struct cache_head *citem)
     135                 :            : {
     136                 :          0 :         struct ip_map *new = container_of(cnew, struct ip_map, h);
     137                 :          0 :         struct ip_map *item = container_of(citem, struct ip_map, h);
     138                 :            : 
     139                 :          0 :         kref_get(&item->m_client->h.ref);
     140                 :          0 :         new->m_client = item->m_client;
     141                 :          0 : }
     142                 :          0 : static struct cache_head *ip_map_alloc(void)
     143                 :            : {
     144                 :          0 :         struct ip_map *i = kmalloc(sizeof(*i), GFP_KERNEL);
     145         [ #  # ]:          0 :         if (i)
     146                 :          0 :                 return &i->h;
     147                 :            :         else
     148                 :            :                 return NULL;
     149                 :            : }
     150                 :            : 
     151                 :          0 : static void ip_map_request(struct cache_detail *cd,
     152                 :            :                                   struct cache_head *h,
     153                 :            :                                   char **bpp, int *blen)
     154                 :            : {
     155                 :          0 :         char text_addr[40];
     156                 :          0 :         struct ip_map *im = container_of(h, struct ip_map, h);
     157                 :            : 
     158         [ #  # ]:          0 :         if (ipv6_addr_v4mapped(&(im->m_addr))) {
     159                 :          0 :                 snprintf(text_addr, 20, "%pI4", &im->m_addr.s6_addr32[3]);
     160                 :            :         } else {
     161                 :          0 :                 snprintf(text_addr, 40, "%pI6", &im->m_addr);
     162                 :            :         }
     163                 :          0 :         qword_add(bpp, blen, im->m_class);
     164                 :          0 :         qword_add(bpp, blen, text_addr);
     165                 :          0 :         (*bpp)[-1] = '\n';
     166                 :          0 : }
     167                 :            : 
     168                 :            : static struct ip_map *__ip_map_lookup(struct cache_detail *cd, char *class, struct in6_addr *addr);
     169                 :            : static int __ip_map_update(struct cache_detail *cd, struct ip_map *ipm, struct unix_domain *udom, time64_t expiry);
     170                 :            : 
     171                 :          0 : static int ip_map_parse(struct cache_detail *cd,
     172                 :            :                           char *mesg, int mlen)
     173                 :            : {
     174                 :            :         /* class ipaddress [domainname] */
     175                 :            :         /* should be safe just to use the start of the input buffer
     176                 :            :          * for scratch: */
     177                 :          0 :         char *buf = mesg;
     178                 :          0 :         int len;
     179                 :          0 :         char class[8];
     180                 :          0 :         union {
     181                 :            :                 struct sockaddr         sa;
     182                 :            :                 struct sockaddr_in      s4;
     183                 :            :                 struct sockaddr_in6     s6;
     184                 :            :         } address;
     185                 :          0 :         struct sockaddr_in6 sin6;
     186                 :          0 :         int err;
     187                 :            : 
     188                 :          0 :         struct ip_map *ipmp;
     189                 :          0 :         struct auth_domain *dom;
     190                 :          0 :         time64_t expiry;
     191                 :            : 
     192         [ #  # ]:          0 :         if (mesg[mlen-1] != '\n')
     193                 :            :                 return -EINVAL;
     194                 :          0 :         mesg[mlen-1] = 0;
     195                 :            : 
     196                 :            :         /* class */
     197                 :          0 :         len = qword_get(&mesg, class, sizeof(class));
     198         [ #  # ]:          0 :         if (len <= 0) return -EINVAL;
     199                 :            : 
     200                 :            :         /* ip address */
     201                 :          0 :         len = qword_get(&mesg, buf, mlen);
     202         [ #  # ]:          0 :         if (len <= 0) return -EINVAL;
     203                 :            : 
     204         [ #  # ]:          0 :         if (rpc_pton(cd->net, buf, len, &address.sa, sizeof(address)) == 0)
     205                 :            :                 return -EINVAL;
     206      [ #  #  # ]:          0 :         switch (address.sa.sa_family) {
     207                 :          0 :         case AF_INET:
     208                 :            :                 /* Form a mapped IPv4 address in sin6 */
     209                 :          0 :                 sin6.sin6_family = AF_INET6;
     210         [ #  # ]:          0 :                 ipv6_addr_set_v4mapped(address.s4.sin_addr.s_addr,
     211                 :            :                                 &sin6.sin6_addr);
     212                 :            :                 break;
     213                 :            : #if IS_ENABLED(CONFIG_IPV6)
     214                 :          0 :         case AF_INET6:
     215                 :          0 :                 memcpy(&sin6, &address.s6, sizeof(sin6));
     216                 :          0 :                 break;
     217                 :            : #endif
     218                 :            :         default:
     219                 :            :                 return -EINVAL;
     220                 :            :         }
     221                 :            : 
     222                 :          0 :         expiry = get_expiry(&mesg);
     223         [ #  # ]:          0 :         if (expiry ==0)
     224                 :            :                 return -EINVAL;
     225                 :            : 
     226                 :            :         /* domainname, or empty for NEGATIVE */
     227                 :          0 :         len = qword_get(&mesg, buf, mlen);
     228         [ #  # ]:          0 :         if (len < 0) return -EINVAL;
     229                 :            : 
     230         [ #  # ]:          0 :         if (len) {
     231                 :          0 :                 dom = unix_domain_find(buf);
     232         [ #  # ]:          0 :                 if (dom == NULL)
     233                 :            :                         return -ENOENT;
     234                 :            :         } else
     235                 :            :                 dom = NULL;
     236                 :            : 
     237                 :            :         /* IPv6 scope IDs are ignored for now */
     238                 :          0 :         ipmp = __ip_map_lookup(cd, class, &sin6.sin6_addr);
     239         [ #  # ]:          0 :         if (ipmp) {
     240                 :          0 :                 err = __ip_map_update(cd, ipmp,
     241                 :          0 :                              container_of(dom, struct unix_domain, h),
     242                 :            :                              expiry);
     243                 :            :         } else
     244                 :            :                 err = -ENOMEM;
     245                 :            : 
     246         [ #  # ]:          0 :         if (dom)
     247                 :          0 :                 auth_domain_put(dom);
     248                 :            : 
     249                 :          0 :         cache_flush();
     250                 :          0 :         return err;
     251                 :            : }
     252                 :            : 
     253                 :          0 : static int ip_map_show(struct seq_file *m,
     254                 :            :                        struct cache_detail *cd,
     255                 :            :                        struct cache_head *h)
     256                 :            : {
     257                 :          0 :         struct ip_map *im;
     258                 :          0 :         struct in6_addr addr;
     259                 :          0 :         char *dom = "-no-domain-";
     260                 :            : 
     261         [ #  # ]:          0 :         if (h == NULL) {
     262                 :          0 :                 seq_puts(m, "#class IP domain\n");
     263                 :          0 :                 return 0;
     264                 :            :         }
     265                 :          0 :         im = container_of(h, struct ip_map, h);
     266                 :            :         /* class addr domain */
     267                 :          0 :         addr = im->m_addr;
     268                 :            : 
     269   [ #  #  #  # ]:          0 :         if (test_bit(CACHE_VALID, &h->flags) &&
     270                 :            :             !test_bit(CACHE_NEGATIVE, &h->flags))
     271                 :          0 :                 dom = im->m_client->h.name;
     272                 :            : 
     273         [ #  # ]:          0 :         if (ipv6_addr_v4mapped(&addr)) {
     274                 :          0 :                 seq_printf(m, "%s %pI4 %s\n",
     275                 :          0 :                         im->m_class, &addr.s6_addr32[3], dom);
     276                 :            :         } else {
     277                 :          0 :                 seq_printf(m, "%s %pI6 %s\n", im->m_class, &addr, dom);
     278                 :            :         }
     279                 :            :         return 0;
     280                 :            : }
     281                 :            : 
     282                 :            : 
     283                 :          0 : static struct ip_map *__ip_map_lookup(struct cache_detail *cd, char *class,
     284                 :            :                 struct in6_addr *addr)
     285                 :            : {
     286                 :          0 :         struct ip_map ip;
     287                 :          0 :         struct cache_head *ch;
     288                 :            : 
     289                 :          0 :         strcpy(ip.m_class, class);
     290                 :          0 :         ip.m_addr = *addr;
     291                 :          0 :         ch = sunrpc_cache_lookup_rcu(cd, &ip.h,
     292                 :          0 :                                      hash_str(class, IP_HASHBITS) ^
     293                 :            :                                      hash_ip6(addr));
     294                 :            : 
     295         [ #  # ]:          0 :         if (ch)
     296                 :            :                 return container_of(ch, struct ip_map, h);
     297                 :            :         else
     298                 :          0 :                 return NULL;
     299                 :            : }
     300                 :            : 
     301                 :            : static inline struct ip_map *ip_map_lookup(struct net *net, char *class,
     302                 :            :                 struct in6_addr *addr)
     303                 :            : {
     304                 :            :         struct sunrpc_net *sn;
     305                 :            : 
     306                 :            :         sn = net_generic(net, sunrpc_net_id);
     307                 :            :         return __ip_map_lookup(sn->ip_map_cache, class, addr);
     308                 :            : }
     309                 :            : 
     310                 :          0 : static int __ip_map_update(struct cache_detail *cd, struct ip_map *ipm,
     311                 :            :                 struct unix_domain *udom, time64_t expiry)
     312                 :            : {
     313                 :          0 :         struct ip_map ip;
     314                 :          0 :         struct cache_head *ch;
     315                 :            : 
     316                 :          0 :         ip.m_client = udom;
     317                 :          0 :         ip.h.flags = 0;
     318         [ #  # ]:          0 :         if (!udom)
     319                 :          0 :                 set_bit(CACHE_NEGATIVE, &ip.h.flags);
     320                 :          0 :         ip.h.expiry_time = expiry;
     321                 :          0 :         ch = sunrpc_cache_update(cd, &ip.h, &ipm->h,
     322                 :          0 :                                  hash_str(ipm->m_class, IP_HASHBITS) ^
     323                 :            :                                  hash_ip6(&ipm->m_addr));
     324         [ #  # ]:          0 :         if (!ch)
     325                 :            :                 return -ENOMEM;
     326                 :          0 :         cache_put(ch, cd);
     327                 :          0 :         return 0;
     328                 :            : }
     329                 :            : 
     330                 :            : static inline int ip_map_update(struct net *net, struct ip_map *ipm,
     331                 :            :                 struct unix_domain *udom, time64_t expiry)
     332                 :            : {
     333                 :            :         struct sunrpc_net *sn;
     334                 :            : 
     335                 :            :         sn = net_generic(net, sunrpc_net_id);
     336                 :            :         return __ip_map_update(sn->ip_map_cache, ipm, udom, expiry);
     337                 :            : }
     338                 :            : 
     339                 :          0 : void svcauth_unix_purge(struct net *net)
     340                 :            : {
     341                 :          0 :         struct sunrpc_net *sn;
     342                 :            : 
     343                 :          0 :         sn = net_generic(net, sunrpc_net_id);
     344                 :          0 :         cache_purge(sn->ip_map_cache);
     345                 :          0 : }
     346                 :            : EXPORT_SYMBOL_GPL(svcauth_unix_purge);
     347                 :            : 
     348                 :            : static inline struct ip_map *
     349                 :          0 : ip_map_cached_get(struct svc_xprt *xprt)
     350                 :            : {
     351                 :          0 :         struct ip_map *ipm = NULL;
     352                 :          0 :         struct sunrpc_net *sn;
     353                 :            : 
     354         [ #  # ]:          0 :         if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags)) {
     355                 :          0 :                 spin_lock(&xprt->xpt_lock);
     356                 :          0 :                 ipm = xprt->xpt_auth_cache;
     357         [ #  # ]:          0 :                 if (ipm != NULL) {
     358                 :          0 :                         sn = net_generic(xprt->xpt_net, sunrpc_net_id);
     359         [ #  # ]:          0 :                         if (cache_is_expired(sn->ip_map_cache, &ipm->h)) {
     360                 :            :                                 /*
     361                 :            :                                  * The entry has been invalidated since it was
     362                 :            :                                  * remembered, e.g. by a second mount from the
     363                 :            :                                  * same IP address.
     364                 :            :                                  */
     365                 :          0 :                                 xprt->xpt_auth_cache = NULL;
     366                 :          0 :                                 spin_unlock(&xprt->xpt_lock);
     367                 :          0 :                                 cache_put(&ipm->h, sn->ip_map_cache);
     368                 :          0 :                                 return NULL;
     369                 :            :                         }
     370                 :          0 :                         cache_get(&ipm->h);
     371                 :            :                 }
     372                 :          0 :                 spin_unlock(&xprt->xpt_lock);
     373                 :            :         }
     374                 :            :         return ipm;
     375                 :            : }
     376                 :            : 
     377                 :            : static inline void
     378                 :          0 : ip_map_cached_put(struct svc_xprt *xprt, struct ip_map *ipm)
     379                 :            : {
     380         [ #  # ]:          0 :         if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags)) {
     381                 :          0 :                 spin_lock(&xprt->xpt_lock);
     382         [ #  # ]:          0 :                 if (xprt->xpt_auth_cache == NULL) {
     383                 :            :                         /* newly cached, keep the reference */
     384                 :          0 :                         xprt->xpt_auth_cache = ipm;
     385                 :          0 :                         ipm = NULL;
     386                 :            :                 }
     387                 :          0 :                 spin_unlock(&xprt->xpt_lock);
     388                 :            :         }
     389         [ #  # ]:          0 :         if (ipm) {
     390                 :          0 :                 struct sunrpc_net *sn;
     391                 :            : 
     392                 :          0 :                 sn = net_generic(xprt->xpt_net, sunrpc_net_id);
     393                 :          0 :                 cache_put(&ipm->h, sn->ip_map_cache);
     394                 :            :         }
     395                 :          0 : }
     396                 :            : 
     397                 :            : void
     398                 :          0 : svcauth_unix_info_release(struct svc_xprt *xpt)
     399                 :            : {
     400                 :          0 :         struct ip_map *ipm;
     401                 :            : 
     402                 :          0 :         ipm = xpt->xpt_auth_cache;
     403         [ #  # ]:          0 :         if (ipm != NULL) {
     404                 :          0 :                 struct sunrpc_net *sn;
     405                 :            : 
     406                 :          0 :                 sn = net_generic(xpt->xpt_net, sunrpc_net_id);
     407                 :          0 :                 cache_put(&ipm->h, sn->ip_map_cache);
     408                 :            :         }
     409                 :          0 : }
     410                 :            : 
     411                 :            : /****************************************************************************
     412                 :            :  * auth.unix.gid cache
     413                 :            :  * simple cache to map a UID to a list of GIDs
     414                 :            :  * because AUTH_UNIX aka AUTH_SYS has a max of UNX_NGROUPS
     415                 :            :  */
     416                 :            : #define GID_HASHBITS    8
     417                 :            : #define GID_HASHMAX     (1<<GID_HASHBITS)
     418                 :            : 
     419                 :            : struct unix_gid {
     420                 :            :         struct cache_head       h;
     421                 :            :         kuid_t                  uid;
     422                 :            :         struct group_info       *gi;
     423                 :            :         struct rcu_head         rcu;
     424                 :            : };
     425                 :            : 
     426                 :          0 : static int unix_gid_hash(kuid_t uid)
     427                 :            : {
     428                 :          0 :         return hash_long(from_kuid(&init_user_ns, uid), GID_HASHBITS);
     429                 :            : }
     430                 :            : 
     431                 :          0 : static void unix_gid_put(struct kref *kref)
     432                 :            : {
     433                 :          0 :         struct cache_head *item = container_of(kref, struct cache_head, ref);
     434                 :          0 :         struct unix_gid *ug = container_of(item, struct unix_gid, h);
     435   [ #  #  #  # ]:          0 :         if (test_bit(CACHE_VALID, &item->flags) &&
     436                 :            :             !test_bit(CACHE_NEGATIVE, &item->flags))
     437         [ #  # ]:          0 :                 put_group_info(ug->gi);
     438         [ #  # ]:          0 :         kfree_rcu(ug, rcu);
     439                 :          0 : }
     440                 :            : 
     441                 :          0 : static int unix_gid_match(struct cache_head *corig, struct cache_head *cnew)
     442                 :            : {
     443                 :          0 :         struct unix_gid *orig = container_of(corig, struct unix_gid, h);
     444                 :          0 :         struct unix_gid *new = container_of(cnew, struct unix_gid, h);
     445                 :          0 :         return uid_eq(orig->uid, new->uid);
     446                 :            : }
     447                 :          0 : static void unix_gid_init(struct cache_head *cnew, struct cache_head *citem)
     448                 :            : {
     449                 :          0 :         struct unix_gid *new = container_of(cnew, struct unix_gid, h);
     450                 :          0 :         struct unix_gid *item = container_of(citem, struct unix_gid, h);
     451                 :          0 :         new->uid = item->uid;
     452                 :          0 : }
     453                 :          0 : static void unix_gid_update(struct cache_head *cnew, struct cache_head *citem)
     454                 :            : {
     455                 :          0 :         struct unix_gid *new = container_of(cnew, struct unix_gid, h);
     456                 :          0 :         struct unix_gid *item = container_of(citem, struct unix_gid, h);
     457                 :            : 
     458                 :          0 :         get_group_info(item->gi);
     459                 :          0 :         new->gi = item->gi;
     460                 :          0 : }
     461                 :          0 : static struct cache_head *unix_gid_alloc(void)
     462                 :            : {
     463                 :          0 :         struct unix_gid *g = kmalloc(sizeof(*g), GFP_KERNEL);
     464         [ #  # ]:          0 :         if (g)
     465                 :          0 :                 return &g->h;
     466                 :            :         else
     467                 :            :                 return NULL;
     468                 :            : }
     469                 :            : 
     470                 :          0 : static void unix_gid_request(struct cache_detail *cd,
     471                 :            :                              struct cache_head *h,
     472                 :            :                              char **bpp, int *blen)
     473                 :            : {
     474                 :          0 :         char tuid[20];
     475                 :          0 :         struct unix_gid *ug = container_of(h, struct unix_gid, h);
     476                 :            : 
     477                 :          0 :         snprintf(tuid, 20, "%u", from_kuid(&init_user_ns, ug->uid));
     478                 :          0 :         qword_add(bpp, blen, tuid);
     479                 :          0 :         (*bpp)[-1] = '\n';
     480                 :          0 : }
     481                 :            : 
     482                 :            : static struct unix_gid *unix_gid_lookup(struct cache_detail *cd, kuid_t uid);
     483                 :            : 
     484                 :          0 : static int unix_gid_parse(struct cache_detail *cd,
     485                 :            :                         char *mesg, int mlen)
     486                 :            : {
     487                 :            :         /* uid expiry Ngid gid0 gid1 ... gidN-1 */
     488                 :          0 :         int id;
     489                 :          0 :         kuid_t uid;
     490                 :          0 :         int gids;
     491                 :          0 :         int rv;
     492                 :          0 :         int i;
     493                 :          0 :         int err;
     494                 :          0 :         time64_t expiry;
     495                 :          0 :         struct unix_gid ug, *ugp;
     496                 :            : 
     497         [ #  # ]:          0 :         if (mesg[mlen - 1] != '\n')
     498                 :            :                 return -EINVAL;
     499                 :          0 :         mesg[mlen-1] = 0;
     500                 :            : 
     501                 :          0 :         rv = get_int(&mesg, &id);
     502         [ #  # ]:          0 :         if (rv)
     503                 :            :                 return -EINVAL;
     504                 :          0 :         uid = make_kuid(current_user_ns(), id);
     505                 :          0 :         ug.uid = uid;
     506                 :            : 
     507                 :          0 :         expiry = get_expiry(&mesg);
     508         [ #  # ]:          0 :         if (expiry == 0)
     509                 :            :                 return -EINVAL;
     510                 :            : 
     511                 :          0 :         rv = get_int(&mesg, &gids);
     512   [ #  #  #  #  :          0 :         if (rv || gids < 0 || gids > 8192)
                   #  # ]
     513                 :            :                 return -EINVAL;
     514                 :            : 
     515                 :          0 :         ug.gi = groups_alloc(gids);
     516         [ #  # ]:          0 :         if (!ug.gi)
     517                 :            :                 return -ENOMEM;
     518                 :            : 
     519         [ #  # ]:          0 :         for (i = 0 ; i < gids ; i++) {
     520                 :          0 :                 int gid;
     521                 :          0 :                 kgid_t kgid;
     522                 :          0 :                 rv = get_int(&mesg, &gid);
     523                 :          0 :                 err = -EINVAL;
     524         [ #  # ]:          0 :                 if (rv)
     525                 :          0 :                         goto out;
     526         [ #  # ]:          0 :                 kgid = make_kgid(current_user_ns(), gid);
     527         [ #  # ]:          0 :                 if (!gid_valid(kgid))
     528                 :          0 :                         goto out;
     529                 :          0 :                 ug.gi->gid[i] = kgid;
     530                 :            :         }
     531                 :            : 
     532                 :          0 :         groups_sort(ug.gi);
     533                 :          0 :         ugp = unix_gid_lookup(cd, uid);
     534         [ #  # ]:          0 :         if (ugp) {
     535                 :          0 :                 struct cache_head *ch;
     536                 :          0 :                 ug.h.flags = 0;
     537                 :          0 :                 ug.h.expiry_time = expiry;
     538                 :          0 :                 ch = sunrpc_cache_update(cd,
     539                 :            :                                          &ug.h, &ugp->h,
     540                 :            :                                          unix_gid_hash(uid));
     541         [ #  # ]:          0 :                 if (!ch)
     542                 :            :                         err = -ENOMEM;
     543                 :            :                 else {
     544                 :          0 :                         err = 0;
     545                 :          0 :                         cache_put(ch, cd);
     546                 :            :                 }
     547                 :            :         } else
     548                 :            :                 err = -ENOMEM;
     549                 :          0 :  out:
     550         [ #  # ]:          0 :         if (ug.gi)
     551         [ #  # ]:          0 :                 put_group_info(ug.gi);
     552                 :            :         return err;
     553                 :            : }
     554                 :            : 
     555                 :          0 : static int unix_gid_show(struct seq_file *m,
     556                 :            :                          struct cache_detail *cd,
     557                 :            :                          struct cache_head *h)
     558                 :            : {
     559                 :          0 :         struct user_namespace *user_ns = m->file->f_cred->user_ns;
     560                 :          0 :         struct unix_gid *ug;
     561                 :          0 :         int i;
     562                 :          0 :         int glen;
     563                 :            : 
     564         [ #  # ]:          0 :         if (h == NULL) {
     565                 :          0 :                 seq_puts(m, "#uid cnt: gids...\n");
     566                 :          0 :                 return 0;
     567                 :            :         }
     568                 :          0 :         ug = container_of(h, struct unix_gid, h);
     569   [ #  #  #  # ]:          0 :         if (test_bit(CACHE_VALID, &h->flags) &&
     570                 :            :             !test_bit(CACHE_NEGATIVE, &h->flags))
     571                 :          0 :                 glen = ug->gi->ngroups;
     572                 :            :         else
     573                 :            :                 glen = 0;
     574                 :            : 
     575         [ #  # ]:          0 :         seq_printf(m, "%u %d:", from_kuid_munged(user_ns, ug->uid), glen);
     576         [ #  # ]:          0 :         for (i = 0; i < glen; i++)
     577         [ #  # ]:          0 :                 seq_printf(m, " %d", from_kgid_munged(user_ns, ug->gi->gid[i]));
     578                 :          0 :         seq_printf(m, "\n");
     579                 :          0 :         return 0;
     580                 :            : }
     581                 :            : 
     582                 :            : static const struct cache_detail unix_gid_cache_template = {
     583                 :            :         .owner          = THIS_MODULE,
     584                 :            :         .hash_size      = GID_HASHMAX,
     585                 :            :         .name           = "auth.unix.gid",
     586                 :            :         .cache_put      = unix_gid_put,
     587                 :            :         .cache_request  = unix_gid_request,
     588                 :            :         .cache_parse    = unix_gid_parse,
     589                 :            :         .cache_show     = unix_gid_show,
     590                 :            :         .match          = unix_gid_match,
     591                 :            :         .init           = unix_gid_init,
     592                 :            :         .update         = unix_gid_update,
     593                 :            :         .alloc          = unix_gid_alloc,
     594                 :            : };
     595                 :            : 
     596                 :         21 : int unix_gid_cache_create(struct net *net)
     597                 :            : {
     598                 :         21 :         struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
     599                 :         21 :         struct cache_detail *cd;
     600                 :         21 :         int err;
     601                 :            : 
     602                 :         21 :         cd = cache_create_net(&unix_gid_cache_template, net);
     603         [ -  + ]:         21 :         if (IS_ERR(cd))
     604                 :          0 :                 return PTR_ERR(cd);
     605                 :         21 :         err = cache_register_net(cd, net);
     606         [ -  + ]:         21 :         if (err) {
     607                 :          0 :                 cache_destroy_net(cd, net);
     608                 :          0 :                 return err;
     609                 :            :         }
     610                 :         21 :         sn->unix_gid_cache = cd;
     611                 :         21 :         return 0;
     612                 :            : }
     613                 :            : 
     614                 :          0 : void unix_gid_cache_destroy(struct net *net)
     615                 :            : {
     616                 :          0 :         struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
     617                 :          0 :         struct cache_detail *cd = sn->unix_gid_cache;
     618                 :            : 
     619                 :          0 :         sn->unix_gid_cache = NULL;
     620                 :          0 :         cache_purge(cd);
     621                 :          0 :         cache_unregister_net(cd, net);
     622                 :          0 :         cache_destroy_net(cd, net);
     623                 :          0 : }
     624                 :            : 
     625                 :          0 : static struct unix_gid *unix_gid_lookup(struct cache_detail *cd, kuid_t uid)
     626                 :            : {
     627                 :          0 :         struct unix_gid ug;
     628                 :          0 :         struct cache_head *ch;
     629                 :            : 
     630                 :          0 :         ug.uid = uid;
     631                 :          0 :         ch = sunrpc_cache_lookup_rcu(cd, &ug.h, unix_gid_hash(uid));
     632   [ #  #  #  # ]:          0 :         if (ch)
     633                 :            :                 return container_of(ch, struct unix_gid, h);
     634                 :            :         else
     635                 :          0 :                 return NULL;
     636                 :            : }
     637                 :            : 
     638                 :          0 : static struct group_info *unix_gid_find(kuid_t uid, struct svc_rqst *rqstp)
     639                 :            : {
     640                 :          0 :         struct unix_gid *ug;
     641                 :          0 :         struct group_info *gi;
     642                 :          0 :         int ret;
     643                 :          0 :         struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net,
     644                 :            :                                             sunrpc_net_id);
     645                 :            : 
     646                 :          0 :         ug = unix_gid_lookup(sn->unix_gid_cache, uid);
     647                 :          0 :         if (!ug)
     648                 :          0 :                 return ERR_PTR(-EAGAIN);
     649                 :          0 :         ret = cache_check(sn->unix_gid_cache, &ug->h, &rqstp->rq_chandle);
     650   [ #  #  #  # ]:          0 :         switch (ret) {
     651                 :            :         case -ENOENT:
     652                 :            :                 return ERR_PTR(-ENOENT);
     653                 :            :         case -ETIMEDOUT:
     654                 :          0 :                 return ERR_PTR(-ESHUTDOWN);
     655                 :          0 :         case 0:
     656                 :          0 :                 gi = get_group_info(ug->gi);
     657                 :          0 :                 cache_put(&ug->h, sn->unix_gid_cache);
     658                 :          0 :                 return gi;
     659                 :            :         default:
     660                 :          0 :                 return ERR_PTR(-EAGAIN);
     661                 :            :         }
     662                 :            : }
     663                 :            : 
     664                 :            : int
     665                 :          0 : svcauth_unix_set_client(struct svc_rqst *rqstp)
     666                 :            : {
     667                 :          0 :         struct sockaddr_in *sin;
     668                 :          0 :         struct sockaddr_in6 *sin6, sin6_storage;
     669                 :          0 :         struct ip_map *ipm;
     670                 :          0 :         struct group_info *gi;
     671                 :          0 :         struct svc_cred *cred = &rqstp->rq_cred;
     672                 :          0 :         struct svc_xprt *xprt = rqstp->rq_xprt;
     673                 :          0 :         struct net *net = xprt->xpt_net;
     674                 :          0 :         struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
     675                 :            : 
     676      [ #  #  # ]:          0 :         switch (rqstp->rq_addr.ss_family) {
     677                 :            :         case AF_INET:
     678         [ #  # ]:          0 :                 sin = svc_addr_in(rqstp);
     679                 :          0 :                 sin6 = &sin6_storage;
     680         [ #  # ]:          0 :                 ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &sin6->sin6_addr);
     681                 :            :                 break;
     682                 :            :         case AF_INET6:
     683                 :          0 :                 sin6 = svc_addr_in6(rqstp);
     684                 :          0 :                 break;
     685                 :          0 :         default:
     686                 :          0 :                 BUG();
     687                 :            :         }
     688                 :            : 
     689                 :          0 :         rqstp->rq_client = NULL;
     690         [ #  # ]:          0 :         if (rqstp->rq_proc == 0)
     691                 :            :                 return SVC_OK;
     692                 :            : 
     693                 :          0 :         ipm = ip_map_cached_get(xprt);
     694         [ #  # ]:          0 :         if (ipm == NULL)
     695                 :          0 :                 ipm = __ip_map_lookup(sn->ip_map_cache, rqstp->rq_server->sv_program->pg_class,
     696                 :            :                                     &sin6->sin6_addr);
     697                 :            : 
     698         [ #  # ]:          0 :         if (ipm == NULL)
     699                 :            :                 return SVC_DENIED;
     700                 :            : 
     701   [ #  #  #  #  :          0 :         switch (cache_check(sn->ip_map_cache, &ipm->h, &rqstp->rq_chandle)) {
                      # ]
     702                 :          0 :                 default:
     703                 :          0 :                         BUG();
     704                 :            :                 case -ETIMEDOUT:
     705                 :            :                         return SVC_CLOSE;
     706                 :          0 :                 case -EAGAIN:
     707                 :          0 :                         return SVC_DROP;
     708                 :          0 :                 case -ENOENT:
     709                 :          0 :                         return SVC_DENIED;
     710                 :          0 :                 case 0:
     711                 :          0 :                         rqstp->rq_client = &ipm->m_client->h;
     712                 :          0 :                         kref_get(&rqstp->rq_client->ref);
     713                 :          0 :                         ip_map_cached_put(xprt, ipm);
     714                 :          0 :                         break;
     715                 :            :         }
     716                 :            : 
     717                 :          0 :         gi = unix_gid_find(cred->cr_uid, rqstp);
     718   [ #  #  #  # ]:          0 :         switch (PTR_ERR(gi)) {
     719                 :            :         case -EAGAIN:
     720                 :            :                 return SVC_DROP;
     721                 :          0 :         case -ESHUTDOWN:
     722                 :          0 :                 return SVC_CLOSE;
     723                 :            :         case -ENOENT:
     724                 :            :                 break;
     725                 :          0 :         default:
     726         [ #  # ]:          0 :                 put_group_info(cred->cr_group_info);
     727                 :          0 :                 cred->cr_group_info = gi;
     728                 :            :         }
     729                 :            :         return SVC_OK;
     730                 :            : }
     731                 :            : 
     732                 :            : EXPORT_SYMBOL_GPL(svcauth_unix_set_client);
     733                 :            : 
     734                 :            : static int
     735                 :          0 : svcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp)
     736                 :            : {
     737                 :          0 :         struct kvec     *argv = &rqstp->rq_arg.head[0];
     738                 :          0 :         struct kvec     *resv = &rqstp->rq_res.head[0];
     739                 :          0 :         struct svc_cred *cred = &rqstp->rq_cred;
     740                 :            : 
     741         [ #  # ]:          0 :         if (argv->iov_len < 3*4)
     742                 :            :                 return SVC_GARBAGE;
     743                 :            : 
     744         [ #  # ]:          0 :         if (svc_getu32(argv) != 0) {
     745                 :          0 :                 dprintk("svc: bad null cred\n");
     746                 :          0 :                 *authp = rpc_autherr_badcred;
     747                 :          0 :                 return SVC_DENIED;
     748                 :            :         }
     749   [ #  #  #  # ]:          0 :         if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) {
     750                 :          0 :                 dprintk("svc: bad null verf\n");
     751                 :          0 :                 *authp = rpc_autherr_badverf;
     752                 :          0 :                 return SVC_DENIED;
     753                 :            :         }
     754                 :            : 
     755                 :            :         /* Signal that mapping to nobody uid/gid is required */
     756                 :          0 :         cred->cr_uid = INVALID_UID;
     757                 :          0 :         cred->cr_gid = INVALID_GID;
     758                 :          0 :         cred->cr_group_info = groups_alloc(0);
     759         [ #  # ]:          0 :         if (cred->cr_group_info == NULL)
     760                 :            :                 return SVC_CLOSE; /* kmalloc failure - client must retry */
     761                 :            : 
     762                 :            :         /* Put NULL verifier */
     763                 :          0 :         svc_putnl(resv, RPC_AUTH_NULL);
     764                 :          0 :         svc_putnl(resv, 0);
     765                 :            : 
     766                 :          0 :         rqstp->rq_cred.cr_flavor = RPC_AUTH_NULL;
     767                 :          0 :         return SVC_OK;
     768                 :            : }
     769                 :            : 
     770                 :            : static int
     771                 :          0 : svcauth_null_release(struct svc_rqst *rqstp)
     772                 :            : {
     773         [ #  # ]:          0 :         if (rqstp->rq_client)
     774                 :          0 :                 auth_domain_put(rqstp->rq_client);
     775                 :          0 :         rqstp->rq_client = NULL;
     776         [ #  # ]:          0 :         if (rqstp->rq_cred.cr_group_info)
     777         [ #  # ]:          0 :                 put_group_info(rqstp->rq_cred.cr_group_info);
     778                 :          0 :         rqstp->rq_cred.cr_group_info = NULL;
     779                 :            : 
     780                 :          0 :         return 0; /* don't drop */
     781                 :            : }
     782                 :            : 
     783                 :            : 
     784                 :            : struct auth_ops svcauth_null = {
     785                 :            :         .name           = "null",
     786                 :            :         .owner          = THIS_MODULE,
     787                 :            :         .flavour        = RPC_AUTH_NULL,
     788                 :            :         .accept         = svcauth_null_accept,
     789                 :            :         .release        = svcauth_null_release,
     790                 :            :         .set_client     = svcauth_unix_set_client,
     791                 :            : };
     792                 :            : 
     793                 :            : 
     794                 :            : static int
     795                 :          0 : svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp)
     796                 :            : {
     797                 :          0 :         struct kvec     *argv = &rqstp->rq_arg.head[0];
     798                 :          0 :         struct kvec     *resv = &rqstp->rq_res.head[0];
     799                 :          0 :         struct svc_cred *cred = &rqstp->rq_cred;
     800                 :          0 :         struct user_namespace *userns;
     801                 :          0 :         u32             slen, i;
     802                 :          0 :         int             len   = argv->iov_len;
     803                 :            : 
     804         [ #  # ]:          0 :         if ((len -= 3*4) < 0)
     805                 :            :                 return SVC_GARBAGE;
     806                 :            : 
     807         [ #  # ]:          0 :         svc_getu32(argv);                       /* length */
     808                 :          0 :         svc_getu32(argv);                       /* time stamp */
     809         [ #  # ]:          0 :         slen = XDR_QUADLEN(svc_getnl(argv));    /* machname length */
     810   [ #  #  #  # ]:          0 :         if (slen > 64 || (len -= (slen + 3)*4) < 0)
     811                 :          0 :                 goto badcred;
     812                 :          0 :         argv->iov_base = (void*)((__be32*)argv->iov_base + slen); /* skip machname */
     813                 :          0 :         argv->iov_len -= slen*4;
     814                 :            :         /*
     815                 :            :          * Note: we skip uid_valid()/gid_valid() checks here for
     816                 :            :          * backwards compatibility with clients that use -1 id's.
     817                 :            :          * Instead, -1 uid or gid is later mapped to the
     818                 :            :          * (export-specific) anonymous id by nfsd_setuser.
     819                 :            :          * Supplementary gid's will be left alone.
     820                 :            :          */
     821                 :          0 :         userns = (rqstp->rq_xprt && rqstp->rq_xprt->xpt_cred) ?
     822                 :            :                 rqstp->rq_xprt->xpt_cred->user_ns : &init_user_ns;
     823         [ #  # ]:          0 :         cred->cr_uid = make_kuid(userns, svc_getnl(argv)); /* uid */
     824         [ #  # ]:          0 :         cred->cr_gid = make_kgid(userns, svc_getnl(argv)); /* gid */
     825                 :          0 :         slen = svc_getnl(argv);                 /* gids length */
     826   [ #  #  #  # ]:          0 :         if (slen > UNX_NGROUPS || (len -= (slen + 2)*4) < 0)
     827                 :          0 :                 goto badcred;
     828                 :          0 :         cred->cr_group_info = groups_alloc(slen);
     829         [ #  # ]:          0 :         if (cred->cr_group_info == NULL)
     830                 :            :                 return SVC_CLOSE;
     831         [ #  # ]:          0 :         for (i = 0; i < slen; i++) {
     832                 :          0 :                 kgid_t kgid = make_kgid(userns, svc_getnl(argv));
     833                 :          0 :                 cred->cr_group_info->gid[i] = kgid;
     834                 :            :         }
     835                 :          0 :         groups_sort(cred->cr_group_info);
     836   [ #  #  #  # ]:          0 :         if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) {
     837                 :          0 :                 *authp = rpc_autherr_badverf;
     838                 :          0 :                 return SVC_DENIED;
     839                 :            :         }
     840                 :            : 
     841                 :            :         /* Put NULL verifier */
     842                 :          0 :         svc_putnl(resv, RPC_AUTH_NULL);
     843                 :          0 :         svc_putnl(resv, 0);
     844                 :            : 
     845                 :          0 :         rqstp->rq_cred.cr_flavor = RPC_AUTH_UNIX;
     846                 :          0 :         return SVC_OK;
     847                 :            : 
     848                 :          0 : badcred:
     849                 :          0 :         *authp = rpc_autherr_badcred;
     850                 :          0 :         return SVC_DENIED;
     851                 :            : }
     852                 :            : 
     853                 :            : static int
     854                 :          0 : svcauth_unix_release(struct svc_rqst *rqstp)
     855                 :            : {
     856                 :            :         /* Verifier (such as it is) is already in place.
     857                 :            :          */
     858         [ #  # ]:          0 :         if (rqstp->rq_client)
     859                 :          0 :                 auth_domain_put(rqstp->rq_client);
     860                 :          0 :         rqstp->rq_client = NULL;
     861         [ #  # ]:          0 :         if (rqstp->rq_cred.cr_group_info)
     862         [ #  # ]:          0 :                 put_group_info(rqstp->rq_cred.cr_group_info);
     863                 :          0 :         rqstp->rq_cred.cr_group_info = NULL;
     864                 :            : 
     865                 :          0 :         return 0;
     866                 :            : }
     867                 :            : 
     868                 :            : 
     869                 :            : struct auth_ops svcauth_unix = {
     870                 :            :         .name           = "unix",
     871                 :            :         .owner          = THIS_MODULE,
     872                 :            :         .flavour        = RPC_AUTH_UNIX,
     873                 :            :         .accept         = svcauth_unix_accept,
     874                 :            :         .release        = svcauth_unix_release,
     875                 :            :         .domain_release = svcauth_unix_domain_release,
     876                 :            :         .set_client     = svcauth_unix_set_client,
     877                 :            : };
     878                 :            : 
     879                 :            : static const struct cache_detail ip_map_cache_template = {
     880                 :            :         .owner          = THIS_MODULE,
     881                 :            :         .hash_size      = IP_HASHMAX,
     882                 :            :         .name           = "auth.unix.ip",
     883                 :            :         .cache_put      = ip_map_put,
     884                 :            :         .cache_request  = ip_map_request,
     885                 :            :         .cache_parse    = ip_map_parse,
     886                 :            :         .cache_show     = ip_map_show,
     887                 :            :         .match          = ip_map_match,
     888                 :            :         .init           = ip_map_init,
     889                 :            :         .update         = update,
     890                 :            :         .alloc          = ip_map_alloc,
     891                 :            : };
     892                 :            : 
     893                 :         21 : int ip_map_cache_create(struct net *net)
     894                 :            : {
     895                 :         21 :         struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
     896                 :         21 :         struct cache_detail *cd;
     897                 :         21 :         int err;
     898                 :            : 
     899                 :         21 :         cd = cache_create_net(&ip_map_cache_template, net);
     900         [ -  + ]:         21 :         if (IS_ERR(cd))
     901                 :          0 :                 return PTR_ERR(cd);
     902                 :         21 :         err = cache_register_net(cd, net);
     903         [ -  + ]:         21 :         if (err) {
     904                 :          0 :                 cache_destroy_net(cd, net);
     905                 :          0 :                 return err;
     906                 :            :         }
     907                 :         21 :         sn->ip_map_cache = cd;
     908                 :         21 :         return 0;
     909                 :            : }
     910                 :            : 
     911                 :          0 : void ip_map_cache_destroy(struct net *net)
     912                 :            : {
     913                 :          0 :         struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
     914                 :          0 :         struct cache_detail *cd = sn->ip_map_cache;
     915                 :            : 
     916                 :          0 :         sn->ip_map_cache = NULL;
     917                 :          0 :         cache_purge(cd);
     918                 :          0 :         cache_unregister_net(cd, net);
     919                 :          0 :         cache_destroy_net(cd, net);
     920                 :          0 : }

Generated by: LCOV version 1.14