Whamcloud - gitweb
LU-5577 obdclass: change loop indexes to unsigned
[fs/lustre-release.git] / libcfs / libcfs / nidstrings.c
index 9cf58b4..10e8f8d 100644 (file)
@@ -1,6 +1,4 @@
-/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
- * vim:expandtab:shiftwidth=8:tabstop=8:
- *
+/*
  * GPL HEADER START
  *
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  * GPL HEADER END
  */
 /*
- * Copyright  2008 Sun Microsystems, Inc. All rights reserved
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
  */
 /*
  * This file is part of Lustre, http://www.lustre.org/
  * Author: Phil Schwan <phil@clusterfs.com>
  */
 
-#ifndef EXPORT_SYMTAB
-# define EXPORT_SYMTAB
-#endif
-
 #define DEBUG_SUBSYSTEM S_LNET
 
 #include <libcfs/libcfs.h>
-#include <lnet/lnet.h>
+#include <lnet/nidstr.h>
 #ifndef __KERNEL__
 #ifdef HAVE_GETHOSTBYNAME
 # include <netdb.h>
 #endif
 #endif
 
+/* max value for numeric network address */
+#define MAX_NUMERIC_VALUE 0xffffffff
+
+#define IPSTRING_LENGTH 16
+
 /* CAVEAT VENDITOR! Keep the canonical string representation of nets/nids
  * consistent in all conversion functions.  Some code fragments are copied
  * around for the sake of clarity...
@@ -64,9 +65,6 @@
  * between getting its string and using it.
  */
 
-#define LNET_NIDSTR_COUNT  128     /* # of nidstrings */
-#define LNET_NIDSTR_SIZE   32      /* size of each one (see below for usage) */
-
 static char      libcfs_nidstrings[LNET_NIDSTR_COUNT][LNET_NIDSTR_SIZE];
 static int       libcfs_nidstring_idx = 0;
 
@@ -75,14 +73,14 @@ static spinlock_t libcfs_nidstring_lock;
 
 void libcfs_init_nidstrings (void)
 {
-        spin_lock_init(&libcfs_nidstring_lock);
+       spin_lock_init(&libcfs_nidstring_lock);
 }
 
 # define NIDSTR_LOCK(f)   spin_lock_irqsave(&libcfs_nidstring_lock, f)
 # define NIDSTR_UNLOCK(f) spin_unlock_irqrestore(&libcfs_nidstring_lock, f)
 #else
-# define NIDSTR_LOCK(f)   (f=0)                 /* avoid unused var warnings */
-# define NIDSTR_UNLOCK(f) (f=0)
+# define NIDSTR_LOCK(f)   (f=sizeof(f))  /* avoid set-but-unused warnings */
+# define NIDSTR_UNLOCK(f) (f=sizeof(f))
 #endif
 
 static char *
@@ -105,81 +103,179 @@ libcfs_next_nidstring (void)
 static int  libcfs_lo_str2addr(const char *str, int nob, __u32 *addr);
 static void libcfs_ip_addr2str(__u32 addr, char *str);
 static int  libcfs_ip_str2addr(const char *str, int nob, __u32 *addr);
+static bool cfs_ip_is_contiguous(struct list_head *nidlist);
+static void cfs_ip_min_max(struct list_head *nidlist, __u32 *min, __u32 *max);
 static void libcfs_decnum_addr2str(__u32 addr, char *str);
 static void libcfs_hexnum_addr2str(__u32 addr, char *str);
 static int  libcfs_num_str2addr(const char *str, int nob, __u32 *addr);
+static int  libcfs_num_parse(char *str, int len, struct list_head *list);
+static int  libcfs_num_match(__u32 addr, struct list_head *list);
+static int  libcfs_num_addr_range_print(char *buffer, int count,
+                                       struct list_head *list);
+static int  libcfs_ip_addr_range_print(char *buffer, int count,
+                                      struct list_head *list);
+static bool cfs_num_is_contiguous(struct list_head *nidlist);
+static void cfs_num_min_max(struct list_head *nidlist, __u32 *min, __u32 *max);
 
 struct netstrfns {
-        int          nf_type;
-        char        *nf_name;
-        char        *nf_modname;
-        void       (*nf_addr2str)(__u32 addr, char *str);
-        int        (*nf_str2addr)(const char *str, int nob, __u32 *addr);
+       int      nf_type;
+       char    *nf_name;
+       char    *nf_modname;
+       void    (*nf_addr2str)(__u32 addr, char *str);
+       int     (*nf_str2addr)(const char *str, int nob, __u32 *addr);
+       int     (*nf_parse_addrlist)(char *str, int len,
+                                       struct list_head *list);
+       int     (*nf_print_addrlist)(char *buffer, int count,
+                                    struct list_head *list);
+       int     (*nf_match_addr)(__u32 addr, struct list_head *list);
+       bool    (*nf_is_contiguous)(struct list_head *nidlist);
+       void    (*nf_min_max)(struct list_head *nidlist, __u32 *min_nid,
+                               __u32 *max_nid);
 };
 
 static struct netstrfns  libcfs_netstrfns[] = {
-        {/* .nf_type      */  LOLND,
-         /* .nf_name      */  "lo",
-         /* .nf_modname   */  "klolnd",
-         /* .nf_addr2str  */  libcfs_decnum_addr2str,
-         /* .nf_str2addr  */  libcfs_lo_str2addr},
-        {/* .nf_type      */  SOCKLND,
-         /* .nf_name      */  "tcp",
-         /* .nf_modname   */  "ksocklnd",
-         /* .nf_addr2str  */  libcfs_ip_addr2str,
-         /* .nf_str2addr  */  libcfs_ip_str2addr},
-        {/* .nf_type      */  O2IBLND,
-         /* .nf_name      */  "o2ib",
-         /* .nf_modname   */  "ko2iblnd",
-         /* .nf_addr2str  */  libcfs_ip_addr2str,
-         /* .nf_str2addr  */  libcfs_ip_str2addr},
-        {/* .nf_type      */  CIBLND,
-         /* .nf_name      */  "cib",
-         /* .nf_modname   */  "kciblnd",
-         /* .nf_addr2str  */  libcfs_ip_addr2str,
-         /* .nf_str2addr  */  libcfs_ip_str2addr},
-        {/* .nf_type      */  OPENIBLND,
-         /* .nf_name      */  "openib",
-         /* .nf_modname   */  "kopeniblnd",
-         /* .nf_addr2str  */  libcfs_ip_addr2str,
-         /* .nf_str2addr  */  libcfs_ip_str2addr},
-        {/* .nf_type      */  IIBLND,
-         /* .nf_name      */  "iib",
-         /* .nf_modname   */  "kiiblnd",
-         /* .nf_addr2str  */  libcfs_ip_addr2str,
-         /* .nf_str2addr  */  libcfs_ip_str2addr},
-        {/* .nf_type      */  VIBLND,
-         /* .nf_name      */  "vib",
-         /* .nf_modname   */  "kviblnd",
-         /* .nf_addr2str  */  libcfs_ip_addr2str,
-         /* .nf_str2addr  */  libcfs_ip_str2addr},
-        {/* .nf_type      */  RALND,
-         /* .nf_name      */  "ra",
-         /* .nf_modname   */  "kralnd",
-         /* .nf_addr2str  */  libcfs_ip_addr2str,
-         /* .nf_str2addr  */  libcfs_ip_str2addr},
-        {/* .nf_type      */  QSWLND,
-         /* .nf_name      */  "elan",
-         /* .nf_modname   */  "kqswlnd",
-         /* .nf_addr2str  */  libcfs_decnum_addr2str,
-         /* .nf_str2addr  */  libcfs_num_str2addr},
-        {/* .nf_type      */  GMLND,
-         /* .nf_name      */  "gm",
-         /* .nf_modname   */  "kgmlnd",
-         /* .nf_addr2str  */  libcfs_hexnum_addr2str,
-         /* .nf_str2addr  */  libcfs_num_str2addr},
-        {/* .nf_type      */  MXLND,
-         /* .nf_name      */  "mx",
-         /* .nf_modname   */  "kmxlnd",
-         /* .nf_addr2str  */  libcfs_ip_addr2str,
-         /* .nf_str2addr  */  libcfs_ip_str2addr},
-        {/* .nf_type      */  PTLLND,
-         /* .nf_name      */  "ptl",
-         /* .nf_modname   */  "kptllnd",
-         /* .nf_addr2str  */  libcfs_decnum_addr2str,
-         /* .nf_str2addr  */  libcfs_num_str2addr},
-        /* placeholder for net0 alias.  It MUST BE THE LAST ENTRY */
-        {/* .nf_type      */  -1},
+       {/* .nf_type      */  LOLND,
+        /* .nf_name      */  "lo",
+        /* .nf_modname   */  "klolnd",
+        /* .nf_addr2str  */  libcfs_decnum_addr2str,
+        /* .nf_str2addr  */  libcfs_lo_str2addr,
+        /* .nf_parse_addr*/  libcfs_num_parse,
+        /* .nf_print_addrlist*/  libcfs_num_addr_range_print,
+        /* .nf_match_addr*/      libcfs_num_match,
+        /* .nf_is_contiguous */  cfs_num_is_contiguous,
+        /* .nf_min_max   */      cfs_num_min_max},
+       {/* .nf_type      */  SOCKLND,
+        /* .nf_name      */  "tcp",
+        /* .nf_modname   */  "ksocklnd",
+        /* .nf_addr2str  */  libcfs_ip_addr2str,
+        /* .nf_str2addr  */  libcfs_ip_str2addr,
+        /* .nf_parse_addrlist*/  cfs_ip_addr_parse,
+        /* .nf_print_addrlist*/  libcfs_ip_addr_range_print,
+        /* .nf_match_addr*/      cfs_ip_addr_match,
+        /* .nf_is_contiguous */  cfs_ip_is_contiguous,
+        /* .nf_min_max   */      cfs_ip_min_max},
+       {/* .nf_type      */  O2IBLND,
+        /* .nf_name      */  "o2ib",
+        /* .nf_modname   */  "ko2iblnd",
+        /* .nf_addr2str  */  libcfs_ip_addr2str,
+        /* .nf_str2addr  */  libcfs_ip_str2addr,
+        /* .nf_parse_addrlist*/  cfs_ip_addr_parse,
+        /* .nf_print_addrlist*/  libcfs_ip_addr_range_print,
+        /* .nf_match_addr*/      cfs_ip_addr_match,
+        /* .nf_is_contiguous */  cfs_ip_is_contiguous,
+        /* .nf_min_max   */      cfs_ip_min_max},
+       {/* .nf_type      */  CIBLND,
+        /* .nf_name      */  "cib",
+        /* .nf_modname   */  "kciblnd",
+        /* .nf_addr2str  */  libcfs_ip_addr2str,
+        /* .nf_str2addr  */  libcfs_ip_str2addr,
+        /* .nf_parse_addrlist*/  cfs_ip_addr_parse,
+        /* .nf_print_addrlist*/  libcfs_ip_addr_range_print,
+        /* .nf_match_addr*/      cfs_ip_addr_match,
+        /* .nf_is_contiguous */  cfs_ip_is_contiguous,
+        /* .nf_min_max   */      cfs_ip_min_max},
+       {/* .nf_type      */  OPENIBLND,
+        /* .nf_name      */  "openib",
+        /* .nf_modname   */  "kopeniblnd",
+        /* .nf_addr2str  */  libcfs_ip_addr2str,
+        /* .nf_str2addr  */  libcfs_ip_str2addr,
+        /* .nf_parse_addrlist*/  cfs_ip_addr_parse,
+        /* .nf_print_addrlist*/  libcfs_ip_addr_range_print,
+        /* .nf_match_addr*/      cfs_ip_addr_match,
+        /* .nf_is_contiguous */  cfs_ip_is_contiguous,
+        /* .nf_min_max   */      cfs_ip_min_max},
+       {/* .nf_type      */  IIBLND,
+        /* .nf_name      */  "iib",
+        /* .nf_modname   */  "kiiblnd",
+        /* .nf_addr2str  */  libcfs_ip_addr2str,
+        /* .nf_str2addr  */  libcfs_ip_str2addr,
+        /* .nf_parse_addrlist*/  cfs_ip_addr_parse,
+        /* .nf_print_addrlist*/  libcfs_ip_addr_range_print,
+        /* .nf_match_addr*/      cfs_ip_addr_match,
+        /* .nf_is_contiguous */  cfs_ip_is_contiguous,
+        /* .nf_min_max   */      cfs_ip_min_max},
+       {/* .nf_type      */  VIBLND,
+        /* .nf_name      */  "vib",
+        /* .nf_modname   */  "kviblnd",
+        /* .nf_addr2str  */  libcfs_ip_addr2str,
+        /* .nf_str2addr  */  libcfs_ip_str2addr,
+        /* .nf_parse_addrlist*/  cfs_ip_addr_parse,
+        /* .nf_print_addrlist*/  libcfs_ip_addr_range_print,
+        /* .nf_match_addr*/      cfs_ip_addr_match,
+        /* .nf_is_contiguous */  cfs_ip_is_contiguous,
+        /* .nf_min_max   */      cfs_ip_min_max},
+       {/* .nf_type      */  RALND,
+        /* .nf_name      */  "ra",
+        /* .nf_modname   */  "kralnd",
+        /* .nf_addr2str  */  libcfs_ip_addr2str,
+        /* .nf_str2addr  */  libcfs_ip_str2addr,
+        /* .nf_parse_addrlist*/  cfs_ip_addr_parse,
+        /* .nf_print_addrlist*/  libcfs_ip_addr_range_print,
+        /* .nf_match_addr*/      cfs_ip_addr_match,
+        /* .nf_is_contiguous */  cfs_ip_is_contiguous,
+        /* .nf_min_max   */      cfs_ip_min_max},
+       {/* .nf_type      */      QSWLND,
+        /* .nf_name      */      "elan",
+        /* .nf_modname   */      "kqswlnd",
+        /* .nf_addr2str  */      libcfs_decnum_addr2str,
+        /* .nf_str2addr  */      libcfs_num_str2addr,
+        /* .nf_parse_addrlist*/  libcfs_num_parse,
+        /* .nf_print_addrlist*/  libcfs_num_addr_range_print,
+        /* .nf_match_addr*/      libcfs_num_match,
+        /* .nf_is_contiguous */  cfs_num_is_contiguous,
+        /* .nf_min_max   */      cfs_num_min_max},
+       {/* .nf_type      */      GMLND,
+        /* .nf_name      */      "gm",
+        /* .nf_modname   */      "kgmlnd",
+        /* .nf_addr2str  */      libcfs_hexnum_addr2str,
+        /* .nf_str2addr  */      libcfs_num_str2addr,
+        /* .nf_parse_addrlist*/  libcfs_num_parse,
+        /* .nf_print_addrlist*/  libcfs_num_addr_range_print,
+        /* .nf_match_addr*/      libcfs_num_match,
+        /* .nf_is_contiguous */  cfs_num_is_contiguous,
+        /* .nf_min_max   */      cfs_num_min_max},
+       {/* .nf_type      */      MXLND,
+        /* .nf_name      */      "mx",
+        /* .nf_modname   */      "kmxlnd",
+        /* .nf_addr2str  */      libcfs_ip_addr2str,
+        /* .nf_str2addr  */      libcfs_ip_str2addr,
+        /* .nf_parse_addrlist*/  cfs_ip_addr_parse,
+        /* .nf_print_addrlist*/  libcfs_ip_addr_range_print,
+        /* .nf_match_addr*/      cfs_ip_addr_match,
+        /* .nf_is_contiguous */  cfs_ip_is_contiguous,
+        /* .nf_min_max   */      cfs_ip_min_max},
+       {/* .nf_type      */      PTLLND,
+        /* .nf_name      */      "ptl",
+        /* .nf_modname   */      "kptllnd",
+        /* .nf_addr2str  */      libcfs_decnum_addr2str,
+        /* .nf_str2addr  */      libcfs_num_str2addr,
+        /* .nf_parse_addrlist*/  libcfs_num_parse,
+        /* .nf_print_addrlist*/  libcfs_num_addr_range_print,
+        /* .nf_match_addr*/      libcfs_num_match,
+        /* .nf_is_contiguous */  cfs_num_is_contiguous,
+        /* .nf_min_max   */      cfs_num_min_max},
+       {/* .nf_type      */      GNILND,
+        /* .nf_name      */      "gni",
+        /* .nf_modname   */      "kgnilnd",
+        /* .nf_addr2str  */      libcfs_decnum_addr2str,
+        /* .nf_str2addr  */      libcfs_num_str2addr,
+        /* .nf_parse_addrlist*/  libcfs_num_parse,
+        /* .nf_print_addrlist*/  libcfs_num_addr_range_print,
+        /* .nf_match_addr*/      libcfs_num_match,
+        /* .nf_is_contiguous */  cfs_num_is_contiguous,
+        /* .nf_min_max   */      cfs_num_min_max},
+       {/* .nf_type      */      GNIIPLND,
+        /* .nf_name      */      "gip",
+        /* .nf_modname   */      "kgnilnd",
+        /* .nf_addr2str  */      libcfs_ip_addr2str,
+        /* .nf_str2addr  */      libcfs_ip_str2addr,
+        /* .nf_parse_addrlist*/  cfs_ip_addr_parse,
+        /* .nf_print_addrlist*/  libcfs_ip_addr_range_print,
+        /* .nf_match_addr*/      cfs_ip_addr_match,
+        /* .nf_is_contiguous */  cfs_ip_is_contiguous,
+        /* .nf_min_max   */      cfs_ip_min_max},
+        /* placeholder for net0 alias.  It MUST BE THE LAST ENTRY */
+       {/* .nf_type      */  -1},
 };
 
 const int libcfs_nnetstrfns = sizeof(libcfs_netstrfns)/sizeof(libcfs_netstrfns[0]);
@@ -220,11 +316,11 @@ libcfs_ip_addr2str(__u32 addr, char *str)
 int
 libcfs_ip_str2addr(const char *str, int nob, __u32 *addr)
 {
-        int   a;
-        int   b;
-        int   c;
-        int   d;
-        int   n = nob;                          /* XscanfX */
+       unsigned int    a;
+       unsigned int    b;
+       unsigned int    c;
+       unsigned int    d;
+       int             n = nob; /* XscanfX */
 
         /* numeric IP? */
         if (sscanf(str, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n) >= 4 &&
@@ -310,6 +406,21 @@ libcfs_lnd2netstrfns(int lnd)
 }
 
 struct netstrfns *
+libcfs_namenum2netstrfns(const char *name)
+{
+        struct netstrfns *nf;
+        int               i;
+
+        for (i = 0; i < libcfs_nnetstrfns; i++) {
+                nf = &libcfs_netstrfns[i];
+                if (nf->nf_type >= 0 &&
+                    !strncmp(name, nf->nf_name, strlen(nf->nf_name)))
+                        return nf;
+        }
+        return NULL;
+}
+
+struct netstrfns *
 libcfs_name2netstrfns(const char *name)
 {
         int    i;
@@ -391,7 +502,7 @@ libcfs_nid2str(lnet_nid_t nid)
         int               nob;
 
         if (nid == LNET_NID_ANY)
-                return "LNET_NID_ANY";
+                return "<?>";
 
         nf = libcfs_lnd2netstrfns(lnd);
         str = libcfs_next_nidstring();
@@ -415,10 +526,10 @@ libcfs_nid2str(lnet_nid_t nid)
 static struct netstrfns *
 libcfs_str2net_internal(const char *str, __u32 *net)
 {
-        struct netstrfns *nf;
-        int               nob;
-        int               netnum;
-        int               i;
+       struct netstrfns *nf = NULL;
+       int               nob;
+       unsigned int      netnum;
+       int               i;
 
         for (i = 0; i < libcfs_nnetstrfns; i++) {
                 nf = &libcfs_netstrfns[i];
@@ -479,7 +590,7 @@ libcfs_str2nid(const char *str)
                 LASSERT (nf != NULL);
         }
 
-        if (!nf->nf_str2addr(str, sep - str, &addr))
+        if (!nf->nf_str2addr(str, (int)(sep - str), &addr))
                 return LNET_NID_ANY;
 
         return LNET_MKNID(net, addr);
@@ -514,6 +625,757 @@ libcfs_str2anynid(lnet_nid_t *nidp, const char *str)
         return *nidp != LNET_NID_ANY;
 }
 
+/**
+ * Nid range list syntax.
+ * \verbatim
+ *
+ * <nidlist>         :== <nidrange> [ ' ' <nidrange> ]
+ * <nidrange>        :== <addrrange> '@' <net>
+ * <addrrange>       :== '*' |
+ *                       <ipaddr_range> |
+ *                      <cfs_expr_list>
+ * <ipaddr_range>    :== <cfs_expr_list>.<cfs_expr_list>.<cfs_expr_list>.
+ *                      <cfs_expr_list>
+ * <cfs_expr_list>   :== <number> |
+ *                       <expr_list>
+ * <expr_list>       :== '[' <range_expr> [ ',' <range_expr>] ']'
+ * <range_expr>      :== <number> |
+ *                       <number> '-' <number> |
+ *                       <number> '-' <number> '/' <number>
+ * <net>             :== <netname> | <netname><number>
+ * <netname>         :== "lo" | "tcp" | "o2ib" | "cib" | "openib" | "iib" |
+ *                       "vib" | "ra" | "elan" | "mx" | "ptl"
+ * \endverbatim
+ */
+
+/**
+ * Structure to represent \<nidrange\> token of the syntax.
+ *
+ * One of this is created for each \<net\> parsed.
+ */
+struct nidrange {
+       /**
+        * Link to list of this structures which is built on nid range
+        * list parsing.
+        */
+       struct list_head nr_link;
+       /**
+        * List head for addrrange::ar_link.
+        */
+       struct list_head nr_addrranges;
+       /**
+        * Flag indicating that *@<net> is found.
+        */
+       int nr_all;
+       /**
+        * Pointer to corresponding element of libcfs_netstrfns.
+        */
+       struct netstrfns *nr_netstrfns;
+       /**
+        * Number of network. E.g. 5 if \<net\> is "elan5".
+        */
+       int nr_netnum;
+};
+
+/**
+ * Structure to represent \<addrrange\> token of the syntax.
+ */
+struct addrrange {
+       /**
+        * Link to nidrange::nr_addrranges.
+        */
+       struct list_head ar_link;
+       /**
+        * List head for cfs_expr_list::el_list.
+        */
+       struct list_head ar_numaddr_ranges;
+};
+
+/**
+ * Nf_parse_addrlist method for networks using numeric addresses.
+ *
+ * Examples of such networks are gm and elan.
+ *
+ * \retval 0 if \a str parsed to numeric address
+ * \retval errno otherwise
+ */
+static int
+libcfs_num_parse(char *str, int len, struct list_head *list)
+{
+       struct cfs_expr_list *el;
+       int     rc;
+
+       rc = cfs_expr_list_parse(str, len, 0, MAX_NUMERIC_VALUE, &el);
+       if (rc == 0)
+               list_add_tail(&el->el_link, list);
+
+       return rc;
+}
+
+/**
+ * Parses \<addrrange\> token on the syntax.
+ *
+ * Allocates struct addrrange and links to \a nidrange via
+ * (nidrange::nr_addrranges)
+ *
+ * \retval 0 if \a src parses to '*' | \<ipaddr_range\> | \<cfs_expr_list\>
+ * \retval -errno otherwise
+ */
+static int
+parse_addrange(const struct cfs_lstr *src, struct nidrange *nidrange)
+{
+       struct addrrange *addrrange;
+
+       if (src->ls_len == 1 && src->ls_str[0] == '*') {
+               nidrange->nr_all = 1;
+               return 0;
+       }
+
+       LIBCFS_ALLOC(addrrange, sizeof(struct addrrange));
+       if (addrrange == NULL)
+               return -ENOMEM;
+       list_add_tail(&addrrange->ar_link, &nidrange->nr_addrranges);
+       INIT_LIST_HEAD(&addrrange->ar_numaddr_ranges);
+
+       return nidrange->nr_netstrfns->nf_parse_addrlist(src->ls_str,
+                                               src->ls_len,
+                                               &addrrange->ar_numaddr_ranges);
+}
+
+/**
+ * Finds or creates struct nidrange.
+ *
+ * Checks if \a src is a valid network name, looks for corresponding
+ * nidrange on the ist of nidranges (\a nidlist), creates new struct
+ * nidrange if it is not found.
+ *
+ * \retval pointer to struct nidrange matching network specified via \a src
+ * \retval NULL if \a src does not match any network
+ */
+static struct nidrange *
+add_nidrange(const struct cfs_lstr *src,
+            struct list_head *nidlist)
+{
+        struct netstrfns *nf;
+        struct nidrange *nr;
+        int endlen;
+        unsigned netnum;
+
+        if (src->ls_len >= LNET_NIDSTR_SIZE)
+                return NULL;
+
+        nf = libcfs_namenum2netstrfns(src->ls_str);
+        if (nf == NULL)
+                return NULL;
+        endlen = src->ls_len - strlen(nf->nf_name);
+        if (endlen == 0)
+                /* network name only, e.g. "elan" or "tcp" */
+                netnum = 0;
+        else {
+                /* e.g. "elan25" or "tcp23", refuse to parse if
+                 * network name is not appended with decimal or
+                 * hexadecimal number */
+               if (!cfs_str2num_check(src->ls_str + strlen(nf->nf_name),
+                                      endlen, &netnum, 0, MAX_NUMERIC_VALUE))
+                        return NULL;
+        }
+
+       list_for_each_entry(nr, nidlist, nr_link) {
+               if (nr->nr_netstrfns != nf)
+                       continue;
+               if (nr->nr_netnum != netnum)
+                       continue;
+               return nr;
+       }
+
+       LIBCFS_ALLOC(nr, sizeof(struct nidrange));
+       if (nr == NULL)
+               return NULL;
+       list_add_tail(&nr->nr_link, nidlist);
+       INIT_LIST_HEAD(&nr->nr_addrranges);
+       nr->nr_netstrfns = nf;
+       nr->nr_all = 0;
+       nr->nr_netnum = netnum;
+
+       return nr;
+}
+
+/**
+ * Parses \<nidrange\> token of the syntax.
+ *
+ * \retval 1 if \a src parses to \<addrrange\> '@' \<net\>
+ * \retval 0 otherwise
+ */
+static int
+parse_nidrange(struct cfs_lstr *src, struct list_head *nidlist)
+{
+       struct cfs_lstr addrrange;
+       struct cfs_lstr net;
+       struct cfs_lstr tmp;
+        struct nidrange *nr;
+
+        tmp = *src;
+       if (cfs_gettok(src, '@', &addrrange) == 0)
+                goto failed;
+
+       if (cfs_gettok(src, '@', &net) == 0 || src->ls_str != NULL)
+                goto failed;
+
+        nr = add_nidrange(&net, nidlist);
+        if (nr == NULL)
+                goto failed;
+
+       if (parse_addrange(&addrrange, nr) != 0)
+                goto failed;
+
+        return 1;
+ failed:
+        CWARN("can't parse nidrange: \"%.*s\"\n", tmp.ls_len, tmp.ls_str);
+        return 0;
+}
+
+/**
+ * Frees addrrange structures of \a list.
+ *
+ * For each struct addrrange structure found on \a list it frees
+ * cfs_expr_list list attached to it and frees the addrrange itself.
+ *
+ * \retval none
+ */
+static void
+free_addrranges(struct list_head *list)
+{
+       while (!list_empty(list)) {
+               struct addrrange *ar;
+
+               ar = list_entry(list->next, struct addrrange, ar_link);
+
+               cfs_expr_list_free_list(&ar->ar_numaddr_ranges);
+               list_del(&ar->ar_link);
+               LIBCFS_FREE(ar, sizeof(struct addrrange));
+       }
+}
+
+/**
+ * Frees nidrange strutures of \a list.
+ *
+ * For each struct nidrange structure found on \a list it frees
+ * addrrange list attached to it and frees the nidrange itself.
+ *
+ * \retval none
+ */
+void
+cfs_free_nidlist(struct list_head *list)
+{
+       struct list_head *pos, *next;
+       struct nidrange *nr;
+
+       list_for_each_safe(pos, next, list) {
+               nr = list_entry(pos, struct nidrange, nr_link);
+               free_addrranges(&nr->nr_addrranges);
+               list_del(pos);
+               LIBCFS_FREE(nr, sizeof(struct nidrange));
+       }
+}
+
+/**
+ * Parses nid range list.
+ *
+ * Parses with rigorous syntax and overflow checking \a str into
+ * \<nidrange\> [ ' ' \<nidrange\> ], compiles \a str into set of
+ * structures and links that structure to \a nidlist. The resulting
+ * list can be used to match a NID againts set of NIDS defined by \a
+ * str.
+ * \see cfs_match_nid
+ *
+ * \retval 1 on success
+ * \retval 0 otherwise
+ */
+int
+cfs_parse_nidlist(char *str, int len, struct list_head *nidlist)
+{
+       struct cfs_lstr src;
+       struct cfs_lstr res;
+       int rc;
+
+       src.ls_str = str;
+       src.ls_len = len;
+       INIT_LIST_HEAD(nidlist);
+       while (src.ls_str) {
+               rc = cfs_gettok(&src, ' ', &res);
+               if (rc == 0) {
+                       cfs_free_nidlist(nidlist);
+                       return 0;
+               }
+               rc = parse_nidrange(&res, nidlist);
+               if (rc == 0) {
+                       cfs_free_nidlist(nidlist);
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+/*
+ * Nf_match_addr method for networks using numeric addresses
+ *
+ * \retval 1 on match
+ * \retval 0 otherwise
+ */
+static int
+libcfs_num_match(__u32 addr, struct list_head *numaddr)
+{
+       struct cfs_expr_list *el;
+
+       LASSERT(!list_empty(numaddr));
+       el = list_entry(numaddr->next, struct cfs_expr_list, el_link);
+
+       return cfs_expr_list_match(addr, el);
+}
+
+/**
+ * Matches a nid (\a nid) against the compiled list of nidranges (\a nidlist).
+ *
+ * \see cfs_parse_nidlist()
+ *
+ * \retval 1 on match
+ * \retval 0  otherwises
+ */
+int cfs_match_nid(lnet_nid_t nid, struct list_head *nidlist)
+{
+       struct nidrange *nr;
+       struct addrrange *ar;
+
+       list_for_each_entry(nr, nidlist, nr_link) {
+               if (nr->nr_netstrfns->nf_type != LNET_NETTYP(LNET_NIDNET(nid)))
+                       continue;
+               if (nr->nr_netnum != LNET_NETNUM(LNET_NIDNET(nid)))
+                       continue;
+               if (nr->nr_all)
+                       return 1;
+               list_for_each_entry(ar, &nr->nr_addrranges, ar_link)
+                       if (nr->nr_netstrfns->nf_match_addr(LNET_NIDADDR(nid),
+                                                       &ar->ar_numaddr_ranges))
+                               return 1;
+       }
+       return 0;
+}
+
+static int
+libcfs_num_addr_range_print(char *buffer, int count, struct list_head *list)
+{
+       int i = 0, j = 0;
+       struct cfs_expr_list *el;
+
+       list_for_each_entry(el, list, el_link) {
+               LASSERT(j++ < 1);
+               i += cfs_expr_list_print(buffer + i, count - i, el);
+       }
+       return i;
+}
+
+static int
+libcfs_ip_addr_range_print(char *buffer, int count, struct list_head *list)
+{
+       int i = 0, j = 0;
+       struct cfs_expr_list *el;
+
+       list_for_each_entry(el, list, el_link) {
+               LASSERT(j++ < 4);
+               if (i != 0)
+                       i += cfs_snprintf(buffer + i, count - i, ".");
+               i += cfs_expr_list_print(buffer + i, count - i, el);
+       }
+       return i;
+}
+
+
+/**
+ * Print the network part of the nidrange \a nr into the specified \a buffer.
+ *
+ * \retval number of characters written
+ */
+static int
+cfs_print_network(char *buffer, int count, struct nidrange *nr)
+{
+       struct netstrfns *nf = nr->nr_netstrfns;
+
+       if (nr->nr_netnum == 0)
+               return cfs_snprintf(buffer, count, "@%s", nf->nf_name);
+       else
+               return cfs_snprintf(buffer, count, "@%s%u",
+                                   nf->nf_name, nr->nr_netnum);
+}
+
+
+/**
+ * Print a list of addrrange (\a addrranges) into the specified \a buffer.
+ * At max \a count characters can be printed into \a buffer.
+ *
+ * \retval number of characters written
+ */
+static int
+cfs_print_addrranges(char *buffer, int count, struct list_head *addrranges,
+                    struct nidrange *nr)
+{
+       int i = 0;
+       struct addrrange *ar;
+       struct netstrfns *nf = nr->nr_netstrfns;
+
+       list_for_each_entry(ar, addrranges, ar_link) {
+               if (i != 0)
+                       i += cfs_snprintf(buffer + i, count - i, " ");
+               i += nf->nf_print_addrlist(buffer + i, count - i,
+                                          &ar->ar_numaddr_ranges);
+               i += cfs_print_network(buffer + i, count - i, nr);
+       }
+       return i;
+}
+
+
+/**
+ * Print a list of nidranges (\a nidlist) into the specified \a buffer.
+ * At max \a count characters can be printed into \a buffer.
+ * Nidranges are separated by a space character.
+ *
+ * \retval number of characters written
+ */
+int cfs_print_nidlist(char *buffer, int count, struct list_head *nidlist)
+{
+       int i = 0;
+       struct nidrange *nr;
+
+       if (count <= 0)
+               return 0;
+
+       list_for_each_entry(nr, nidlist, nr_link) {
+               if (i != 0)
+                       i += cfs_snprintf(buffer + i, count - i, " ");
+
+               if (nr->nr_all != 0) {
+                       LASSERT(list_empty(&nr->nr_addrranges));
+                       i += cfs_snprintf(buffer + i, count - i, "*");
+                       i += cfs_print_network(buffer + i, count - i, nr);
+               } else {
+                       i += cfs_print_addrranges(buffer + i, count - i,
+                                                 &nr->nr_addrranges, nr);
+               }
+       }
+       return i;
+}
+
+/**
+ * Determines minimum and maximum addresses for a single
+ * numeric address range
+ *
+ * \param      ar
+ * \param      min_nid
+ * \param      max_nid
+ */
+static void cfs_ip_ar_min_max(struct addrrange *ar, __u32 *min_nid,
+                             __u32 *max_nid)
+{
+       struct cfs_expr_list    *el;
+       struct cfs_range_expr   *re;
+       __u32                   tmp_ip_addr = 0;
+       unsigned int            min_ip[4] = {0};
+       unsigned int            max_ip[4] = {0};
+       int                     re_count = 0;
+
+       list_for_each_entry(el, &ar->ar_numaddr_ranges, el_link) {
+               list_for_each_entry(re, &el->el_exprs, re_link) {
+                       min_ip[re_count] = re->re_lo;
+                       max_ip[re_count] = re->re_hi;
+                       re_count++;
+               }
+       }
+
+       tmp_ip_addr = ((min_ip[0] << 24) | (min_ip[1] << 16) |
+                      (min_ip[2] << 8) | min_ip[3]);
+
+       if (min_nid != NULL)
+               *min_nid = tmp_ip_addr;
+
+       tmp_ip_addr = ((max_ip[0] << 24) | (max_ip[1] << 16) |
+                      (max_ip[2] << 8) | max_ip[3]);
+
+       if (max_nid != NULL)
+               *max_nid = tmp_ip_addr;
+}
+
+/**
+ * Determines minimum and maximum addresses for a single
+ * numeric address range
+ *
+ * \param      ar
+ * \param      min_nid
+ * \param      max_nid
+ */
+static void cfs_num_ar_min_max(struct addrrange *ar, __u32 *min_nid,
+                              __u32 *max_nid)
+{
+       struct cfs_expr_list    *el;
+       struct cfs_range_expr   *re;
+       unsigned int            min_addr = 0;
+       unsigned int            max_addr = 0;
+
+       list_for_each_entry(el, &ar->ar_numaddr_ranges, el_link) {
+               list_for_each_entry(re, &el->el_exprs, re_link) {
+                       if (re->re_lo < min_addr || min_addr == 0)
+                               min_addr = re->re_lo;
+                       if (re->re_hi > max_addr)
+                               max_addr = re->re_hi;
+               }
+       }
+
+       if (min_nid != NULL)
+               *min_nid = min_addr;
+       if (max_nid != NULL)
+               *max_nid = max_addr;
+}
+
+/**
+ * Determines whether an expression list in an nidrange contains exactly
+ * one contiguous address range. Calls the correct netstrfns for the LND
+ *
+ * \param      *nidlist
+ *
+ * \retval     true if contiguous
+ * \retval     false if not contiguous
+ */
+bool cfs_nidrange_is_contiguous(struct list_head *nidlist)
+{
+       struct nidrange         *nr;
+       struct netstrfns        *nf = NULL;
+       char                    *lndname = NULL;
+       int                     netnum = -1;
+
+       list_for_each_entry(nr, nidlist, nr_link) {
+               nf = nr->nr_netstrfns;
+               if (lndname == NULL)
+                       lndname = nf->nf_name;
+               if (netnum == -1)
+                       netnum = nr->nr_netnum;
+
+               if (strcmp(lndname, nf->nf_name) != 0 ||
+                   netnum != nr->nr_netnum)
+                       return false;
+       }
+
+       if (nf == NULL)
+               return false;
+
+       if (!nf->nf_is_contiguous(nidlist))
+               return false;
+
+       return true;
+}
+EXPORT_SYMBOL(cfs_nidrange_is_contiguous);
+
+/**
+ * Determines whether an expression list in an num nidrange contains exactly
+ * one contiguous address range.
+ *
+ * \param      *nidlist
+ *
+ * \retval     true if contiguous
+ * \retval     false if not contiguous
+ */
+static bool cfs_num_is_contiguous(struct list_head *nidlist)
+{
+       struct nidrange         *nr;
+       struct addrrange        *ar;
+       struct cfs_expr_list    *el;
+       struct cfs_range_expr   *re;
+       int                     last_hi = 0;
+       __u32                   last_end_nid = 0;
+       __u32                   current_start_nid = 0;
+       __u32                   current_end_nid = 0;
+
+       list_for_each_entry(nr, nidlist, nr_link) {
+               list_for_each_entry(ar, &nr->nr_addrranges, ar_link) {
+                       cfs_num_ar_min_max(ar, &current_start_nid,
+                                          &current_end_nid);
+                       if (last_end_nid != 0 &&
+                           (current_start_nid - last_end_nid != 1))
+                                       return false;
+                       last_end_nid = current_end_nid;
+                       list_for_each_entry(el, &ar->ar_numaddr_ranges,
+                                           el_link) {
+                               list_for_each_entry(re, &el->el_exprs,
+                                                   re_link) {
+                                       if (re->re_stride > 1)
+                                               return false;
+                                       else if (last_hi != 0 &&
+                                                re->re_hi - last_hi != 1)
+                                               return false;
+                                       last_hi = re->re_hi;
+                               }
+                       }
+               }
+       }
+
+       return true;
+}
+
+/**
+ * Determines whether an expression list in an ip nidrange contains exactly
+ * one contiguous address range.
+ *
+ * \param      *nidlist
+ *
+ * \retval     true if contiguous
+ * \retval     false if not contiguous
+ */
+static bool cfs_ip_is_contiguous(struct list_head *nidlist)
+{
+       struct nidrange         *nr;
+       struct addrrange        *ar;
+       struct cfs_expr_list    *el;
+       struct cfs_range_expr   *re;
+       int                     expr_count;
+       int                     last_hi = 255;
+       int                     last_diff = 0;
+       __u32                   last_end_nid = 0;
+       __u32                   current_start_nid = 0;
+       __u32                   current_end_nid = 0;
+
+       list_for_each_entry(nr, nidlist, nr_link) {
+               list_for_each_entry(ar, &nr->nr_addrranges, ar_link) {
+                       last_hi = 255;
+                       last_diff = 0;
+                       cfs_ip_ar_min_max(ar, &current_start_nid,
+                                         &current_end_nid);
+                       if (last_end_nid != 0 &&
+                           (current_start_nid - last_end_nid != 1))
+                                       return false;
+                       last_end_nid = current_end_nid;
+                       list_for_each_entry(el,
+                                           &ar->ar_numaddr_ranges,
+                                           el_link) {
+                               expr_count = 0;
+                               list_for_each_entry(re, &el->el_exprs,
+                                                   re_link) {
+                                       expr_count++;
+                                       if (re->re_stride > 1 ||
+                                           (last_diff > 0 && last_hi != 255) ||
+                                           (last_diff > 0 && last_hi == 255 &&
+                                            re->re_lo > 0))
+                                               return false;
+                                       last_hi = re->re_hi;
+                                       last_diff = re->re_hi - re->re_lo;
+                               }
+                       }
+               }
+       }
+
+       return true;
+}
+
+/**
+ * Takes a linked list of nidrange expressions, determines the minimum
+ * and maximum nid and creates appropriate nid structures
+ *
+ * \param      *nidlist
+ * \param      *min_nid
+ * \param      *max_nid
+ */
+void cfs_nidrange_find_min_max(struct list_head *nidlist, char *min_nid,
+                              char *max_nid, int nidstr_length)
+{
+       struct nidrange         *nr;
+       struct netstrfns        *nf = NULL;
+       int                     netnum = -1;
+       __u32                   min_addr;
+       __u32                   max_addr;
+       char                    *lndname = NULL;
+       char                    min_addr_str[IPSTRING_LENGTH];
+       char                    max_addr_str[IPSTRING_LENGTH];
+
+       list_for_each_entry(nr, nidlist, nr_link) {
+               nf = nr->nr_netstrfns;
+               lndname = nf->nf_name;
+               if (netnum == -1)
+                       netnum = nr->nr_netnum;
+
+               nf->nf_min_max(nidlist, &min_addr, &max_addr);
+       }
+       nf->nf_addr2str(min_addr, min_addr_str);
+       nf->nf_addr2str(max_addr, max_addr_str);
+
+       snprintf(min_nid, nidstr_length, "%s@%s%d", min_addr_str, lndname,
+                netnum);
+       snprintf(max_nid, nidstr_length, "%s@%s%d", max_addr_str, lndname,
+                netnum);
+}
+EXPORT_SYMBOL(cfs_nidrange_find_min_max);
+
+/**
+ * Determines the min and max NID values for num LNDs
+ *
+ * \param      *nidlist
+ * \param      *min_nid
+ * \param      *max_nid
+ */
+static void cfs_num_min_max(struct list_head *nidlist, __u32 *min_nid,
+                           __u32 *max_nid)
+{
+       struct nidrange         *nr;
+       struct addrrange        *ar;
+       unsigned int            tmp_min_addr = 0;
+       unsigned int            tmp_max_addr = 0;
+       unsigned int            min_addr = 0;
+       unsigned int            max_addr = 0;
+
+       list_for_each_entry(nr, nidlist, nr_link) {
+               list_for_each_entry(ar, &nr->nr_addrranges, ar_link) {
+                       cfs_num_ar_min_max(ar, &tmp_min_addr,
+                                          &tmp_max_addr);
+                       if (tmp_min_addr < min_addr || min_addr == 0)
+                               min_addr = tmp_min_addr;
+                       if (tmp_max_addr > max_addr)
+                               max_addr = tmp_min_addr;
+               }
+       }
+       *max_nid = max_addr;
+       *min_nid = min_addr;
+}
+
+/**
+ * Takes an nidlist and determines the minimum and maximum
+ * ip addresses.
+ *
+ * \param      *nidlist
+ * \param      *min_nid
+ * \param      *max_nid
+ */
+static void cfs_ip_min_max(struct list_head *nidlist, __u32 *min_nid,
+                          __u32 *max_nid)
+{
+       struct nidrange         *nr;
+       struct addrrange        *ar;
+       __u32                   tmp_min_ip_addr = 0;
+       __u32                   tmp_max_ip_addr = 0;
+       __u32                   min_ip_addr = 0;
+       __u32                   max_ip_addr = 0;
+
+       list_for_each_entry(nr, nidlist, nr_link) {
+               list_for_each_entry(ar, &nr->nr_addrranges, ar_link) {
+                       cfs_ip_ar_min_max(ar, &tmp_min_ip_addr,
+                                         &tmp_max_ip_addr);
+                       if (tmp_min_ip_addr < min_ip_addr || min_ip_addr == 0)
+                               min_ip_addr = tmp_min_ip_addr;
+                       if (tmp_max_ip_addr > max_ip_addr)
+                               max_ip_addr = tmp_max_ip_addr;
+               }
+       }
+
+       if (min_nid != NULL)
+               *min_nid = min_ip_addr;
+       if (max_nid != NULL)
+               *max_nid = max_ip_addr;
+}
+
 #ifdef __KERNEL__
 
 EXPORT_SYMBOL(libcfs_isknown_lnd);
@@ -526,5 +1388,9 @@ EXPORT_SYMBOL(libcfs_str2net);
 EXPORT_SYMBOL(libcfs_str2nid);
 EXPORT_SYMBOL(libcfs_id2str);
 EXPORT_SYMBOL(libcfs_str2anynid);
+EXPORT_SYMBOL(cfs_free_nidlist);
+EXPORT_SYMBOL(cfs_parse_nidlist);
+EXPORT_SYMBOL(cfs_print_nidlist);
+EXPORT_SYMBOL(cfs_match_nid);
 
 #endif