[ofa-general] [PATCH 1/3] Create a new library libibnetdisc

Ira Weiny weiny2 at llnl.gov
Thu Nov 20 16:38:12 PST 2008


>From 663b13de4253c4d87c73e8d2f50c9b798fa3a4d8 Mon Sep 17 00:00:00 2001
From: Ira Weiny <weiny2 at llnl.gov>
Date: Fri, 14 Nov 2008 15:36:03 -0800
Subject: [PATCH] Create a new library libibnetdisc

This encompasses the functionality of ibnetdiscover in a C library.  It returns
a single "ibnd_fabric_t" object which represents the data found during the
scan.  The NodeInfo, PortInfo, and SwitchInfo are preserved from the queries
made on the fabric to be used by the calling function as they see fit.

This greatly benefits some diags like iblinkinfo.pl.  This diag in particular
was re-written using this library in C and has shown an 85% speed up on a ~1000
node cluster.

Previous iblinkinfo.pl
   real    3m35.876s
   user    0m13.210s
   sys     1m1.046s

New iblinkinfotest
   real    0m32.869s
   user    0m0.067s
   sys     0m0.140s

Signed-off-by: Ira Weiny <weiny2 at llnl.gov>
---
 libibnetdisc/AUTHORS                        |    1 +
 libibnetdisc/COPYING                        |  384 ++++++++++++
 libibnetdisc/ChangeLog                      |    4 +
 libibnetdisc/Makefile.am                    |   73 +++
 libibnetdisc/autogen.sh                     |   11 +
 libibnetdisc/configure.in                   |   68 +++
 libibnetdisc/include/infiniband/ibnetdisc.h |  306 ++++++++++
 libibnetdisc/libibnetdisc.spec.in           |   94 +++
 libibnetdisc/libibnetdisc.ver               |    9 +
 libibnetdisc/man/ibnd_debug.3               |    2 +
 libibnetdisc/man/ibnd_destroy_fabric.3      |    2 +
 libibnetdisc/man/ibnd_discover_fabric.3     |   43 ++
 libibnetdisc/man/ibnd_find_node_dr.3        |    2 +
 libibnetdisc/man/ibnd_find_node_guid.3      |   25 +
 libibnetdisc/man/ibnd_iter_nodes.3          |   24 +
 libibnetdisc/man/ibnd_iter_nodes_type.3     |    2 +
 libibnetdisc/man/ibnd_linkspeed_str.3       |    2 +
 libibnetdisc/man/ibnd_linkstate_str.3       |    2 +
 libibnetdisc/man/ibnd_linkwidth_str.3       |   26 +
 libibnetdisc/man/ibnd_node_type_str.3       |    2 +
 libibnetdisc/man/ibnd_node_type_str_short.3 |    2 +
 libibnetdisc/man/ibnd_physstate_str.3       |    2 +
 libibnetdisc/man/ibnd_update_node.3         |   21 +
 libibnetdisc/src/chassis.c                  |  820 +++++++++++++++++++++++++
 libibnetdisc/src/chassis.h                  |   82 +++
 libibnetdisc/src/ibnetdisc.c                |  863 +++++++++++++++++++++++++++
 libibnetdisc/src/libibnetdisc.map           |   27 +
 libibnetdisc/test/iblinkinfotest.c          |  395 ++++++++++++
 libibnetdisc/test/ibnetdisctest.c           |  588 ++++++++++++++++++
 libibnetdisc/test/testleaks.c               |  261 ++++++++
 30 files changed, 4143 insertions(+), 0 deletions(-)
 create mode 100644 libibnetdisc/AUTHORS
 create mode 100644 libibnetdisc/COPYING
 create mode 100644 libibnetdisc/ChangeLog
 create mode 100644 libibnetdisc/Makefile.am
 create mode 100755 libibnetdisc/autogen.sh
 create mode 100644 libibnetdisc/configure.in
 create mode 100644 libibnetdisc/include/infiniband/ibnetdisc.h
 create mode 100644 libibnetdisc/libibnetdisc.spec.in
 create mode 100644 libibnetdisc/libibnetdisc.ver
 create mode 100644 libibnetdisc/man/ibnd_debug.3
 create mode 100644 libibnetdisc/man/ibnd_destroy_fabric.3
 create mode 100644 libibnetdisc/man/ibnd_discover_fabric.3
 create mode 100644 libibnetdisc/man/ibnd_find_node_dr.3
 create mode 100644 libibnetdisc/man/ibnd_find_node_guid.3
 create mode 100644 libibnetdisc/man/ibnd_iter_nodes.3
 create mode 100644 libibnetdisc/man/ibnd_iter_nodes_type.3
 create mode 100644 libibnetdisc/man/ibnd_linkspeed_str.3
 create mode 100644 libibnetdisc/man/ibnd_linkstate_str.3
 create mode 100644 libibnetdisc/man/ibnd_linkwidth_str.3
 create mode 100644 libibnetdisc/man/ibnd_node_type_str.3
 create mode 100644 libibnetdisc/man/ibnd_node_type_str_short.3
 create mode 100644 libibnetdisc/man/ibnd_physstate_str.3
 create mode 100644 libibnetdisc/man/ibnd_update_node.3
 create mode 100644 libibnetdisc/src/chassis.c
 create mode 100644 libibnetdisc/src/chassis.h
 create mode 100644 libibnetdisc/src/ibnetdisc.c
 create mode 100644 libibnetdisc/src/libibnetdisc.map
 create mode 100644 libibnetdisc/test/iblinkinfotest.c
 create mode 100644 libibnetdisc/test/ibnetdisctest.c
 create mode 100644 libibnetdisc/test/testleaks.c

diff --git a/libibnetdisc/AUTHORS b/libibnetdisc/AUTHORS
new file mode 100644
index 0000000..d7211f9
--- /dev/null
+++ b/libibnetdisc/AUTHORS
@@ -0,0 +1 @@
+Ira Weiny <weiny2 at llnl.gov>
diff --git a/libibnetdisc/COPYING b/libibnetdisc/COPYING
new file mode 100644
index 0000000..a017728
--- /dev/null
+++ b/libibnetdisc/COPYING
@@ -0,0 +1,384 @@
+This software with the exception of OpenSM is available to you
+under a choice of one of two licenses. You may chose to be
+licensed under the terms of the the OpenIB.org BSD license or
+the GNU General Public License (GPL) Version 2, both included
+below.
+
+OpenSM is licensed under either GNU General Public License (GPL)
+Version 2, or Intel BSD + Patent license. See OpenSM for the
+specific language for the latter licensing terms.
+
+
+Copyright (c) 2004, 2005 Voltaire, Inc.  All rights reserved.
+
+==================================================================
+
+		       OpenIB.org BSD license
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+  * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+  * Redistributions in binary form must reproduce the above
+    copyright notice, this list of conditions and the following
+    disclaimer in the documentation and/or other materials provided
+    with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+==================================================================
+
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/libibnetdisc/ChangeLog b/libibnetdisc/ChangeLog
new file mode 100644
index 0000000..d74037e
--- /dev/null
+++ b/libibnetdisc/ChangeLog
@@ -0,0 +1,4 @@
+
+2008-04-09  Ira Weiny <weiny2 at llnl.gov>
+
+	* Added to git tree
diff --git a/libibnetdisc/Makefile.am b/libibnetdisc/Makefile.am
new file mode 100644
index 0000000..b5c0dd0
--- /dev/null
+++ b/libibnetdisc/Makefile.am
@@ -0,0 +1,73 @@
+
+SUBDIRS = .
+
+INCLUDES = -I$(srcdir)/include -I$(includedir) -I$(includedir)/infiniband
+
+lib_LTLIBRARIES = libibnetdisc.la
+sbin_PROGRAMS =
+
+if ENABLE_TEST_UTILS
+sbin_PROGRAMS += test/ibnetdisctest \
+                 test/iblinkinfotest \
+                 test/testleaks
+endif
+
+DBGFLAGS = -g
+
+if HAVE_LD_VERSION_SCRIPT
+libibnetdisc_version_script = -Wl,--version-script=$(srcdir)/src/libibnetdisc.map
+else
+libibnetdisc_version_script =
+endif
+
+libibnetdisc_la_SOURCES = src/ibnetdisc.c src/chassis.c src/chassis.h
+libibnetdisc_la_CFLAGS = -Wall $(DBGFLAGS)
+libibnetdisc_la_LDFLAGS = -version-info $(ibnetdisc_api_version) \
+	-export-dynamic $(libibnetdisc_version_script) \
+	-losmcomp -libmad
+libibnetdisc_la_DEPENDENCIES = $(srcdir)/src/libibnetdisc.map
+
+libibnetdiscincludedir = $(includedir)/infiniband
+
+test_ibnetdisctest_SOURCES = test/ibnetdisctest.c
+test_ibnetdisctest_CFLAGS = -Wall $(DBGFLAGS)
+test_ibnetdisctest_LDFLAGS = -Wl,--rpath -Wl,$(libdir) \
+			-libcommon -libnetdisc
+
+test_iblinkinfotest_SOURCES = test/iblinkinfotest.c
+test_iblinkinfotest_CFLAGS = -Wall $(DBGFLAGS)
+test_iblinkinfotest_LDFLAGS = -Wl,--rpath -Wl,$(libdir) \
+			-libcommon -libnetdisc
+
+test_testleaks_SOURCES = test/testleaks.c
+test_testleaks_CFLAGS = -Wall $(DBGFLAGS)
+test_testleaks_LDFLAGS = -Wl,--rpath -Wl,$(libdir) \
+			-libcommon -libnetdisc
+
+libibnetdiscinclude_HEADERS = $(srcdir)/include/infiniband/ibnetdisc.h
+
+man_MANS = man/ibnd_debug.3 \
+	man/ibnd_destroy_fabric.3 \
+	man/ibnd_discover_fabric.3 \
+	man/ibnd_find_node_dr.3 \
+	man/ibnd_find_node_guid.3 \
+	man/ibnd_iter_nodes.3 \
+	man/ibnd_iter_nodes_type.3 \
+	man/ibnd_linkspeed_str.3 \
+	man/ibnd_linkstate_str.3 \
+	man/ibnd_linkwidth_str.3 \
+	man/ibnd_node_type_str.3 \
+	man/ibnd_physstate_str.3 \
+	man/ibnd_update_node.3
+
+EXTRA_DIST = libibnetdisc.spec.in libibnetdisc.spec \
+	$(srcdir)/src/libibnetdisc.map libibnetdisc.ver autogen.sh
+
+dist-hook:
+	if [ -x $(top_srcdir)/../gen_chlog.sh ] ; then \
+		$(top_srcdir)/../gen_chlog.sh $(PACKAGE) > $(distdir)/ChangeLog ; \
+	fi
+	if [ -x $(top_srcdir)/../gen_ver.sh ] ; then \
+		ver=`$(top_srcdir)/../gen_ver.sh $(PACKAGE)` ; \
+		sed -e '/AC_INIT/s/$(PACKAGE), .*,/$(PACKAGE), '$$ver',/' $(top_srcdir)/configure.in > $(distdir)/configure.in ; \
+	fi
diff --git a/libibnetdisc/autogen.sh b/libibnetdisc/autogen.sh
new file mode 100755
index 0000000..4827884
--- /dev/null
+++ b/libibnetdisc/autogen.sh
@@ -0,0 +1,11 @@
+#! /bin/sh
+
+# create config dir if not exist
+test -d config || mkdir config
+
+set -x
+aclocal -I config
+libtoolize --force --copy
+autoheader
+automake --foreign --add-missing --copy
+autoconf
diff --git a/libibnetdisc/configure.in b/libibnetdisc/configure.in
new file mode 100644
index 0000000..e5bb0f9
--- /dev/null
+++ b/libibnetdisc/configure.in
@@ -0,0 +1,68 @@
+dnl Process this file with autoconf to produce a configure script.
+
+AC_PREREQ(2.57)
+AC_INIT(libibnetdisc, 0.0.1, general at lists.openfabrics.org)
+dnl AC_CONFIG_SRCDIR([src/stack.c])
+AC_CONFIG_AUX_DIR(config)
+AM_CONFIG_HEADER(config.h)
+AM_INIT_AUTOMAKE
+
+AC_SUBST(RELEASE, ${RELEASE:-unknown})
+AC_SUBST(TARBALL, ${TARBALL:-${PACKAGE}-${VERSION}.tar.gz})
+
+dnl the library version info is available in the file: libibnetdisc.ver
+ibnetdisc_api_version=`grep LIBVERSION $srcdir/libibnetdisc.ver | sed 's/LIBVERSION=//'`
+if test -z $ibnetdisc_api_version; then
+   echo "FAILED to find $srcdir/libibnetdisc.ver"
+   exit 1
+fi
+AC_SUBST(ibnetdisc_api_version)
+AC_DEFINE_UNQUOTED(API_VERSION,
+	["$ibnetdisc_api_version"],
+	[The API version of this library])
+
+dnl Checks for programs
+AC_PROG_CC
+AC_PROG_CPP
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_MAKE_SET
+AM_PROG_LIBTOOL
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS([stdint.h stdlib.h string.h syslog.h unistd.h])
+
+dnl Checks for library functions
+AC_TYPE_SIGNAL
+AC_FUNC_VPRINTF
+AC_CHECK_FUNCS([strrchr strtoul strtoull])
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_C_INLINE
+AC_STRUCT_TM
+
+AC_CACHE_CHECK(whether ld accepts --version-script, ac_cv_version_script,
+    if test -n "`$LD --help < /dev/null 2>/dev/null | grep version-script`"; then
+        ac_cv_version_script=yes
+    else
+        ac_cv_version_script=no
+    fi)
+
+AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$ac_cv_version_script" = "yes")
+
+dnl Check if we should include test utilities
+AC_MSG_CHECKING(for --enable-test-utils)
+AC_ARG_ENABLE(test-utils,
+[  --enable-test-utils build additional test utilities (default=no)],
+[case "${enableval}" in
+  yes) tutils=yes ;;
+  no)  tutils=no ;;
+  *) AC_MSG_ERROR(bad value ${enableval} for --enable-test-utils) ;;
+esac],[tutils=no])
+AM_CONDITIONAL(ENABLE_TEST_UTILS, test x$tutils = xyes)
+AC_MSG_RESULT(${tutils=no})
+
+AC_CONFIG_FILES([Makefile libibnetdisc.spec])
+AC_OUTPUT
diff --git a/libibnetdisc/include/infiniband/ibnetdisc.h b/libibnetdisc/include/infiniband/ibnetdisc.h
new file mode 100644
index 0000000..92fa8c4
--- /dev/null
+++ b/libibnetdisc/include/infiniband/ibnetdisc.h
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2008 Lawrence Livermore National Lab.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#ifndef _IBNETDISC_H_
+#define _IBNETDISC_H_
+
+#include <stdio.h>
+#include <infiniband/mad.h>
+
+#define MAXHOPS		63
+
+/* HASH table defines */
+#define HASHGUID(guid) ((uint32_t)(((uint32_t)(guid) * 101) ^ ((uint32_t)((guid) >> 32) * 103)))
+#define HTSZ 137
+
+#define	IBND_DEBUG(str, args...) \
+	if (ibdebug) printf("%s:%d; "str, __FILE__, __LINE__, ##args)
+#define	IBND_ERROR(str, args...) \
+	fprintf(stderr, "%s:%d; "str, __FILE__, __LINE__, ##args)
+
+/** =========================================================================
+ * ENUM definitions
+ */
+typedef enum {
+	IBND_CA_NODE	= 1,
+	IBND_SWITCH_NODE = 2,
+	IBND_ROUTER_NODE = 3
+} ibnd_node_type_t;
+
+typedef enum {
+	IBND_LINK_DOWN = 1,
+	IBND_LINK_INIT = 2,
+	IBND_LINK_ARMED = 3,
+	IBND_LINK_ACTIVE = 4
+} ibnd_link_state_t;
+
+/** =========================================================================
+ * Node
+ */
+typedef struct switch_info {
+	int smaenhsp0;
+} ibnd_switch_info_t;
+
+typedef struct node_info {
+	int base_ver;
+	int class_ver;
+	int type;
+	int numports;
+	uint64_t sysimgguid;
+	uint64_t nodeguid;
+	uint64_t nodeportguid;
+	uint16_t partition_cap;
+	uint32_t devid;
+	uint32_t revision;
+	int localport;
+	uint32_t vendid;
+} ibnd_node_info_t;
+
+struct port;
+struct ib_fabric;
+struct chassis_record;
+typedef struct node {
+	struct node *next; /* all node list in fabric */
+	struct node *htnext; /* store node in guid hash table */
+	struct node *dnext; /* store node in nodesdist table */
+	struct node *type_next; /* store node in "type" list (ca|switch|router) */
+
+	struct ib_fabric *fabric; /* the fabric node belongs to */
+
+	ib_portid_t path_portid; /* path from "from_node" */
+	int dist; /* num of hops from "from_node" */
+
+	int smalid;
+	int smalmc;
+	ibnd_switch_info_t sw_info;
+	ibnd_node_info_t info;
+
+	char nodedesc[64];
+
+	struct port **ports; /* in order array of port pointers */
+				/* the size of this array is info.numports + 1 */
+				/* items MAY BE NULL!  (ie 0 == switches only) */
+
+	/* chassis info */
+	struct node *chassis_next; /* store node in "chassis" list */
+	struct chassis_record *chrecord;
+
+	void *user_data; /* users can store data here */
+} ibnd_node_t;
+
+/** =========================================================================
+ * Port
+ */
+typedef struct port_info {
+	int lid;
+	int smlid;
+	int link_speed_supported;
+	int link_speed_enabled;
+	int link_speed_active;
+	int link_state;
+	int phys_state;
+	int link_down_def_state;
+	int mkey_prot_bits;
+	int lmc;
+	int neighbor_mtu;
+	int smsl;
+	int init_type;
+	int vl_capability;
+	int vl_high_limit;
+	int vl_arb_high_cap;
+	int vl_arb_low_cap;
+	int init_reply;
+	int mtu_cap;
+	int vl_stall_count;
+	int hoq_lifetime;
+	int oper_vls;
+	int partition_enforce_in;
+	int partition_enforce_out;
+	int filter_raw_in;
+	int filter_raw_out;
+	int mkey_violations;
+	int pkey_violations;
+	int qkey_violations;
+	int guid_capabilities;
+	int client_rereg;
+	int subnet_timeout;
+	int response_time_val;
+	int local_phys_error;
+	int overrun_error;
+	int max_credit_hint;
+	uint32_t link_round_trip;
+	int local_port;
+	int link_width_supported;
+	int link_width_enabled;
+	int link_width_active;
+	int diag_code;
+	int mkey_lease;
+	uint32_t capability_mask;
+	uint64_t mkey;
+	uint64_t gid_prefix;
+} ibnd_port_info_t;
+
+typedef struct port {
+	struct port *htnext;
+	uint64_t guid;
+	int portnum;
+	int ext_portnum; /* optional (!= 0) external port num */
+	ibnd_node_t *node;
+	struct port *remoteport; /* null if SMA, or does not exist */
+	ibnd_port_info_t info;
+	void *user_data; /* users can store data here */
+} ibnd_port_t;
+
+
+/** =========================================================================
+ * Chassis data
+ */
+typedef struct chassis_record {
+	struct chassis_record *next;
+	unsigned char chassisnum;
+	unsigned char chassistype;
+	unsigned char anafanum;
+	unsigned char slotnum;
+	unsigned char chassisslot;
+} ibnd_chassis_record_t;
+
+#define SPINES_MAX_NUM 12
+#define LINES_MAX_NUM 36
+
+typedef struct chassis_list {
+	struct chassis_list *next;
+	uint64_t chassisguid;
+	int chassisnum;
+	int chassistype;
+
+	/* generic grouping by SystemImageGUID */
+	int nodecount;
+	ibnd_node_t *nodes;
+
+	/* specific to voltaire type nodes */
+	ibnd_node_t *spinenode[SPINES_MAX_NUM + 1];
+	ibnd_node_t *linenode[LINES_MAX_NUM + 1];
+} ibnd_chassis_list_t;
+
+/** =========================================================================
+ * Fabric
+ * Main fabric object which is returned and represents the data discovered
+ */
+typedef struct ib_fabric {
+	/* the node which you requested to start on
+	 * "from" parameter in ibnd_discover_fabric
+	 */
+	ibnd_node_t *from_node;
+
+	/* list of all nodes in the system */
+	ibnd_node_t *nodes;
+
+	/* NULL terminated lists of node types */
+	ibnd_node_t *switches;
+	ibnd_node_t *ch_adapters;
+	ibnd_node_t *routers;
+
+	/* list of all chassis found in the fabric */
+	ibnd_chassis_list_t *chassis;
+
+	/* the following are for internal use */
+	void *ibmad_port;
+	ibnd_node_t *nodestbl[HTSZ];
+	ibnd_port_t *portstbl[HTSZ];
+	int maxhops_discovered;
+	ibnd_node_t *nodesdist[MAXHOPS+1];
+	ibnd_chassis_list_t *first_chassis;
+	ibnd_chassis_list_t *current_chassis;
+} ibnd_fabric_t;
+
+
+/** =========================================================================
+ * Initialization (fabric operations)
+ */
+void           ibnd_debug(int i);
+void           ibnd_show_progress(int i);
+
+ibnd_fabric_t *ibnd_discover_fabric(char *dev_name, int dev_port,
+			int timeout_ms, ib_portid_t *from, int hops);
+	/**
+	 * dev_name: (required) local device name to use to access the fabric
+	 * dev_port: (required) local device port to use to access the fabric
+	 * timeout_ms: (required) gives the timeout for a _SINGLE_ query on
+	 *             the fabric.  So if there are mutiple nodes not
+	 *             responding this may result in a lengthy delay.
+	 * from: (optional) specify the node to start scanning from.
+	 *       If NULL start from the node we are running on.
+	 * hops: (optional) Specify how much of the fabric to traverse.
+	 *       negative value == scan entire fabric
+	 */
+void           ibnd_destroy_fabric(ibnd_fabric_t *fabric);
+
+/** =========================================================================
+ * Node operations
+ */
+typedef void (*ibnd_iter_func_t)(ibnd_node_t *node, void *user_data);
+
+ibnd_node_t *ibnd_find_node_guid(ibnd_fabric_t *fabric, uint64_t guid);
+ibnd_node_t *ibnd_find_node_dr(ibnd_fabric_t *fabric, char *dr_str);
+ibnd_node_t *ibnd_update_node(ibnd_node_t *node);
+
+void         ibnd_iter_nodes(ibnd_fabric_t *fabric,
+				ibnd_iter_func_t func,
+				void *user_data);
+void         ibnd_iter_nodes_type(ibnd_fabric_t *fabric,
+				ibnd_iter_func_t func,
+				ibnd_node_type_t node_type,
+				void *user_data);
+
+/** =========================================================================
+ * Str convert functions
+ */
+char          *ibnd_linkwidth_str(int link_width);
+char          *ibnd_linkspeed_str(int link_speed);
+char          *ibnd_linkstate_str(int link_state);
+char          *ibnd_physstate_str(int phys_state);
+const char    *ibnd_node_type_str(ibnd_node_t *node);
+const char    *ibnd_node_type_str_short(ibnd_node_t *node);
+
+/** =========================================================================
+ * Chassis queries
+ */
+uint64_t  ibnd_get_chassis_guid(ibnd_fabric_t *fabric, unsigned char chassisnum);
+char     *ibnd_get_chassis_type(ibnd_node_t *node);
+char     *ibnd_get_chassis_slot_str(ibnd_node_t *node, char *str, size_t size);
+
+int       ibnd_is_xsigo_guid(uint64_t guid);
+int       ibnd_is_xsigo_tca(uint64_t guid);
+int       ibnd_is_xsigo_hca(uint64_t guid);
+
+#endif	/* _IBNETDISC_H_ */
diff --git a/libibnetdisc/libibnetdisc.spec.in b/libibnetdisc/libibnetdisc.spec.in
new file mode 100644
index 0000000..015cd24
--- /dev/null
+++ b/libibnetdisc/libibnetdisc.spec.in
@@ -0,0 +1,94 @@
+
+%define RELEASE @RELEASE@
+%define rel %{?CUSTOM_RELEASE} %{!?CUSTOM_RELEASE:%RELEASE}
+
+%if %{?_with_test_utils:1}%{!?_with_test_utils:0}
+%define _enable_test_utils --enable-test-utils
+%endif
+%if %{?_without_test_utils:1}%{!?_without_test_utils:0}
+%define _disable_test_utils --disable-test-utils
+%endif
+
+Summary: OpenFabrics Alliance InfiniBand MAD library
+Name: libibnetdisc
+Version: @VERSION@
+Release: %rel%{?dist}
+License: GPLv2 or BSD
+Group: System Environment/Libraries
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+Source: http://www.openfabrics.org/downloads/management/@TARBALL@
+Url: http://openfabrics.org/
+BuildRequires: opensm-libs, libtool, libibcommon, libibumad
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+
+%description
+libibnetdisc provides a higer level C interface to scaning an IB fabric.
+
+%package devel
+Summary: Development files for the libibnetdisc library
+Group: System Environment/Libraries
+Requires: %{name} = %{version}-%{release}, opensm-devel, libibcommon-devel, libibumad-devel
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+
+%description devel
+Development files for the libibnetdisc library.
+
+%package static
+Summary: Static version of the libibnetdisc library
+Group: System Environment/Libraries
+Requires: %{name} = %{version}-%{release}
+
+%description static
+Static version of the libibnetdisc library
+
+%if %{?_with_test_utils:1}%{!?_with_test_utils:0}
+%package utils
+Summary: Debug utilities built against libibnetdisc
+Group: System Environment/Libraries
+Requires: %{name} = %{version}-%{release}
+
+%description utils
+Debug utilities built against libibnetdisc
+
+%files utils
+%defattr(-,root,root)
+%{_sbindir}/*
+%endif
+
+%prep
+%setup -q
+
+%build
+%configure \
+   %{?_enable_test_utils} \
+   %{?_disable_test_utils}
+make
+
+%install
+make DESTDIR=${RPM_BUILD_ROOT} install
+# remove unpackaged files from the buildroot
+rm -f $RPM_BUILD_ROOT%{_libdir}/*.la
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post -p /sbin/ldconfig
+%postun -p /sbin/ldconfig
+%post devel -p /sbin/ldconfig
+%postun devel -p /sbin/ldconfig
+
+%files
+%defattr(-,root,root)
+%{_libdir}/libibnetdisc*.so.*
+%doc AUTHORS COPYING ChangeLog
+
+%files devel
+%defattr(-,root,root)
+%{_libdir}/libibnetdisc.so
+%{_includedir}/infiniband/*.h
+
+%files static
+%defattr(-,root,root)
+%{_libdir}/libibnetdisc.a
diff --git a/libibnetdisc/libibnetdisc.ver b/libibnetdisc/libibnetdisc.ver
new file mode 100644
index 0000000..a0a5f3c
--- /dev/null
+++ b/libibnetdisc/libibnetdisc.ver
@@ -0,0 +1,9 @@
+# In this file we track the current API version
+# of the IB net discover interface (and libraries)
+# The version is built of the following
+# tree numbers:
+# API_REV:RUNNING_REV:AGE
+# API_REV - advance on any added API
+# RUNNING_REV - advance any change to the vendor files
+# AGE - number of backward versions the API still supports
+LIBVERSION=1:0:0
diff --git a/libibnetdisc/man/ibnd_debug.3 b/libibnetdisc/man/ibnd_debug.3
new file mode 100644
index 0000000..a4076fc
--- /dev/null
+++ b/libibnetdisc/man/ibnd_debug.3
@@ -0,0 +1,2 @@
+.\".TH IBND_DEBUG 3  "Aug 04, 2008" "OpenIB" "OpenIB Programmer's Manual"
+.so man3/ibnd_discover_fabric.3
diff --git a/libibnetdisc/man/ibnd_destroy_fabric.3 b/libibnetdisc/man/ibnd_destroy_fabric.3
new file mode 100644
index 0000000..8fe20ae
--- /dev/null
+++ b/libibnetdisc/man/ibnd_destroy_fabric.3
@@ -0,0 +1,2 @@
+.\".TH IBND_DESTROY_FABRIC 3  "Aug 04, 2008" "OpenIB" "OpenIB Programmer's Manual"
+.so man3/ibnd_discover_fabric.3
diff --git a/libibnetdisc/man/ibnd_discover_fabric.3 b/libibnetdisc/man/ibnd_discover_fabric.3
new file mode 100644
index 0000000..0db23f4
--- /dev/null
+++ b/libibnetdisc/man/ibnd_discover_fabric.3
@@ -0,0 +1,43 @@
+.TH IBND_DISCOVER_FABRIC 3  "July 25, 2008" "OpenIB" "OpenIB Programmer's Manual"
+.SH "NAME"
+ibnd_discover_fabric, ibnd_destroy_fabric, ibnd_debug \- initialize ibnetdiscover library.
+.SH "SYNOPSIS"
+.nf
+.B #include <infiniband/ibnetdisc.h>
+.sp
+.BI "ibnd_fabric_t *ibnd_discover_fabric(char *dev_name, int dev_port, int timeout_ms, ib_portid_t *from, int hops)"
+.BI "void ibnd_destroy_fabric(ibnd_fabric_t *fabric)"
+.BI "void ibnd_debug(int i)"
+
+.SH "DESCRIPTION"
+.B ibnd_discover_fabric()
+Discover the fabric connected to the port specified by dev_name and dev_port, using a timeout specified.  The "from" and "hops" parameters are optional and allow one to scan part of a fabric by specifying a node "from" and a number of hops away from that node to scan, "hops".  This gives the user a "sub-fabric" which is "centered" anywhere they chose.
+
+.B ibnd_destroy_fabric()
+free all memory and resources associated with the fabric.
+
+.B ibnd_debug()
+Set the debug level to be printed as library operations take place.
+
+.SH "RETURN VALUE"
+.B ibnd_discover_fabric()
+return NULL on failure, otherwise a valid ibnd_fabric_t object.
+
+.B ibnd_destory_fabric(), ibnd_debug()
+NONE
+
+.SH "EXAMPLES"
+
+.B Discover the entire fabric connected to device "mthca0", port 1.
+
+	ibnd_discover_fabric("mthca0", 1, 100, NULL, 0);
+
+.B Discover only a single node and those nodes connected to it.
+
+	str2drpath(&(port_id.drpath), from, 0, 0);
+
+	ibnd_discover_fabric("mthca0", 1, 100, &port_id, 1);
+
+.SH "AUTHORS"
+.TP
+Ira Weiny <weiny2 at llnl.gov>
diff --git a/libibnetdisc/man/ibnd_find_node_dr.3 b/libibnetdisc/man/ibnd_find_node_dr.3
new file mode 100644
index 0000000..612e501
--- /dev/null
+++ b/libibnetdisc/man/ibnd_find_node_dr.3
@@ -0,0 +1,2 @@
+.\".TH IBND_FIND_NODE_DR 3  "Aug 04, 2008" "OpenIB" "OpenIB Programmer's Manual"
+.so man3/ibnd_find_node_guid.3
diff --git a/libibnetdisc/man/ibnd_find_node_guid.3 b/libibnetdisc/man/ibnd_find_node_guid.3
new file mode 100644
index 0000000..676b528
--- /dev/null
+++ b/libibnetdisc/man/ibnd_find_node_guid.3
@@ -0,0 +1,25 @@
+.TH IBND_FIND_NODE_GUID 3  "July 25, 2008" "OpenIB" "OpenIB Programmer's Manual"
+.SH "NAME"
+ibnd_find_node_guid, ibnd_find_node_dr \- given a fabric object find the node object within it which matches the guid or directed route specified.
+
+.SH "SYNOPSIS"
+.nf
+.B #include <infiniband/ibnetdisc.h>
+.sp
+.BI "ibnd_node_t *ibnd_find_node_guid(ibnd_fabric_t *fabric, uint64_t guid)"
+.BI "ibnd_node_t *ibnd_find_node_dr(ibnd_fabric_t *fabric, char *dr_str)"
+
+.SH "DESCRIPTION"
+.B ibnd_find_node_guid()
+Given a fabric object and a guid, return the ibnd_node_t object with that node guid.
+.B ibnd_find_node_dr()
+Given a fabric object and a directed route, return the ibnd_node_t object with
+that directed route.
+
+.SH "RETURN VALUE"
+.B ibnd_find_node_guid(), ibnd_find_node_dr()
+return NULL on failure, otherwise a valid ibnd_node_t object.
+
+.SH "AUTHORS"
+.TP
+Ira Weiny <weiny2 at llnl.gov>
diff --git a/libibnetdisc/man/ibnd_iter_nodes.3 b/libibnetdisc/man/ibnd_iter_nodes.3
new file mode 100644
index 0000000..7199dfb
--- /dev/null
+++ b/libibnetdisc/man/ibnd_iter_nodes.3
@@ -0,0 +1,24 @@
+.TH IBND_ITER_NODES 3  "July 25, 2008" "OpenIB" "OpenIB Programmer's Manual"
+.SH "NAME"
+ibnd_iter_nodes, ibnd_iter_nodes_type \- given a fabric object and a function itterate over the nodes in the fabric.
+
+.SH "SYNOPSIS"
+.nf
+.B #include <infiniband/ibnetdisc.h>
+.sp
+.BI "void ibnd_iter_nodes(ibnd_fabric_t *fabric, ibnd_iter_func_t func, void *user_data)"
+.BI "void ibnd_iter_nodes_type(ibnd_fabric_t *fabric, ibnd_iter_func_t func, ibnd_node_type_t type, void *user_data)"
+
+.SH "DESCRIPTION"
+.B ibnd_iter_nodes()
+Itterate through all the nodes in the fabric and call "func" on them.
+.B ibnd_iter_nodes_type()
+The same as ibnd_iter_nodes except to limit the iteration to the nodes with the specified type.
+
+.SH "RETURN VALUE"
+.B ibnd_iter_nodes(), ibnd_iter_nodes_type()
+NONE
+
+.SH "AUTHORS"
+.TP
+Ira Weiny <weiny2 at llnl.gov>
diff --git a/libibnetdisc/man/ibnd_iter_nodes_type.3 b/libibnetdisc/man/ibnd_iter_nodes_type.3
new file mode 100644
index 0000000..878547c
--- /dev/null
+++ b/libibnetdisc/man/ibnd_iter_nodes_type.3
@@ -0,0 +1,2 @@
+.\".TH IBND_FIND_NODES_TYPE 3  "Aug 04, 2008" "OpenIB" "OpenIB Programmer's Manual"
+.so man3/ibnd_find_nodes.3
diff --git a/libibnetdisc/man/ibnd_linkspeed_str.3 b/libibnetdisc/man/ibnd_linkspeed_str.3
new file mode 100644
index 0000000..128cd3e
--- /dev/null
+++ b/libibnetdisc/man/ibnd_linkspeed_str.3
@@ -0,0 +1,2 @@
+.\".TH IBND_LINKSPEED_STR 3  "Aug 04, 2008" "OpenIB" "OpenIB Programmer's Manual"
+.so man3/ibnd_linkwidth_str.3
diff --git a/libibnetdisc/man/ibnd_linkstate_str.3 b/libibnetdisc/man/ibnd_linkstate_str.3
new file mode 100644
index 0000000..2fa9189
--- /dev/null
+++ b/libibnetdisc/man/ibnd_linkstate_str.3
@@ -0,0 +1,2 @@
+.\".TH IBND_LINKSTATE_STR 3  "Aug 04, 2008" "OpenIB" "OpenIB Programmer's Manual"
+.so man3/ibnd_linkwidth_str.3
diff --git a/libibnetdisc/man/ibnd_linkwidth_str.3 b/libibnetdisc/man/ibnd_linkwidth_str.3
new file mode 100644
index 0000000..2cd4f0a
--- /dev/null
+++ b/libibnetdisc/man/ibnd_linkwidth_str.3
@@ -0,0 +1,26 @@
+.TH IBND_LINKWIDTH_STR 3  "July 25, 2008" "OpenIB" "OpenIB Programmer's Manual"
+.SH "NAME"
+ibnd_linkwidth_str, ibnd_linkspeed_str, ibnd_linkstate_str, ibnd_physstate_str, ibnd_node_type_str \- prety string functions.
+
+.SH "SYNOPSIS"
+.nf
+.B #include <infiniband/ibnetdisc.h>
+.sp
+.BI
+.BI "char          *ibnd_linkwidth_str(int link_width)"
+.BI "char          *ibnd_linkspeed_str(int link_speed)"
+.BI "char          *ibnd_linkstate_str(int link_state)"
+.BI "char          *ibnd_physstate_str(int phys_state)"
+.BI "const char    *ibnd_node_type_str(ibnd_node_t *node)"
+.BI "const char    *ibnd_node_type_str_short(ibnd_node_t *node)"
+
+.SH "DESCRIPTION"
+Return user readable strings for the values given.
+
+.BI "const char    *ibnd_node_type_str_short(ibnd_node_t *node)"
+Returns a shorter abbreviated version of the string.
+
+
+.SH "AUTHORS"
+.TP
+Ira Weiny <weiny2 at llnl.gov>
diff --git a/libibnetdisc/man/ibnd_node_type_str.3 b/libibnetdisc/man/ibnd_node_type_str.3
new file mode 100644
index 0000000..77dbf07
--- /dev/null
+++ b/libibnetdisc/man/ibnd_node_type_str.3
@@ -0,0 +1,2 @@
+.\".TH IBND_NODE_TYPE_STR 3  "Aug 04, 2008" "OpenIB" "OpenIB Programmer's Manual"
+.so man3/ibnd_linkwidth_str.3
diff --git a/libibnetdisc/man/ibnd_node_type_str_short.3 b/libibnetdisc/man/ibnd_node_type_str_short.3
new file mode 100644
index 0000000..62feb6e
--- /dev/null
+++ b/libibnetdisc/man/ibnd_node_type_str_short.3
@@ -0,0 +1,2 @@
+.\".TH IBND_NODE_TYPE_STR_SHORT 3  "Aug 05, 2008" "OpenIB" "OpenIB Programmer's Manual"
+.so man3/ibnd_linkwidth_str.3
diff --git a/libibnetdisc/man/ibnd_physstate_str.3 b/libibnetdisc/man/ibnd_physstate_str.3
new file mode 100644
index 0000000..aeeaeb7
--- /dev/null
+++ b/libibnetdisc/man/ibnd_physstate_str.3
@@ -0,0 +1,2 @@
+.\".TH IBND_PHYSSTATE_STR 3  "Aug 04, 2008" "OpenIB" "OpenIB Programmer's Manual"
+.so man3/ibnd_physstate_str.3
diff --git a/libibnetdisc/man/ibnd_update_node.3 b/libibnetdisc/man/ibnd_update_node.3
new file mode 100644
index 0000000..d3aa206
--- /dev/null
+++ b/libibnetdisc/man/ibnd_update_node.3
@@ -0,0 +1,21 @@
+.TH IBND_UPDATE_NODE 3  "July 25, 2008" "OpenIB" "OpenIB Programmer's Manual"
+.SH "NAME"
+ibnd_update_node \- Update the node specified with new data from the fabric.
+
+.SH "SYNOPSIS"
+.nf
+.B #include <infiniband/ibnetdisc.h>
+.sp
+.BI "ibnd_node_t *ibnd_update_node(ibnd_node_t *node)"
+
+.SH "DESCRIPTION"
+.B ibnd_update_node()
+Update the node info, port info, and node description of the node specified.
+
+.SH "RETURN VALUE"
+.B ibnd_update_node()
+Return NULL on failure, otherwise a valid ibnd_node_t object which is part of the fabric object.
+
+.SH "AUTHORS"
+.TP
+Ira Weiny <weiny2 at llnl.gov>
diff --git a/libibnetdisc/src/chassis.c b/libibnetdisc/src/chassis.c
new file mode 100644
index 0000000..5f9c073
--- /dev/null
+++ b/libibnetdisc/src/chassis.c
@@ -0,0 +1,820 @@
+/*
+ * Copyright (c) 2004-2007 Voltaire Inc.  All rights reserved.
+ * Copyright (c) 2007 Xsigo Systems Inc.  All rights reserved.
+ * Copyright (c) 2008 Lawrence Livermore National Lab.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+/*========================================================*/
+/*               FABRIC SCANNER SPECIFIC DATA             */
+/*========================================================*/
+
+#if HAVE_CONFIG_H
+#  include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include <infiniband/common.h>
+#include <infiniband/mad.h>
+
+#include "chassis.h"
+
+static char *ChassisTypeStr[5] = { "", "ISR9288", "ISR9096", "ISR2012", "ISR2004" };
+static char *ChassisSlotTypeStr[4] = { "", "Line", "Spine", "SRBD" };
+
+char *ibnd_get_chassis_type(ibnd_node_t *node)
+{
+	/* Currently, only if Voltaire chassis */
+	if (node->info.vendid != VTR_VENDOR_ID)
+		return (NULL);
+	if (!node->chrecord)
+		return (NULL);
+	if (node->chrecord->chassistype == UNRESOLVED_CT
+		|| node->chrecord->chassistype > ISR2004_CT)
+		return (NULL);
+	return ChassisTypeStr[node->chrecord->chassistype];
+}
+
+char *ibnd_get_chassis_slot_str(ibnd_node_t *node, char *str, size_t size)
+{
+	/* Currently, only if Voltaire chassis */
+	if (node->info.vendid != VTR_VENDOR_ID)
+		return (NULL);
+	if (!node->chrecord)
+		return (NULL);
+	if (node->chrecord->chassisslot == UNRESOLVED_CS
+		|| node->chrecord->chassisslot > SRBD_CS)
+		return (NULL);
+	if (!str)
+		return (NULL);
+	snprintf(str, size, "%s %d Chip %d",
+			ChassisSlotTypeStr[node->chrecord->chassisslot],
+			node->chrecord->slotnum,
+			node->chrecord->anafanum);
+	return (str);
+}
+
+static ibnd_chassis_list_t *find_chassisnum(ibnd_fabric_t *fabric, unsigned char chassisnum)
+{
+	ibnd_chassis_list_t *current;
+
+	for (current = fabric->first_chassis; current; current = current->next) {
+		if (current->chassisnum == chassisnum)
+			return current;
+	}
+
+	return NULL;
+}
+
+static uint64_t topspin_chassisguid(uint64_t guid)
+{
+	/* Byte 3 in system image GUID is chassis type, and */
+	/* Byte 4 is location ID (slot) so just mask off byte 4 */
+	return guid & 0xffffffff00ffffffULL;
+}
+
+int ibnd_is_xsigo_guid(uint64_t guid)
+{
+	if ((guid & 0xffffff0000000000ULL) == 0x0013970000000000ULL)
+		return 1;
+	else
+		return 0;
+}
+
+static int is_xsigo_leafone(uint64_t guid)
+{
+	if ((guid & 0xffffffffff000000ULL) == 0x0013970102000000ULL)
+		return 1;
+	else
+		return 0;
+}
+
+int ibnd_is_xsigo_hca(uint64_t guid)
+{
+	/* NodeType 2 is HCA */
+	if ((guid & 0xffffffff00000000ULL) == 0x0013970200000000ULL)
+		return 1;
+	else
+		return 0;
+}
+
+int ibnd_is_xsigo_tca(uint64_t guid)
+{
+	/* NodeType 3 is TCA */
+	if ((guid & 0xffffffff00000000ULL) == 0x0013970300000000ULL)
+		return 1;
+	else
+		return 0;
+}
+
+static int is_xsigo_ca(uint64_t guid)
+{
+	if (ibnd_is_xsigo_hca(guid) || ibnd_is_xsigo_tca(guid))
+		return 1;
+	else
+		return 0;
+}
+
+static int is_xsigo_switch(uint64_t guid)
+{
+	if ((guid & 0xffffffff00000000ULL) == 0x0013970100000000ULL)
+		return 1;
+	else
+		return 0;
+}
+
+static uint64_t xsigo_chassisguid(ibnd_node_t *node)
+{
+	if (!is_xsigo_ca(node->info.sysimgguid)) {
+		/* Byte 3 is NodeType and byte 4 is PortType */
+		/* If NodeType is 1 (switch), PortType is masked */
+		if (is_xsigo_switch(node->info.sysimgguid))
+			return node->info.sysimgguid & 0xffffffff00ffffffULL;
+		else
+			return node->info.sysimgguid;
+	} else {
+		if (!node->ports || !node->ports[1])
+			return (0);
+
+		/* Is there a peer port ? */
+		if (!node->ports[1]->remoteport)
+			return node->info.sysimgguid;
+
+		/* If peer port is Leaf 1, use its chassis GUID */
+		if (is_xsigo_leafone(node->ports[1]->remoteport->node->info.sysimgguid))
+			return node->ports[1]->remoteport->node->info.sysimgguid &
+			       0xffffffff00ffffffULL;
+		else
+			return node->info.sysimgguid;
+	}
+}
+
+static uint64_t get_chassisguid(ibnd_node_t *node)
+{
+	if (node->info.vendid == TS_VENDOR_ID || node->info.vendid == SS_VENDOR_ID)
+		return topspin_chassisguid(node->info.sysimgguid);
+	else if (node->info.vendid == XS_VENDOR_ID || ibnd_is_xsigo_guid(node->info.sysimgguid))
+		return xsigo_chassisguid(node);
+	else
+		return node->info.sysimgguid;
+}
+
+static ibnd_chassis_list_t *find_chassisguid(ibnd_node_t *node)
+{
+	ibnd_chassis_list_t *current;
+	uint64_t chguid;
+
+	chguid = get_chassisguid(node);
+	for (current = node->fabric->first_chassis; current; current = current->next) {
+		if (current->chassisguid == chguid)
+			return current;
+	}
+
+	return NULL;
+}
+
+uint64_t ibnd_get_chassis_guid(ibnd_fabric_t *fabric, unsigned char chassisnum)
+{
+	ibnd_chassis_list_t *chassis;
+
+	chassis = find_chassisnum(fabric, chassisnum);
+	if (chassis)
+		return chassis->chassisguid;
+	else
+		return 0;
+}
+
+static int is_router(ibnd_node_t *node)
+{
+	return (node->info.devid == VTR_DEVID_IB_FC_ROUTER ||
+		node->info.devid == VTR_DEVID_IB_IP_ROUTER);
+}
+
+static int is_spine_9096(ibnd_node_t *node)
+{
+	return (node->info.devid == VTR_DEVID_SFB4 ||
+		node->info.devid == VTR_DEVID_SFB4_DDR);
+}
+
+static int is_spine_9288(ibnd_node_t *node)
+{
+	return (node->info.devid == VTR_DEVID_SFB12 ||
+		node->info.devid == VTR_DEVID_SFB12_DDR);
+}
+
+static int is_spine_2004(ibnd_node_t *node)
+{
+	return (node->info.devid == VTR_DEVID_SFB2004);
+}
+
+static int is_spine_2012(ibnd_node_t *node)
+{
+	return (node->info.devid == VTR_DEVID_SFB2012);
+}
+
+static int is_spine(ibnd_node_t *node)
+{
+	return (is_spine_9096(node) || is_spine_9288(node) ||
+		is_spine_2004(node) || is_spine_2012(node));
+}
+
+static int is_line_24(ibnd_node_t *node)
+{
+	return (node->info.devid == VTR_DEVID_SLB24 ||
+		node->info.devid == VTR_DEVID_SLB24_DDR);
+}
+
+static int is_line_8(ibnd_node_t *node)
+{
+	return (node->info.devid == VTR_DEVID_SLB8);
+}
+
+static int is_line_2024(ibnd_node_t *node)
+{
+	return (node->info.devid == VTR_DEVID_SLB2024);
+}
+
+static int is_line(ibnd_node_t *node)
+{
+	return (is_line_24(node) || is_line_8(node) || is_line_2024(node));
+}
+
+int is_chassis_switch(ibnd_node_t *node)
+{
+    return (is_spine(node) || is_line(node));
+}
+
+/* these structs help find Line (Anafa) slot number while using spine portnum */
+int line_slot_2_sfb4[25]        = { 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4 };
+int anafa_line_slot_2_sfb4[25]  = { 0, 1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 2, 2 };
+int line_slot_2_sfb12[25]       = { 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,10, 10, 11, 11, 12, 12 };
+int anafa_line_slot_2_sfb12[25] = { 0, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 };
+
+/* IPR FCR modules connectivity while using sFB4 port as reference */
+int ipr_slot_2_sfb4_port[25]    = { 0, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1 };
+
+/* these structs help find Spine (Anafa) slot number while using spine portnum */
+int spine12_slot_2_slb[25]      = { 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+int anafa_spine12_slot_2_slb[25]= { 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+int spine4_slot_2_slb[25]       = { 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+int anafa_spine4_slot_2_slb[25] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+/*	reference                     { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }; */
+
+static void get_sfb_slot(ibnd_node_t *node, ibnd_port_t *lineport)
+{
+	ibnd_chassis_record_t *ch = node->chrecord;
+
+	ch->chassisslot = SPINE_CS;
+	if (is_spine_9096(node)) {
+		ch->chassistype = ISR9096_CT;
+		ch->slotnum = spine4_slot_2_slb[lineport->portnum];
+		ch->anafanum = anafa_spine4_slot_2_slb[lineport->portnum];
+	} else if (is_spine_9288(node)) {
+		ch->chassistype = ISR9288_CT;
+		ch->slotnum = spine12_slot_2_slb[lineport->portnum];
+		ch->anafanum = anafa_spine12_slot_2_slb[lineport->portnum];
+	} else if (is_spine_2012(node)) {
+		ch->chassistype = ISR2012_CT;
+		ch->slotnum = spine12_slot_2_slb[lineport->portnum];
+		ch->anafanum = anafa_spine12_slot_2_slb[lineport->portnum];
+	} else if (is_spine_2004(node)) {
+		ch->chassistype = ISR2004_CT;
+		ch->slotnum = spine4_slot_2_slb[lineport->portnum];
+		ch->anafanum = anafa_spine4_slot_2_slb[lineport->portnum];
+	} else {
+		IBPANIC("Unexpected node found: guid 0x%016" PRIx64,
+		node->info.nodeguid);
+	}
+}
+
+static void get_router_slot(ibnd_node_t *node, ibnd_port_t *spineport)
+{
+	ibnd_chassis_record_t *ch = node->chrecord;
+	int guessnum = 0;
+
+	if (!ch) {
+		if (!(node->chrecord = calloc(1, sizeof(ibnd_chassis_record_t))))
+			IBPANIC("out of mem");
+		ch = node->chrecord;
+	}
+
+	ch->chassisslot = SRBD_CS;
+	if (is_spine_9096(spineport->node)) {
+		ch->chassistype = ISR9096_CT;
+		ch->slotnum = line_slot_2_sfb4[spineport->portnum];
+		ch->anafanum = ipr_slot_2_sfb4_port[spineport->portnum];
+	} else if (is_spine_9288(spineport->node)) {
+		ch->chassistype = ISR9288_CT;
+		ch->slotnum = line_slot_2_sfb12[spineport->portnum];
+		/* this is a smart guess based on nodeguids order on sFB-12 module */
+		guessnum = spineport->node->info.nodeguid % 4;
+		/* module 1 <--> remote anafa 3 */
+		/* module 2 <--> remote anafa 2 */
+		/* module 3 <--> remote anafa 1 */
+		ch->anafanum = (guessnum == 3 ? 1 : (guessnum == 1 ? 3 : 2));
+	} else if (is_spine_2012(spineport->node)) {
+		ch->chassistype = ISR2012_CT;
+		ch->slotnum = line_slot_2_sfb12[spineport->portnum];
+		/* this is a smart guess based on nodeguids order on sFB-12 module */
+		guessnum = spineport->node->info.nodeguid % 4;
+		// module 1 <--> remote anafa 3
+		// module 2 <--> remote anafa 2
+		// module 3 <--> remote anafa 1
+		ch->anafanum = (guessnum == 3? 1 : (guessnum == 1 ? 3 : 2));
+	} else if (is_spine_2004(spineport->node)) {
+		ch->chassistype = ISR2004_CT;
+		ch->slotnum = line_slot_2_sfb4[spineport->portnum];
+		ch->anafanum = ipr_slot_2_sfb4_port[spineport->portnum];
+	} else {
+		IBPANIC("Unexpected node found: guid 0x%016" PRIx64,
+		spineport->node->info.nodeguid);
+	}
+}
+
+static void get_slb_slot(ibnd_chassis_record_t *ch, ibnd_port_t *spineport)
+{
+	ch->chassisslot = LINE_CS;
+	if (is_spine_9096(spineport->node)) {
+		ch->chassistype = ISR9096_CT;
+		ch->slotnum = line_slot_2_sfb4[spineport->portnum];
+		ch->anafanum = anafa_line_slot_2_sfb4[spineport->portnum];
+	} else if (is_spine_9288(spineport->node)) {
+		ch->chassistype = ISR9288_CT;
+		ch->slotnum = line_slot_2_sfb12[spineport->portnum];
+		ch->anafanum = anafa_line_slot_2_sfb12[spineport->portnum];
+	} else if (is_spine_2012(spineport->node)) {
+		ch->chassistype = ISR2012_CT;
+		ch->slotnum = line_slot_2_sfb12[spineport->portnum];
+		ch->anafanum = anafa_line_slot_2_sfb12[spineport->portnum];
+	} else if (is_spine_2004(spineport->node)) {
+		ch->chassistype = ISR2004_CT;
+		ch->slotnum = line_slot_2_sfb4[spineport->portnum];
+		ch->anafanum = anafa_line_slot_2_sfb4[spineport->portnum];
+	} else {
+		IBPANIC("Unexpected node found: guid 0x%016" PRIx64,
+		spineport->node->info.nodeguid);
+	}
+}
+
+/* forward declare this */
+static void voltaire_portmap(ibnd_port_t *port);
+/*
+	This function called for every Voltaire node in fabric
+	It could be optimized so, but time overhead is very small
+	and its only diag.util
+*/
+static void fill_voltaire_chassis_record(ibnd_node_t *node)
+{
+	int p = 0;
+	ibnd_port_t *port;
+	ibnd_node_t *remnode = 0;
+	ibnd_chassis_record_t *ch = 0;
+
+	if (node->chrecord) /* somehow this node has already been passed */
+		return;
+
+	if (!(node->chrecord = calloc(1, sizeof(ibnd_chassis_record_t))))
+		IBPANIC("out of mem");
+
+	ch = node->chrecord;
+
+	/* node is router only in case of using unique lid */
+	/* (which is lid of chassis router port) */
+	/* in such case node->ports is actually a requested port... */
+	if (is_router(node)) {
+		/* find the remote node */
+		for (p = 1; p <= node->info.numports; p++) {
+			port = node->ports[p];
+			if (port && is_spine(port->remoteport->node))
+				get_router_slot(node, port->remoteport);
+		}
+	} else if (is_spine(node)) {
+		for (p = 1; p <= node->info.numports; p++) {
+			port = node->ports[p];
+			if (!port || !port->remoteport)
+				continue;
+			remnode = port->remoteport->node;
+			if (remnode->info.type != IBND_SWITCH_NODE) {
+				if (!remnode->chrecord)
+					get_router_slot(remnode, port);
+				continue;
+			}
+			if (!ch->chassistype)
+				/* we assume here that remoteport belongs to line */
+				get_sfb_slot(node, port->remoteport);
+
+				/* we could break here, but need to find if more routers connected */
+		}
+
+	} else if (is_line(node)) {
+		for (p = 1; p <= node->info.numports; p++) {
+			port = node->ports[p];
+			if (!port || port->portnum > 12 || !port->remoteport)
+				continue;
+			/* we assume here that remoteport belongs to spine */
+			get_slb_slot(ch, port->remoteport);
+			break;
+		}
+	}
+
+	/* for each port of this node, map external ports */
+	for (p = 1; p <= node->info.numports; p++) {
+		port = node->ports[p];
+		if (!port)
+			continue;
+		voltaire_portmap(port);
+	}
+
+	return;
+}
+
+static int get_line_index(ibnd_node_t *node)
+{
+	int retval = 3 * (node->chrecord->slotnum - 1) + node->chrecord->anafanum;
+
+	if (retval > LINES_MAX_NUM || retval < 1)
+		IBPANIC("Internal error");
+	return retval;
+}
+
+static int get_spine_index(ibnd_node_t *node)
+{
+	int retval;
+
+	if (is_spine_9288(node) || is_spine_2012(node))
+		retval = 3 * (node->chrecord->slotnum - 1) + node->chrecord->anafanum;
+	else
+		retval = node->chrecord->slotnum;
+
+	if (retval > SPINES_MAX_NUM || retval < 1)
+		IBPANIC("Internal error");
+	return retval;
+}
+
+static void insert_line_router(ibnd_node_t *node, ibnd_chassis_list_t *chassislist)
+{
+	int i = get_line_index(node);
+
+	if (chassislist->linenode[i])
+		return;		/* already filled slot */
+
+	chassislist->linenode[i] = node;
+	node->chrecord->chassisnum = chassislist->chassisnum;
+}
+
+static void insert_spine(ibnd_node_t *node, ibnd_chassis_list_t *chassislist)
+{
+	int i = get_spine_index(node);
+
+	if (chassislist->spinenode[i])
+		return;		/* already filled slot */
+
+	chassislist->spinenode[i] = node;
+	node->chrecord->chassisnum = chassislist->chassisnum;
+}
+
+static void pass_on_lines_catch_spines(ibnd_chassis_list_t *chassislist)
+{
+	ibnd_node_t *node, *remnode;
+	ibnd_port_t *port;
+	int i, p;
+
+	for (i = 1; i <= LINES_MAX_NUM; i++) {
+		node = chassislist->linenode[i];
+
+		if (!(node && is_line(node)))
+			continue;	/* empty slot or router */
+
+		for (p = 1; p <= node->info.numports; p++) {
+			port = node->ports[p];
+			if (!port || port->portnum > 12 || !port->remoteport)
+				continue;
+
+			remnode = port->remoteport->node;
+
+			if (!remnode->chrecord)
+				continue;	/* some error - spine not initialized ? FIXME */
+			insert_spine(remnode, chassislist);
+		}
+	}
+}
+
+static void pass_on_spines_catch_lines(ibnd_chassis_list_t *chassislist)
+{
+	ibnd_node_t *node, *remnode;
+	ibnd_port_t *port;
+	int i, p;
+
+	for (i = 1; i <= SPINES_MAX_NUM; i++) {
+		node = chassislist->spinenode[i];
+		if (!node)
+			continue;	/* empty slot */
+		for (p = 1; p <= node->info.numports; p++) {
+			port = node->ports[p];
+			if (!port || !port->remoteport)
+				continue;
+			remnode = port->remoteport->node;
+
+			if (!remnode->chrecord)
+				continue;	/* some error - line/router not initialized ? FIXME */
+			insert_line_router(remnode, chassislist);
+		}
+	}
+}
+
+/*
+	Stupid interpolation algorithm...
+	But nothing to do - have to be compliant with VoltaireSM/NMS
+*/
+static void pass_on_spines_interpolate_chguid(ibnd_chassis_list_t *chassislist)
+{
+	ibnd_node_t *node;
+	int i;
+
+	for (i = 1; i <= SPINES_MAX_NUM; i++) {
+		node = chassislist->spinenode[i];
+		if (!node)
+			continue;	/* skip the empty slots */
+
+		/* take first guid minus one to be consistent with SM */
+		chassislist->chassisguid = node->info.nodeguid - 1;
+		break;
+	}
+}
+
+/*
+	This function fills chassislist structure with all nodes
+	in that chassis
+	chassislist structure = structure of one standalone chassis
+*/
+static void build_chassis(ibnd_node_t *node, ibnd_chassis_list_t *chassislist)
+{
+	int p = 0;
+	ibnd_node_t *remnode = 0;
+	ibnd_port_t *port = 0;
+
+	/* we get here with node = chassis_spine */
+	chassislist->chassistype = node->chrecord->chassistype;
+	insert_spine(node, chassislist);
+
+	/* loop: pass on all ports of node */
+	for (p = 1; p <= node->info.numports; p++ ) {
+		port = node->ports[p];
+		if (!port || !port->remoteport)
+			continue;
+		remnode = port->remoteport->node;
+
+		if (!remnode->chrecord)
+			continue; /* some error - line or router not initialized ? FIXME */
+
+		insert_line_router(remnode, chassislist);
+	}
+
+	pass_on_lines_catch_spines(chassislist);
+	/* this pass needed for to catch routers, since routers connected only */
+	/* to spines in slot 1 or 4 and we could miss them first time */
+	pass_on_spines_catch_lines(chassislist);
+
+	/* additional 2 passes needed for to overcome a problem of pure "in-chassis" */
+	/* connectivity - extra pass to ensure that all related chips/modules */
+	/* inserted into the chassislist */
+	pass_on_lines_catch_spines(chassislist);
+	pass_on_spines_catch_lines(chassislist);
+	pass_on_spines_interpolate_chguid(chassislist);
+}
+
+/*========================================================*/
+/*                INTERNAL TO EXTERNAL PORT MAPPING       */
+/*========================================================*/
+
+/*
+Description : On ISR9288/9096 external ports indexing
+              is not matching the internal ( anafa ) port
+              indexes. Use this MAP to translate the data you get from
+              the OpenIB diagnostics (smpquery, ibroute, ibtracert, etc.)
+
+
+Module : sLB-24
+                anafa 1             anafa 2
+ext port | 13 14 15 16 17 18 | 19 20 21 22 23 24
+int port | 22 23 24 18 17 16 | 22 23 24 18 17 16
+ext port | 1  2  3  4  5  6  | 7  8  9  10 11 12
+int port | 19 20 21 15 14 13 | 19 20 21 15 14 13
+------------------------------------------------
+
+Module : sLB-8
+                anafa 1             anafa 2
+ext port | 13 14 15 16 17 18 | 19 20 21 22 23 24
+int port | 24 23 22 18 17 16 | 24 23 22 18 17 16
+ext port | 1  2  3  4  5  6  | 7  8  9  10 11 12
+int port | 21 20 19 15 14 13 | 21 20 19 15 14 13
+
+----------->
+                anafa 1             anafa 2
+ext port | -  -  5  -  -  6  | -  -  7  -  -  8
+int port | 24 23 22 18 17 16 | 24 23 22 18 17 16
+ext port | -  -  1  -  -  2  | -  -  3  -  -  4
+int port | 21 20 19 15 14 13 | 21 20 19 15 14 13
+------------------------------------------------
+
+Module : sLB-2024
+
+ext port | 13 14 15 16 17 18 19 20 21 22 23 24
+A1 int port| 13 14 15 16 17 18 19 20 21 22 23 24
+ext port | 1 2 3 4 5 6 7 8 9 10 11 12
+A2 int port| 13 14 15 16 17 18 19 20 21 22 23 24
+---------------------------------------------------
+
+*/
+
+int int2ext_map_slb24[2][25] = {
+					{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 5, 4, 18, 17, 16, 1, 2, 3, 13, 14, 15 },
+					{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 11, 10, 24, 23, 22, 7, 8, 9, 19, 20, 21 }
+				};
+int int2ext_map_slb8[2][25] = {
+					{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 6, 6, 6, 1, 1, 1, 5, 5, 5 },
+					{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 8, 8, 8, 3, 3, 3, 7, 7, 7 }
+				};
+int int2ext_map_slb2024[2][25] = {
+					{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 },
+					{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }
+				};
+/*	reference			{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }; */
+
+/* map internal ports to external ports if appropriate */
+static void
+voltaire_portmap(ibnd_port_t *port)
+{
+	ibnd_chassis_record_t *ch = port->node->chrecord;
+	int portnum = port->portnum;
+	int chipnum = 0;
+	ibnd_node_t *node = port->node;
+
+	if (!ch || !is_line(node) || (portnum < 13 || portnum > 24)) {
+		port->ext_portnum = 0;
+		return;
+	}
+
+	if (ch->anafanum < 1 || ch->anafanum > 2) {
+		port->ext_portnum = 0;
+		return;
+	}
+
+	chipnum = ch->anafanum - 1;
+
+	if (is_line_24(node))
+		port->ext_portnum = int2ext_map_slb24[chipnum][portnum];
+	else if (is_line_2024(node))
+		port->ext_portnum = int2ext_map_slb2024[chipnum][portnum];
+	else
+		port->ext_portnum = int2ext_map_slb8[chipnum][portnum];
+}
+
+static void add_chassislist(ibnd_fabric_t *fabric)
+{
+	if (!(fabric->current_chassis = calloc(1, sizeof(ibnd_chassis_list_t))))
+		IBPANIC("out of mem");
+
+	if (fabric->first_chassis == NULL) {
+		fabric->first_chassis = fabric->current_chassis;
+	} else {
+		fabric->current_chassis->next = NULL;
+	}
+}
+
+static void
+add_node_to_chassis(ibnd_chassis_list_t *chassis, ibnd_node_t *node)
+{
+	node->chassis_next = chassis->nodes;
+	if (chassis->nodes)
+		chassis->nodes->chassis_next = node;
+	else
+		chassis->nodes = node;
+}
+
+/*
+	Main grouping function
+	Algorithm:
+	1. pass on every Voltaire node
+	2. catch spine chip for every Voltaire node
+		2.1 build/interpolate chassis around this chip
+		2.2 go to 1.
+	3. pass on non Voltaire nodes (SystemImageGUID based grouping)
+	4. now group non Voltaire nodes by SystemImageGUID
+*/
+ibnd_chassis_list_t *group_nodes(ibnd_fabric_t *fabric)
+{
+	ibnd_node_t *node;
+	int dist;
+	int chassisnum = 0;
+	ibnd_chassis_list_t *chassis;
+
+	fabric->first_chassis = NULL;
+	fabric->current_chassis = NULL;
+
+	/* first pass on switches and build for every Voltaire node */
+	/* an appropriate chassis record (slotnum and position) */
+	/* according to internal connectivity */
+	/* not very efficient but clear code so... */
+	for (dist = 0; dist <= fabric->maxhops_discovered; dist++) {
+		for (node = fabric->nodesdist[dist]; node; node = node->dnext) {
+			if (node->info.vendid == VTR_VENDOR_ID)
+				fill_voltaire_chassis_record(node);
+		}
+	}
+
+	/* separate every Voltaire chassis from each other and build linked list of them */
+	/* algorithm: catch spine and find all surrounding nodes */
+	for (dist = 0; dist <= fabric->maxhops_discovered; dist++) {
+		for (node = fabric->nodesdist[dist]; node; node = node->dnext) {
+			if (node->info.vendid != VTR_VENDOR_ID)
+				continue;
+			if (!node->chrecord || node->chrecord->chassisnum || !is_spine(node))
+				continue;
+			add_chassislist(fabric);
+			fabric->current_chassis->chassisnum = ++chassisnum;
+			build_chassis(node, fabric->current_chassis);
+		}
+	}
+
+	/* now make pass on nodes for chassis which are not Voltaire */
+	/* grouped by common SystemImageGUID */
+	for (dist = 0; dist <= fabric->maxhops_discovered; dist++) {
+		for (node = fabric->nodesdist[dist]; node; node = node->dnext) {
+			if (node->info.vendid == VTR_VENDOR_ID)
+				continue;
+			if (node->info.sysimgguid) {
+				chassis = find_chassisguid(node);
+				if (chassis)
+					chassis->nodecount++;
+				else {
+					/* Possible new chassis */
+					add_chassislist(fabric);
+					fabric->current_chassis->chassisguid = get_chassisguid(node);
+					fabric->current_chassis->nodecount = 1;
+				}
+			}
+		}
+	}
+
+	/* now, make another pass to see which nodes are part of chassis */
+	/* (defined as chassis->nodecount > 1) */
+	for (dist = 0; dist <= MAXHOPS; ) {
+		for (node = fabric->nodesdist[dist]; node; node = node->dnext) {
+			if (node->info.vendid == VTR_VENDOR_ID)
+				continue;
+			if (node->info.sysimgguid) {
+				chassis = find_chassisguid(node);
+				if (chassis && chassis->nodecount > 1) {
+					if (!chassis->chassisnum)
+						chassis->chassisnum = ++chassisnum;
+					if (!node->chrecord) {
+						if (!(node->chrecord =
+						calloc(1,
+						sizeof(ibnd_chassis_record_t))))
+							IBPANIC("out of mem");
+						node->chrecord->chassisnum = chassis->chassisnum;
+						add_node_to_chassis(chassis, node);
+					}
+				}
+			}
+		}
+		if (dist == fabric->maxhops_discovered)
+			dist = MAXHOPS;	/* skip to CAs */
+		else
+			dist++;
+	}
+
+	return (fabric->first_chassis);
+}
diff --git a/libibnetdisc/src/chassis.h b/libibnetdisc/src/chassis.h
new file mode 100644
index 0000000..ea271d0
--- /dev/null
+++ b/libibnetdisc/src/chassis.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2004-2007 Voltaire Inc.  All rights reserved.
+ * Copyright (c) 2007 Xsigo Systems Inc.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#ifndef _CHASSIS_H_
+#define _CHASSIS_H_
+
+#include <infiniband/ibnetdisc.h>
+
+/*========================================================*/
+/*                CHASSIS RECOGNITION SPECIFIC DATA       */
+/*========================================================*/
+
+/* Device IDs */
+#define VTR_DEVID_IB_FC_ROUTER		0x5a00
+#define VTR_DEVID_IB_IP_ROUTER		0x5a01
+#define VTR_DEVID_ISR9600_SPINE		0x5a02
+#define VTR_DEVID_ISR9600_LEAF		0x5a03
+#define VTR_DEVID_HCA1			0x5a04
+#define VTR_DEVID_HCA2			0x5a44
+#define VTR_DEVID_HCA3			0x6278
+#define VTR_DEVID_SW_6IB4		0x5a05
+#define VTR_DEVID_ISR9024		0x5a06
+#define VTR_DEVID_ISR9288		0x5a07
+#define VTR_DEVID_SLB24			0x5a09
+#define VTR_DEVID_SFB12			0x5a08
+#define VTR_DEVID_SFB4			0x5a0b
+#define VTR_DEVID_ISR9024_12		0x5a0c
+#define VTR_DEVID_SLB8			0x5a0d
+#define VTR_DEVID_RLX_SWITCH_BLADE	0x5a20
+#define VTR_DEVID_ISR9024_DDR		0x5a31
+#define VTR_DEVID_SFB12_DDR		0x5a32
+#define VTR_DEVID_SFB4_DDR		0x5a33
+#define VTR_DEVID_SLB24_DDR		0x5a34
+#define VTR_DEVID_SFB2012		0x5a37
+#define VTR_DEVID_SLB2024		0x5a38
+#define VTR_DEVID_ISR2012		0x5a39
+#define VTR_DEVID_SFB2004		0x5a40
+#define VTR_DEVID_ISR2004		0x5a41
+
+/* Vendor IDs (for chassis based systems) */
+#define VTR_VENDOR_ID			0x8f1	/* Voltaire */
+#define TS_VENDOR_ID			0x5ad	/* Cisco */
+#define SS_VENDOR_ID			0x66a	/* InfiniCon */
+#define XS_VENDOR_ID			0x1397	/* Xsigo */
+
+enum ibnd_chassis_type { UNRESOLVED_CT, ISR9288_CT, ISR9096_CT, ISR2012_CT, ISR2004_CT };
+enum ibnd_chassis_slot_type { UNRESOLVED_CS, LINE_CS, SPINE_CS, SRBD_CS };
+
+ibnd_chassis_list_t *group_nodes(ibnd_fabric_t *fabric);
+
+#endif	/* _CHASSIS_H_ */
diff --git a/libibnetdisc/src/ibnetdisc.c b/libibnetdisc/src/ibnetdisc.c
new file mode 100644
index 0000000..3f4901a
--- /dev/null
+++ b/libibnetdisc/src/ibnetdisc.c
@@ -0,0 +1,863 @@
+/*
+ * Copyright (c) 2004-2007 Voltaire Inc.  All rights reserved.
+ * Copyright (c) 2007 Xsigo Systems Inc.  All rights reserved.
+ * Copyright (c) 2008 Lawrence Livermore National Laboratory
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#  include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <time.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <infiniband/common.h>
+#include <infiniband/umad.h>
+#include <infiniband/mad.h>
+
+#include <infiniband/ibnetdisc.h>
+#include <complib/cl_nodenamemap.h>
+
+#include "chassis.h"
+
+static int timeout_ms = 2000;
+static int show_progress = 0;
+
+static char *linkwidth_str[] = {
+	"??",
+	"1x",
+	"4x",
+	"??",
+	"8x",
+	"??",
+	"??",
+	"??",
+	"12x"
+};
+
+static char *linkspeed_str[] = {
+	"???",
+	"SDR",
+	"DDR",
+	"???",
+	"QDR"
+};
+
+static char *linkstate_str[] = {
+	"No State",
+	"Down",
+	"Init",
+	"Armed",
+	"Active"
+};
+
+static char *physstate_str[] = {
+	"No State",
+	"Sleep",
+	"Polling",
+	"Disabled",
+	"PortConfigTraining",
+	"LinkUp",
+	"LinkErrorRecovery",
+	"Phy Test"
+};
+
+char *
+ibnd_linkwidth_str(int link_width)
+{
+	if (link_width > 8)
+		return linkwidth_str[0];
+	else
+		return linkwidth_str[link_width];
+}
+
+char *
+ibnd_linkspeed_str(int link_speed)
+{
+	if (link_speed > 4)
+		return linkspeed_str[0];
+	else
+		return linkspeed_str[link_speed];
+}
+char *
+ibnd_linkstate_str(int link_state)
+{
+	if (link_state > 4)
+		return linkstate_str[0];
+	else
+		return linkstate_str[link_state];
+}
+
+char *
+ibnd_physstate_str(int phys_state)
+{
+	if (phys_state > 7)
+		return physstate_str[0];
+	else
+		return physstate_str[phys_state];
+}
+
+void
+decode_port_info(void * rcv_buf, ibnd_port_info_t *pi)
+{
+	mad_decode_field(rcv_buf, IB_PORT_LID_F, &pi->lid);
+	mad_decode_field(rcv_buf, IB_PORT_SMLID_F, &pi->smlid);
+
+	mad_decode_field(rcv_buf, IB_PORT_LINK_SPEED_SUPPORTED_F, &pi->link_speed_supported);
+	mad_decode_field(rcv_buf, IB_PORT_LINK_SPEED_ENABLED_F, &pi->link_speed_enabled);
+	mad_decode_field(rcv_buf, IB_PORT_LINK_SPEED_ACTIVE_F, &pi->link_speed_active);
+
+	mad_decode_field(rcv_buf, IB_PORT_LOCAL_PORT_F, &pi->local_port);
+	mad_decode_field(rcv_buf, IB_PORT_LINK_WIDTH_SUPPORTED_F, &pi->link_width_supported);
+	mad_decode_field(rcv_buf, IB_PORT_LINK_WIDTH_ENABLED_F, &pi->link_width_enabled);
+
+	mad_decode_field(rcv_buf, IB_PORT_LINK_WIDTH_ACTIVE_F, &pi->link_width_active);
+
+	mad_decode_field(rcv_buf, IB_PORT_DIAG_F, &pi->diag_code);
+	mad_decode_field(rcv_buf, IB_PORT_MKEY_LEASE_F, &pi->mkey_lease);
+	mad_decode_field(rcv_buf, IB_PORT_CAPMASK_F, &pi->capability_mask);
+	mad_decode_field(rcv_buf, IB_PORT_MKEY_F, &pi->mkey);
+	mad_decode_field(rcv_buf, IB_PORT_GID_PREFIX_F, &pi->gid_prefix);
+
+	mad_decode_field(rcv_buf, IB_PORT_STATE_F, &pi->link_state);
+	mad_decode_field(rcv_buf, IB_PORT_PHYS_STATE_F, &pi->phys_state);
+
+	mad_decode_field(rcv_buf, IB_PORT_LINK_DOWN_DEF_F, &pi->link_down_def_state);
+	mad_decode_field(rcv_buf, IB_PORT_MKEY_PROT_BITS_F, &pi->mkey_prot_bits);
+
+	mad_decode_field(rcv_buf, IB_PORT_LMC_F, &pi->lmc);
+	mad_decode_field(rcv_buf, IB_PORT_NEIGHBOR_MTU_F, &pi->neighbor_mtu);
+	mad_decode_field(rcv_buf, IB_PORT_SMSL_F, &pi->smsl);
+	mad_decode_field(rcv_buf, IB_PORT_INIT_TYPE_F, &pi->init_type);
+
+	mad_decode_field(rcv_buf, IB_PORT_VL_CAP_F, &pi->vl_capability);
+	mad_decode_field(rcv_buf, IB_PORT_VL_HIGH_LIMIT_F, &pi->vl_high_limit);
+	mad_decode_field(rcv_buf, IB_PORT_VL_ARBITRATION_HIGH_CAP_F, &pi->vl_arb_high_cap);
+	mad_decode_field(rcv_buf, IB_PORT_VL_ARBITRATION_LOW_CAP_F, &pi->vl_arb_low_cap);
+
+	mad_decode_field(rcv_buf, IB_PORT_INIT_TYPE_REPLY_F, &pi->init_reply);
+	mad_decode_field(rcv_buf, IB_PORT_MTU_CAP_F, &pi->mtu_cap);
+	mad_decode_field(rcv_buf, IB_PORT_VL_STALL_COUNT_F, &pi->vl_stall_count);
+	mad_decode_field(rcv_buf, IB_PORT_HOQ_LIFE_F, &pi->hoq_lifetime);
+	mad_decode_field(rcv_buf, IB_PORT_OPER_VLS_F, &pi->oper_vls);
+	mad_decode_field(rcv_buf, IB_PORT_PART_EN_INB_F, &pi->partition_enforce_in);
+	mad_decode_field(rcv_buf, IB_PORT_PART_EN_OUTB_F, &pi->partition_enforce_out);
+	mad_decode_field(rcv_buf, IB_PORT_FILTER_RAW_INB_F, &pi->filter_raw_in);
+	mad_decode_field(rcv_buf, IB_PORT_FILTER_RAW_OUTB_F, &pi->filter_raw_out);
+	mad_decode_field(rcv_buf, IB_PORT_MKEY_VIOL_F, &pi->mkey_violations);
+	mad_decode_field(rcv_buf, IB_PORT_PKEY_VIOL_F, &pi->pkey_violations);
+	mad_decode_field(rcv_buf, IB_PORT_QKEY_VIOL_F, &pi->qkey_violations);
+
+	mad_decode_field(rcv_buf, IB_PORT_GUID_CAP_F, &pi->guid_capabilities);
+
+	mad_decode_field(rcv_buf, IB_PORT_CLIENT_REREG_F, &pi->client_rereg);
+	mad_decode_field(rcv_buf, IB_PORT_SUBN_TIMEOUT_F, &pi->subnet_timeout);
+	mad_decode_field(rcv_buf, IB_PORT_RESP_TIME_VAL_F, &pi->response_time_val);
+	mad_decode_field(rcv_buf, IB_PORT_LOCAL_PHYS_ERR_F, &pi->local_phys_error);
+	mad_decode_field(rcv_buf, IB_PORT_OVERRUN_ERR_F, &pi->overrun_error);
+	mad_decode_field(rcv_buf, IB_PORT_MAX_CREDIT_HINT_F, &pi->max_credit_hint);
+	mad_decode_field(rcv_buf, IB_PORT_LINK_ROUND_TRIP_F, &pi->link_round_trip);
+}
+
+static int
+get_port_info(ibnd_fabric_t *fabric, ibnd_port_t *port, int portnum, ib_portid_t *portid)
+{
+	char portinfo[64];
+	void *pi = portinfo;
+
+	port->portnum = portnum;
+
+	if (!smp_query_via(pi, portid, IB_ATTR_PORT_INFO, portnum, timeout_ms,
+			fabric->ibmad_port))
+		return -1;
+
+	decode_port_info(pi, &port->info);
+
+	IBND_DEBUG("portid %s portnum %d: lid %d state %d physstate %d %s %s\n",
+		portid2str(portid), portnum, port->info.lid, port->info.link_state,
+		port->info.phys_state, ibnd_linkwidth_str(port->info.link_width_active),
+		ibnd_linkspeed_str(port->info.link_speed_active));
+	return 1;
+}
+
+static void
+decode_node_info(void * rcv_buf, ibnd_node_info_t *ni)
+{
+	mad_decode_field(rcv_buf, IB_NODE_BASE_VERS_F, &ni->base_ver);
+	mad_decode_field(rcv_buf, IB_NODE_CLASS_VERS_F, &ni->class_ver);
+	mad_decode_field(rcv_buf, IB_NODE_TYPE_F, &ni->type);
+	mad_decode_field(rcv_buf, IB_NODE_NPORTS_F, &ni->numports);
+	mad_decode_field(rcv_buf, IB_NODE_SYSTEM_GUID_F, &ni->sysimgguid);
+	mad_decode_field(rcv_buf, IB_NODE_GUID_F, &ni->nodeguid);
+	mad_decode_field(rcv_buf, IB_NODE_PORT_GUID_F, &ni->nodeportguid);
+	mad_decode_field(rcv_buf, IB_NODE_PARTITION_CAP_F, &ni->partition_cap);
+	mad_decode_field(rcv_buf, IB_NODE_DEVID_F, &ni->devid);
+	mad_decode_field(rcv_buf, IB_NODE_REVISION_F, &ni->revision);
+	mad_decode_field(rcv_buf, IB_NODE_LOCAL_PORT_F, &ni->localport);
+	mad_decode_field(rcv_buf, IB_NODE_VENDORID_F, &ni->vendid);
+}
+
+/*
+ * Returns -1 if error.
+ */
+static int
+query_node_info(ibnd_fabric_t *fabric, ibnd_node_t *node, ib_portid_t *portid)
+{
+	char nodeinfo[64];
+	void *ni = nodeinfo;
+	if (!smp_query_via(ni, portid, IB_ATTR_NODE_INFO, 0, timeout_ms,
+			fabric->ibmad_port))
+		return -1;
+	decode_node_info(ni, &(node->info));
+	return (0);
+}
+
+/*
+ * Returns 0 if non switch node is found, 1 if switch is found, -1 if error.
+ */
+static int
+query_node(ibnd_fabric_t *fabric, ibnd_node_t *node, ibnd_port_t *port, ib_portid_t *portid)
+{
+	char portinfo[64];
+	void *pi = portinfo;
+	char switchinfo[64];
+	void *si = switchinfo;
+	void *nd = node->nodedesc;
+
+	if (query_node_info(fabric, node, portid))
+		return -1;
+
+	port->portnum = node->info.localport;
+	port->guid = node->info.nodeportguid;
+
+	if (!smp_query_via(nd, portid, IB_ATTR_NODE_DESC, 0, timeout_ms,
+			fabric->ibmad_port))
+		return -1;
+
+	if (!smp_query_via(pi, portid, IB_ATTR_PORT_INFO, 0, timeout_ms,
+			fabric->ibmad_port))
+		return -1;
+	decode_port_info(pi, &port->info);
+
+	if (node->info.type != IBND_SWITCH_NODE)
+		return 0;
+
+	node->smalid = port->info.lid;
+	node->smalmc = port->info.lmc;
+
+	/* after we have the sma information find out the real PortInfo for this port */
+	if (!smp_query_via(pi, portid, IB_ATTR_PORT_INFO, node->info.localport, timeout_ms,
+			fabric->ibmad_port))
+		return -1;
+	decode_port_info(pi, &port->info);
+
+        if (!smp_query_via(si, portid, IB_ATTR_SWITCH_INFO, 0, timeout_ms,
+			fabric->ibmad_port))
+                node->sw_info.smaenhsp0 = 0;	/* assume base SP0 */
+	else
+		mad_decode_field(si, IB_SW_ENHANCED_PORT0_F, &node->sw_info.smaenhsp0);
+
+	IBND_DEBUG("portid %s: got switch node %" PRIx64 " '%s'\n",
+	      portid2str(portid), node->info.nodeguid, node->nodedesc);
+	return 1;
+}
+
+static int
+add_port_to_dpath(ib_dr_path_t *path, int nextport)
+{
+	if (path->cnt+2 >= sizeof(path->p))
+		return -1;
+	++path->cnt;
+	path->p[path->cnt] = nextport;
+	return path->cnt;
+}
+
+static int
+extend_dpath(ibnd_fabric_t *fabric, ib_dr_path_t *path, int nextport)
+{
+	int rc = add_port_to_dpath(path, nextport);
+	if ((rc != -1) && (path->cnt > fabric->maxhops_discovered))
+		fabric->maxhops_discovered = path->cnt;
+	return (rc);
+}
+
+static void
+dump_endnode(ib_portid_t *path, char *prompt, ibnd_node_t *node, ibnd_port_t *port)
+{
+	if (!show_progress)
+		return;
+
+	printf("%s -> %s %s {%016" PRIx64 "} portnum %d lid %d-%d\"%s\"\n",
+		portid2str(path), prompt,
+		ibnd_node_type_str(node),
+		node->info.nodeguid, node->info.type == IBND_SWITCH_NODE ? 0 : port->portnum,
+		port->info.lid, port->info.lid + (1 << port->info.lmc) - 1,
+		node->nodedesc);
+}
+
+static ibnd_node_t *
+find_existing_node(ibnd_fabric_t *fabric, ibnd_node_t *new)
+{
+	int hash = HASHGUID(new->info.nodeguid) % HTSZ;
+	ibnd_node_t *node;
+
+	for (node = fabric->nodestbl[hash]; node; node = node->htnext)
+		if (node->info.nodeguid == new->info.nodeguid)
+			return node;
+
+	return NULL;
+}
+
+ibnd_node_t *
+ibnd_find_node_guid(ibnd_fabric_t *fabric, uint64_t guid)
+{
+	int hash = HASHGUID(guid) % HTSZ;
+	ibnd_node_t *node;
+
+	for (node = fabric->nodestbl[hash]; node; node = node->htnext)
+		if (node->info.nodeguid == guid)
+			return node;
+
+	return NULL;
+}
+
+ibnd_node_t *
+ibnd_update_node(ibnd_node_t *node)
+{
+	char portinfo[64];
+	void *pi = portinfo;
+	ibnd_port_info_t port0_info;
+	char switchinfo[64];
+	void *si = switchinfo;
+	void *nd = node->nodedesc;
+	int p = 0;
+
+	if (query_node_info(node->fabric, node, &(node->path_portid)))
+		return (NULL);
+
+	if (!smp_query_via(nd, &(node->path_portid), IB_ATTR_NODE_DESC, 0, timeout_ms,
+			node->fabric->ibmad_port))
+		return (NULL);
+
+	/* update all the port info's */
+	for (p = 1; p >= node->info.numports; p++) {
+		get_port_info(node->fabric, node->ports[p], p, &(node->path_portid));
+	}
+
+	if (node->info.type != IBND_SWITCH_NODE)
+		goto done;
+
+	if (!smp_query_via(pi, &(node->path_portid), IB_ATTR_PORT_INFO, 0, timeout_ms,
+			node->fabric->ibmad_port))
+		return (NULL);
+	decode_port_info(pi, &port0_info);
+
+	node->smalid = port0_info.lid;
+	node->smalmc = port0_info.lmc;
+
+        if (!smp_query_via(si, &(node->path_portid), IB_ATTR_SWITCH_INFO, 0, timeout_ms,
+			node->fabric->ibmad_port))
+                node->sw_info.smaenhsp0 = 0;	/* assume base SP0 */
+	else
+		mad_decode_field(si, IB_SW_ENHANCED_PORT0_F, &node->sw_info.smaenhsp0);
+
+done:
+	return (node);
+}
+
+ibnd_node_t *
+ibnd_find_node_dr(ibnd_fabric_t *fabric, char *dr_str)
+{
+	int i = 0;
+	ibnd_node_t *rc = fabric->from_node;
+	ib_dr_path_t path;
+
+	if (str2drpath(&path, dr_str, 0, 0) == -1) {
+		return (NULL);
+	}
+
+	for (i = 0; i <= path.cnt; i++) {
+		ibnd_port_t *remote_port = NULL;
+		if (path.p[i] == 0)
+			continue;
+		if (!rc->ports)
+			return (NULL);
+
+		remote_port = rc->ports[path.p[i]]->remoteport;
+		if (!remote_port)
+			return (NULL);
+
+		rc = remote_port->node;
+	}
+
+	return (rc);
+}
+
+void
+add_to_nodeguid_hash(ibnd_node_t *node, ibnd_node_t *hash[])
+{
+	int hash_idx = HASHGUID(node->info.nodeguid) % HTSZ;
+
+	node->htnext = hash[hash_idx];
+	hash[hash_idx] = node;
+}
+
+void
+add_to_portguid_hash(ibnd_port_t *port, ibnd_port_t *hash[])
+{
+	int hash_idx = HASHGUID(port->guid) % HTSZ;
+
+	port->htnext = hash[hash_idx];
+	hash[hash_idx] = port;
+}
+
+ibnd_port_t *
+find_existing_port_fabric(ibnd_fabric_t *fabric, uint64_t guid)
+{
+	int hash = HASHGUID(guid) % HTSZ;
+	ibnd_port_t *port;
+
+	for (port = fabric->portstbl[hash]; port; port = port->htnext)
+		if (port->guid == guid)
+			return port;
+
+	return NULL;
+}
+
+void
+add_to_type_list(ibnd_node_t *node, ibnd_fabric_t *fabric)
+{
+	switch (node->info.type) {
+		case IBND_CA_NODE:
+			node->type_next = fabric->ch_adapters;
+			fabric->ch_adapters = node;
+			break;
+		case IBND_SWITCH_NODE:
+			node->type_next = fabric->switches;
+			fabric->switches = node;
+			break;
+		case IBND_ROUTER_NODE:
+			node->type_next = fabric->routers;
+			fabric->routers = node;
+			break;
+	}
+}
+
+void
+add_to_nodedist(ibnd_node_t *node, ibnd_fabric_t *fabric)
+{
+	int dist = node->dist;
+	if (node->info.type != IBND_SWITCH_NODE)
+			dist = MAXHOPS; 	/* special Ca list */
+
+	node->dnext = fabric->nodesdist[dist];
+	fabric->nodesdist[dist] = node;
+}
+
+
+static ibnd_node_t *
+create_node(ibnd_fabric_t *fabric, ibnd_node_t *temp, ib_portid_t *path, int dist)
+{
+	ibnd_node_t *node;
+
+	node = malloc(sizeof(*node));
+	if (!node) {
+		IBPANIC("OOM: node creation failed\n");
+		return NULL;
+	}
+
+	memcpy(node, temp, sizeof(*node));
+	node->dist = dist;
+	node->path_portid = *path;
+	node->fabric = fabric;
+
+	add_to_nodeguid_hash(node, fabric->nodestbl);
+
+	/* add this to the all nodes list */
+	node->next = fabric->nodes;
+	fabric->nodes = node;
+
+	add_to_type_list(node, fabric);
+	add_to_nodedist(node, fabric);
+
+	return node;
+}
+
+static ibnd_port_t *
+find_existing_port_node(ibnd_node_t *node, ibnd_port_t *port)
+{
+	if (port->portnum > node->info.numports || node->ports == NULL )
+		return (NULL);
+
+	return (node->ports[port->portnum]);
+}
+
+static ibnd_port_t *
+add_port_to_node(ibnd_fabric_t *fabric, ibnd_node_t *node, ibnd_port_t *temp)
+{
+	ibnd_port_t *port;
+
+	port = malloc(sizeof(*port));
+	if (!port)
+		return NULL;
+
+	memcpy(port, temp, sizeof(*port));
+	port->node = node;
+	port->ext_portnum = 0;
+
+	if (node->ports == NULL) {
+		node->ports = calloc(sizeof(*node->ports), node->info.numports + 1);
+		if (!node->ports) {
+			IBND_ERROR("Failed to allocate the ports array\n");
+			return (NULL);
+		}
+	}
+
+	node->ports[temp->portnum] = port;
+
+	add_to_portguid_hash(port, fabric->portstbl);
+	return port;
+}
+
+void
+link_ports(ibnd_node_t *node, ibnd_port_t *port, ibnd_node_t *remotenode, ibnd_port_t *remoteport)
+{
+	IBND_DEBUG("linking: 0x%" PRIx64 " %p->%p:%u and 0x%" PRIx64 " %p->%p:%u\n",
+		node->info.nodeguid, node, port, port->portnum,
+		remotenode->info.nodeguid, remotenode, remoteport, remoteport->portnum);
+	if (port->remoteport)
+		port->remoteport->remoteport = NULL;
+	if (remoteport->remoteport)
+		remoteport->remoteport->remoteport = NULL;
+	port->remoteport = remoteport;
+	remoteport->remoteport = port;
+}
+
+static int
+get_remote_node(ibnd_fabric_t *fabric, ibnd_node_t *node, ibnd_port_t *port, ib_portid_t *path,
+		int portnum, int dist)
+{
+	ibnd_node_t node_buf;
+	ibnd_port_t port_buf;
+	ibnd_node_t *remotenode, *oldnode;
+	ibnd_port_t *remoteport, *oldport;
+
+	memset(&node_buf, 0, sizeof(node_buf));
+	memset(&port_buf, 0, sizeof(port_buf));
+
+	IBND_DEBUG("handle node %p port %p:%d dist %d\n", node, port, portnum, dist);
+	if (port->info.phys_state != 5)	/* LinkUp */
+		return -1;
+
+	if (extend_dpath(fabric, &path->drpath, portnum) < 0)
+		return -1;
+
+	if (query_node(fabric, &node_buf, &port_buf, path) < 0) {
+		IBWARN("NodeInfo on %s failed, skipping port",
+			portid2str(path));
+		path->drpath.cnt--;	/* restore path */
+		return -1;
+	}
+
+	oldnode = find_existing_node(fabric, &node_buf);
+	if (oldnode)
+		remotenode = oldnode;
+	else if (!(remotenode = create_node(fabric, &node_buf, path, dist + 1)))
+		IBPANIC("no memory");
+
+	oldport = find_existing_port_node(remotenode, &port_buf);
+	if (oldport) {
+		remoteport = oldport;
+	} else if (!(remoteport = add_port_to_node(fabric, remotenode, &port_buf)))
+		IBPANIC("no memory");
+
+	dump_endnode(path, oldnode ? "known remote" : "new remote",
+			remotenode, remoteport);
+
+	link_ports(node, port, remotenode, remoteport);
+
+	path->drpath.cnt--;	/* restore path */
+	return 0;
+}
+
+static void *
+ibnd_init_port(char *dev_name, int dev_port)
+{
+	int mgmt_classes[2] = {IB_SMI_CLASS, IB_SMI_DIRECT_CLASS};
+
+	/* Crank up the mad lib */
+	return (mad_rpc_open_port(dev_name, dev_port, mgmt_classes, 2));
+}
+
+ibnd_fabric_t *
+ibnd_discover_fabric(char *dev_name, int dev_port, int timeout_ms,
+			ib_portid_t *from, int hops)
+{
+	ibnd_fabric_t *fabric = NULL;
+	ib_portid_t my_portid = {0};
+	ibnd_node_t node_buf;
+	ibnd_port_t port_buf;
+	ibnd_node_t *node;
+	ibnd_port_t *port;
+	int i;
+	int dist = 0;
+	ib_portid_t *path;
+	int max_hops = MAXHOPS-1; /* default find everything */
+
+	/* if not everything how much? */
+	if (hops >= 0) {
+		max_hops = hops;
+	}
+
+	/* If not specified start from "my" port */
+	if (!from) {
+		from = &my_portid;
+	}
+
+	fabric = malloc(sizeof(*fabric));
+
+	if (!fabric) {
+		IBPANIC("OOM: failed to malloc ibnd_fabric_t\n");
+		return (NULL);
+	}
+
+	memset(fabric, 0, sizeof(*fabric));
+
+	fabric->ibmad_port = ibnd_init_port(dev_name, dev_port);
+	if (!fabric->ibmad_port) {
+		IBPANIC("OOM: failed to open \"%s\" port %d\n",
+			dev_name, dev_port);
+		goto error;
+	}
+
+	IBND_DEBUG("from %s\n", portid2str(from));
+
+	memset(&node_buf, 0, sizeof(node_buf));
+	memset(&port_buf, 0, sizeof(port_buf));
+
+	if (query_node(fabric, &node_buf, &port_buf, from) < 0) {
+		IBWARN("can't reach node %s\n", portid2str(from));
+		goto error;
+	}
+
+	node = create_node(fabric, &node_buf, from, 0);
+	if (!node)
+		goto error;
+
+	fabric->from_node = node;
+
+	port = add_port_to_node(fabric, node, &port_buf);
+	if (!port)
+		IBPANIC("out of memory");
+
+	if (node->info.type != IBND_SWITCH_NODE &&
+	    get_remote_node(fabric, node, port, from, node->info.localport, 0) < 0)
+		return fabric;
+
+	for (dist = 0; dist <= max_hops; dist++) {
+
+		for (node = fabric->nodesdist[dist]; node; node = node->dnext) {
+
+			path = &node->path_portid;
+
+			IBND_DEBUG("dist %d node %p\n", dist, node);
+			dump_endnode(path, "processing", node, port);
+
+			for (i = 1; i <= node->info.numports; i++) {
+				if (i == node->info.localport)
+					continue;
+
+				if (get_port_info(fabric, &port_buf, i, path) < 0) {
+					IBWARN("can't reach node %s port %d", portid2str(path), i);
+					continue;
+				}
+
+				port = find_existing_port_node(node, &port_buf);
+				if (port)
+					continue;
+
+				port = add_port_to_node(fabric, node, &port_buf);
+				if (!port)
+					IBPANIC("out of memory");
+
+				/* If switch, set port GUID to node port GUID */
+				if (node->info.type == IBND_SWITCH_NODE)
+					port->guid = node->info.nodeportguid;
+
+				get_remote_node(fabric, node, port, path, i, dist);
+			}
+		}
+	}
+
+	fabric->chassis = group_nodes(fabric);
+
+	return fabric;
+error:
+	free(fabric);
+	return (NULL);
+}
+
+static void
+destroy_node(ibnd_node_t *node)
+{
+	int p = 0;
+
+	for (p = 0; p <= node->info.numports; p++) {
+		free(node->ports[p]);
+	}
+	free(node->ports);
+
+	if (node->chrecord)
+		free(node->chrecord);
+	free(node);
+}
+
+void
+ibnd_destroy_fabric(ibnd_fabric_t *fabric)
+{
+	int dist = 0;
+	ibnd_node_t *node = NULL;
+	ibnd_node_t *next = NULL;
+	ibnd_chassis_list_t *ch, *ch_next;
+
+	for (dist = 0; dist <= MAXHOPS; dist++) {
+		node = fabric->nodesdist[dist];
+		while (node) {
+			next = node->dnext;
+			destroy_node(node);
+			node = next;
+		}
+	}
+	ch = fabric->first_chassis;
+	while (ch) {
+		ch_next = ch->next;
+		free(ch);
+		ch = ch_next;
+	}
+	free(fabric);
+	if (fabric->ibmad_port)
+		mad_rpc_close_port(fabric->ibmad_port);
+}
+
+void
+ibnd_debug(int i)
+{
+	if (i) {
+		ibdebug++;
+		madrpc_show_errors(1);
+		umad_debug(i);
+	} else {
+		ibdebug = 0;
+		madrpc_show_errors(0);
+		umad_debug(0);
+	}
+}
+
+void
+ibnd_show_progress(int i)
+{
+	show_progress = i;
+}
+
+const char*
+ibnd_node_type_str(ibnd_node_t *node)
+{
+	switch(node->info.type) {
+	case IBND_CA_NODE:     return "Ca";
+	case IBND_SWITCH_NODE: return "Switch";
+	case IBND_ROUTER_NODE: return "Router";
+	}
+	return "??";
+}
+
+const char*
+ibnd_node_type_str_short(ibnd_node_t *node)
+{
+	switch(node->info.type) {
+	case IBND_SWITCH_NODE: return "SW";
+	case IBND_CA_NODE:     return "CA";
+	case IBND_ROUTER_NODE: return "RT";
+	}
+	return "??";
+}
+
+
+void
+ibnd_iter_nodes(ibnd_fabric_t *fabric,
+		ibnd_iter_func_t func,
+		void *user_data)
+{
+	ibnd_node_t *cur = NULL;
+
+	for (cur = fabric->nodes; cur; cur = cur->next) {
+		func(cur, user_data);
+	}
+}
+
+
+void
+ibnd_iter_nodes_type(ibnd_fabric_t *fabric,
+		ibnd_iter_func_t func,
+		ibnd_node_type_t node_type,
+		void *user_data)
+{
+	ibnd_node_t *list = NULL;
+	ibnd_node_t *cur = NULL;
+
+	switch (node_type) {
+		case IBND_SWITCH_NODE:
+			list = fabric->switches;
+			break;
+		case IBND_CA_NODE:
+			list = fabric->ch_adapters;
+			break;
+		case IBND_ROUTER_NODE:
+			list = fabric->routers;
+			break;
+		default:
+			IBND_DEBUG("Invalid node_type specified %d\n", node_type);
+			break;
+	}
+
+	for (cur = list; cur; cur = cur->type_next) {
+		func(cur, user_data);
+	}
+}
+
diff --git a/libibnetdisc/src/libibnetdisc.map b/libibnetdisc/src/libibnetdisc.map
new file mode 100644
index 0000000..5e8c315
--- /dev/null
+++ b/libibnetdisc/src/libibnetdisc.map
@@ -0,0 +1,27 @@
+IBNETDISC_1.0 {
+	global:
+		ibnd_debug;
+		ibnd_show_progress;
+		ibnd_discover_fabric;
+		ibnd_cache_fabric;
+		ibnd_read_fabric;
+		ibnd_destroy_fabric;
+		ibnd_find_node_guid;
+		ibnd_update_node;
+		ibnd_find_node_dr;
+		ibnd_linkwidth_str;
+		ibnd_linkspeed_str;
+		ibnd_node_type_str;
+		ibnd_node_type_str_short;
+		ibnd_is_xsigo_guid;
+		ibnd_is_xsigo_tca;
+		ibnd_is_xsigo_hca;
+		ibnd_get_chassis_guid;
+		ibnd_get_chassis_type;
+		ibnd_get_chassis_slot_str;
+		ibnd_linkstate_str;
+		ibnd_physstate_str;
+		ibnd_iter_nodes;
+		ibnd_iter_nodes_type;
+	local: *;
+};
diff --git a/libibnetdisc/test/iblinkinfotest.c b/libibnetdisc/test/iblinkinfotest.c
new file mode 100644
index 0000000..7c52a0b
--- /dev/null
+++ b/libibnetdisc/test/iblinkinfotest.c
@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 2004-2007 Voltaire Inc.  All rights reserved.
+ * Copyright (c) 2007 Xsigo Systems Inc.  All rights reserved.
+ * Copyright (c) 2008 Lawrence Livermore National Lab.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#  include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <time.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <infiniband/complib/cl_nodenamemap.h>
+#include <infiniband/ibnetdisc.h>
+
+char *argv0 = "iblinkinfotest";
+static FILE *f;
+
+static char *node_name_map_file = NULL;
+static nn_map_t *node_name_map = NULL;
+
+static int timeout_ms = 500;
+
+static int debug = 0;
+#define	DEBUG(str, args...) \
+	if (debug) fprintf(stderr, str, ##args)
+
+static int down_links_only = 0;
+static int line_mode = 0;
+static int add_sw_settings = 0;
+static int print_port_guids = 0;
+
+static unsigned int
+get_max(unsigned int num)
+{
+	unsigned int v = num; // 32-bit word to find the log base 2 of
+	unsigned r = 0; // r will be lg(v)
+
+	while (v >>= 1) // unroll for more speed...
+	{
+		r++;
+	}
+
+	return (1 << r);
+}
+
+void
+get_msg(char *width_msg, char *speed_msg, int msg_size, ibnd_port_t *port)
+{
+	int max_speed = 0;
+
+	int max_width = get_max(port->info.link_width_supported
+				& port->remoteport->info.link_width_supported);
+	if ((max_width & port->info.link_width_active) == 0) {
+		// we are not at the max supported width
+		// print what we could be at.
+		snprintf(width_msg, msg_size, "Could be %s",
+			ibnd_linkwidth_str(max_width));
+	}
+
+	max_speed = get_max(port->info.link_speed_supported
+				& port->remoteport->info.link_speed_supported);
+	if ((max_speed & port->info.link_speed_active) == 0) {
+		// we are not at the max supported speed
+		// print what we could be at.
+		snprintf(speed_msg, msg_size, "Could be %s",
+			ibnd_linkspeed_str(max_speed));
+	}
+}
+
+void
+print_port(ibnd_node_t *node, ibnd_port_t *port)
+{
+	char remote_guid_str[256];
+	char remote_str[256];
+	char link_str[256];
+	char width_msg[256];
+	char speed_msg[256];
+	char ext_port_str[256];
+
+	if (!port)
+		return;
+
+	remote_guid_str[0] = '\0';
+	remote_str[0] = '\0';
+	link_str[0] = '\0';
+	width_msg[0] = '\0';
+	speed_msg[0] = '\0';
+
+	if (port->remoteport) {
+		char  remote_name_buf[256];
+		strncpy(remote_name_buf, port->remoteport->node->nodedesc, 256);
+
+		if (port->remoteport->ext_portnum)
+			snprintf(ext_port_str, 256, "%d", port->remoteport->ext_portnum);
+		else
+			ext_port_str[0] = '\0';
+
+		get_msg(width_msg, speed_msg, 256, port);
+		if (line_mode) {
+			if (print_port_guids) {
+				snprintf(remote_guid_str, 256,
+					"0x%016lx ",
+					port->remoteport->guid);
+			} else {
+				snprintf(remote_guid_str, 256,
+					"0x%016lx ",
+					port->remoteport->node->info.nodeguid);
+			}
+		}
+
+		snprintf(remote_str, 256,
+			"%s%6d %4d[%2s] \"%s\" (%s %s)\n",
+			remote_guid_str,
+			port->remoteport->info.lid ?
+				port->remoteport->info.lid :
+				port->remoteport->node->smalid,
+			port->remoteport->portnum,
+			ext_port_str,
+			remap_node_name(node_name_map,
+				port->remoteport->node->info.nodeguid,
+				remote_name_buf),
+			width_msg,
+			speed_msg
+			);
+	} else {
+		snprintf(remote_str, 256,
+			"%6s %4s[%2s] \"\" ( )\n", "", "", "");
+	}
+
+	if (add_sw_settings) {
+		snprintf(link_str, 256,
+			"(%3s %s %6s/%8s) (HOQ:%d VL_Stall:%d)",
+			ibnd_linkwidth_str(port->info.link_width_active),
+			ibnd_linkspeed_str(port->info.link_speed_active),
+			ibnd_linkstate_str(port->info.link_state),
+			ibnd_physstate_str(port->info.phys_state),
+			port->info.hoq_lifetime,
+			port->info.vl_stall_count
+			);
+	} else {
+		snprintf(link_str, 256,
+			"(%3s %s %6s/%8s)",
+			ibnd_linkwidth_str(port->info.link_width_active),
+			ibnd_linkspeed_str(port->info.link_speed_active),
+			ibnd_linkstate_str(port->info.link_state),
+			ibnd_physstate_str(port->info.phys_state)
+			);
+	}
+
+	if (port->ext_portnum)
+		snprintf(ext_port_str, 256, "%d", port->ext_portnum);
+	else
+		ext_port_str[0] = '\0';
+
+	if (line_mode) {
+		char  name_buf[256];
+		strncpy(name_buf, node->nodedesc, 256);
+		printf("0x%016lx \"%30s\" %6d %4d[%2s] ==%s==>  %s",
+			node->info.nodeguid,
+			remap_node_name(node_name_map,
+				node->info.nodeguid,
+				name_buf),
+			node->smalid, port->portnum,
+			ext_port_str,
+			link_str,
+			remote_str
+			);
+	} else {
+		printf("      %6d %4d[%2s] ==%s==>  %s",
+			node->smalid, port->portnum,
+			ext_port_str,
+			link_str,
+			remote_str
+			);
+	}
+}
+
+void
+print_switch(ibnd_node_t *node, void *user_data)
+{
+	int i = 0;
+
+	if (!line_mode) {
+		char  name_buf[256];
+		strncpy(name_buf, node->nodedesc, 256);
+		printf("Switch 0x%016lx %s:\n",
+			node->info.nodeguid,
+			remap_node_name(node_name_map,
+				node->info.nodeguid,
+				name_buf));
+	}
+
+	for (i = 1; i <= node->info.numports; i++) {
+		ibnd_port_t *port = node->ports[i];
+		if (!port)
+			continue;
+		if (!down_links_only || port->info.link_state == IBND_LINK_DOWN) {
+			print_port(node, port);
+		}
+	}
+}
+
+void
+usage(void)
+{
+	fprintf(stderr,
+		"Usage: %s [-hclp -S <guid> -D <direct route> -C <ca_name> -P <ca_port>]\n"
+		"   Report link speed and connection for each port of each switch which is active\n"
+		"   -h This help message\n"
+		"   -S <guid> output only the node specified by guid\n"
+		"   -D <direct route> print only node specified by <direct route>\n"
+		"   -f <dr_path> specify node to start \"from\"\n"
+		"   -n <hops> Number of hops to include away from specified node\n"
+		"   -d print only down links\n"
+		"   -l (line mode) print all information for each link on each line\n"
+		"   -p print additional switch settings (PktLifeTime,HoqLife,VLStallCount)\n"
+
+
+		"   -t <timeout_ms> timeout for any single fabric query\n"
+		"   -s show errors\n"
+		"   --node-name-map <map_file> use specified node name map\n"
+
+		"   -C <ca_name> use selected Channel Adaptor name for queries\n"
+		"   -P <ca_port> use selected channel adaptor port for queries\n"
+		"   -g print port guids instead of node guids\n"
+		"   --debug print debug messages\n"
+		,
+			argv0);
+	exit(-1);
+}
+
+int
+main(int argc, char **argv)
+{
+	char *ca = 0;
+	int ca_port = 0;
+	ibnd_fabric_t *fabric = NULL;
+	uint64_t guid = 0;
+	char *dr_path = NULL;
+	char *from = NULL;
+	int hops = 0;
+	ib_portid_t port_id;
+
+	static char const str_opts[] = "S:D:n:C:P:t:sldgphuf:";
+	static const struct option long_opts[] = {
+		{ "S", 1, 0, 'S'},
+		{ "D", 1, 0, 'D'},
+		{ "num-hops", 1, 0, 'n'},
+		{ "down-links-only", 0, 0, 'd'},
+		{ "line-mode", 0, 0, 'l'},
+		{ "ca-name", 1, 0, 'C'},
+		{ "ca-port", 1, 0, 'P'},
+		{ "timeout", 1, 0, 't'},
+		{ "show", 0, 0, 's'},
+		{ "print-port-guids", 0, 0, 'g'},
+		{ "print-additional", 0, 0, 'p'},
+		{ "help", 0, 0, 'h'},
+		{ "usage", 0, 0, 'u'},
+		{ "node-name-map", 1, 0, 1},
+		{ "debug", 0, 0, 2},
+		{ "from", 1, 0, 'f'},
+		{ }
+	};
+
+	f = stdout;
+
+	argv0 = argv[0];
+
+	while (1) {
+		int ch = getopt_long(argc, argv, str_opts, long_opts, NULL);
+		if ( ch == -1 )
+			break;
+		switch(ch) {
+		case 1:
+			node_name_map_file = strdup(optarg);
+			break;
+		case 2:
+			debug = 1;
+			ibnd_debug(1);
+			break;
+		case 'f':
+			from = strdup(optarg);
+			break;
+		case 'C':
+			ca = strdup(optarg);
+			break;
+		case 'P':
+			ca_port = strtoul(optarg, 0, 0);
+			break;
+		case 'D':
+			dr_path = strdup(optarg);
+			break;
+		case 'n':
+			hops = (int)strtol(optarg, NULL, 0);
+			break;
+		case 'd':
+			down_links_only = 1;
+			break;
+		case 'l':
+			line_mode = 1;
+			break;
+		case 't':
+			timeout_ms = strtoul(optarg, 0, 0);
+			break;
+		case 'g':
+			print_port_guids = 1;
+			break;
+		case 'S':
+			guid = (uint64_t)strtoull(optarg, 0, 0);
+			break;
+		case 'p':
+			add_sw_settings = 1;
+			break;
+		default:
+			usage();
+			break;
+		}
+	}
+	argc -= optind;
+	argv += optind;
+
+	if (argc && !(f = fopen(argv[0], "w")))
+		fprintf(stderr, "can't open file %s for writing", argv[0]);
+
+	node_name_map = open_node_name_map(node_name_map_file);
+
+	if (from) {
+		/* only scan part of the fabric */
+		str2drpath(&(port_id.drpath), from, 0, 0);
+		if ((fabric = ibnd_discover_fabric(ca, ca_port, timeout_ms, &port_id, hops)) == NULL) {
+			fprintf(stderr, "discover failed\n");
+			exit(1);
+		}
+		guid = 0;
+	} else {
+		if ((fabric = ibnd_discover_fabric(ca, ca_port, timeout_ms, NULL, -1)) == NULL) {
+			fprintf(stderr, "discover failed\n");
+			exit(1);
+		}
+	}
+
+	if (guid) {
+		ibnd_node_t *sw = ibnd_find_node_guid(fabric, guid);
+		print_switch(sw, NULL);
+	} else if (dr_path) {
+		ibnd_node_t *sw = ibnd_find_node_dr(fabric, dr_path);
+		print_switch(sw, NULL);
+	} else {
+		ibnd_iter_nodes_type(fabric, print_switch, IBND_SWITCH_NODE, NULL);
+	}
+
+	ibnd_destroy_fabric(fabric);
+
+	close_node_name_map(node_name_map);
+	exit(0);
+}
diff --git a/libibnetdisc/test/ibnetdisctest.c b/libibnetdisc/test/ibnetdisctest.c
new file mode 100644
index 0000000..e4088da
--- /dev/null
+++ b/libibnetdisc/test/ibnetdisctest.c
@@ -0,0 +1,588 @@
+/*
+ * Copyright (c) 2004-2007 Voltaire Inc.  All rights reserved.
+ * Copyright (c) 2007 Xsigo Systems Inc.  All rights reserved.
+ * Copyright (c) 2008 Lawrence Livermore National Lab.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#  include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <time.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <infiniband/complib/cl_nodenamemap.h>
+#include <infiniband/ibnetdisc.h>
+
+#define LIST_CA_NODE	 (1 << IBND_CA_NODE)
+#define LIST_SWITCH_NODE (1 << IBND_SWITCH_NODE)
+#define LIST_ROUTER_NODE (1 << IBND_ROUTER_NODE)
+
+char *argv0 = "ibnetdiscover";
+static FILE *f;
+
+static char *node_name_map_file = NULL;
+static nn_map_t *node_name_map = NULL;
+
+static int timeout_ms = 2000;
+static int dumplevel = 0;
+
+static int debug = 0;
+#define	DEBUG(str, args...) \
+	if (debug) fprintf(stderr, str, ##args)
+
+char *
+node_name(ibnd_node_t *node)
+{
+	static char buf[256];
+
+	switch(node->info.type) {
+	case IBND_CA_NODE:
+		sprintf(buf, "\"%s", "H");
+		break;
+	case IBND_SWITCH_NODE:
+		sprintf(buf, "\"%s", "S");
+		break;
+	case IBND_ROUTER_NODE:
+		sprintf(buf, "\"%s", "R");
+		break;
+	default:
+		sprintf(buf, "\"%s", "?");
+		break;
+	}
+	sprintf(buf+2, "-%016" PRIx64 "\"", node->info.nodeguid);
+
+	return buf;
+}
+
+void
+list_node(ibnd_node_t *node, void *user_data)
+{
+	char *nodename = remap_node_name(node_name_map, node->info.nodeguid,
+					      node->nodedesc);
+
+	fprintf(f, "%s\t : 0x%016" PRIx64 " ports %d devid 0x%x vendid 0x%x \"%s\"\n",
+		ibnd_node_type_str(node),
+		node->info.nodeguid, node->info.numports, node->info.devid,
+		node->info.vendid,
+		nodename);
+
+	free(nodename);
+}
+
+void
+list_nodes(ibnd_fabric_t *fabric, int list)
+{
+	if (list & LIST_CA_NODE) {
+		ibnd_iter_nodes_type(fabric, list_node, IBND_CA_NODE, NULL);
+	}
+	if (list & LIST_SWITCH_NODE) {
+		ibnd_iter_nodes_type(fabric, list_node, IBND_SWITCH_NODE, NULL);
+	}
+	if (list & LIST_ROUTER_NODE) {
+		ibnd_iter_nodes_type(fabric, list_node, IBND_ROUTER_NODE, NULL);
+	}
+}
+
+void
+out_ids(ibnd_node_t *node, int group, char *chname)
+{
+	fprintf(f, "\nvendid=0x%x\ndevid=0x%x\n", node->info.vendid, node->info.devid);
+	if (node->info.sysimgguid)
+		fprintf(f, "sysimgguid=0x%" PRIx64, node->info.sysimgguid);
+	if (group
+	    && node->chrecord && node->chrecord->chassisnum) {
+		fprintf(f, "\t\t# Chassis %d", node->chrecord->chassisnum);
+		if (chname)
+			fprintf(f, " (%s)", clean_nodedesc(chname));
+		if (ibnd_is_xsigo_tca(node->info.nodeguid)
+				&& node->ports[1]
+				&& node->ports[1]->remoteport)
+			fprintf(f, " slot %d", node->ports[1]->remoteport->portnum);
+	}
+	fprintf(f, "\n");
+}
+
+
+uint64_t
+out_chassis(ibnd_fabric_t *fabric, int chassisnum)
+{
+	uint64_t guid;
+
+	fprintf(f, "\nChassis %d", chassisnum);
+	guid = ibnd_get_chassis_guid(fabric, chassisnum);
+	if (guid)
+		fprintf(f, " (guid 0x%" PRIx64 ")", guid);
+	fprintf(f, "\n");
+	return guid;
+}
+
+void
+out_switch(ibnd_node_t *node, int group, char *chname)
+{
+	char *str;
+	char  str2[256];
+	char *nodename = NULL;
+
+	out_ids(node, group, chname);
+	fprintf(f, "switchguid=0x%" PRIx64, node->info.nodeguid);
+	fprintf(f, "(%" PRIx64 ")", node->info.nodeportguid);
+	if (group) {
+		str = ibnd_get_chassis_type(node);
+		if (str)
+			fprintf(f, "%s ", str);
+		str = ibnd_get_chassis_slot_str(node, str2, 256);
+		if (str)
+			fprintf(f, "%s ", str);
+	}
+
+	nodename = remap_node_name(node_name_map, node->info.nodeguid,
+				node->nodedesc);
+
+	fprintf(f, "\nSwitch\t%d %s\t\t# \"%s\" %s port 0 lid %d lmc %d\n",
+		node->info.numports, node_name(node),
+		nodename,
+		node->sw_info.smaenhsp0 ? "enhanced" : "base",
+		node->smalid, node->smalmc);
+
+	free(nodename);
+}
+
+void
+out_ca(ibnd_node_t *node, int group, char *chname)
+{
+	char *node_type;
+	char *node_type2;
+
+	out_ids(node, group, chname);
+	switch(node->info.type) {
+	case IBND_CA_NODE:
+		node_type = "ca";
+		node_type2 = "Ca";
+		break;
+	case IBND_ROUTER_NODE:
+		node_type = "rt";
+		node_type2 = "Rt";
+		break;
+	default:
+		node_type = "???";
+		node_type2 = "???";
+		break;
+	}
+
+	fprintf(f, "%sguid=0x%" PRIx64 "\n", node_type, node->info.nodeguid);
+	fprintf(f, "%s\t%d %s\t\t# \"%s\"",
+		node_type2, node->info.numports, node_name(node),
+		clean_nodedesc(node->nodedesc));
+	if (group && ibnd_is_xsigo_hca(node->info.nodeguid))
+		fprintf(f, " (scp)");
+	fprintf(f, "\n");
+}
+
+#define OUT_BUFFER_SIZE 16
+static char *
+out_ext_port(ibnd_port_t *port, int group)
+{
+	static char mapping[OUT_BUFFER_SIZE];
+
+	if (group && port->ext_portnum != 0) {
+		snprintf(mapping, OUT_BUFFER_SIZE,
+			"[ext %d]", port->ext_portnum);
+	}
+
+	return (mapping);
+}
+
+void
+out_switch_port(ibnd_port_t *port, int group)
+{
+	char *ext_port_str = NULL;
+	char *rem_nodename = NULL;
+
+	DEBUG("port %p:%d remoteport %p\n", port, port->portnum, port->remoteport);
+	fprintf(f, "[%d]", port->portnum);
+
+	ext_port_str = out_ext_port(port, group);
+	if (ext_port_str)
+		fprintf(f, "%s", ext_port_str);
+
+	rem_nodename = remap_node_name(node_name_map,
+				port->remoteport->node->info.nodeguid,
+				port->remoteport->node->nodedesc);
+
+	ext_port_str = out_ext_port(port->remoteport, group);
+	fprintf(f, "\t%s[%d]%s",
+		node_name(port->remoteport->node),
+		port->remoteport->portnum,
+		ext_port_str ? ext_port_str : "");
+	if (port->remoteport->node->info.type != IBND_SWITCH_NODE)
+		fprintf(f, "(%" PRIx64 ") ", port->remoteport->guid);
+	fprintf(f, "\t\t# \"%s\" lid %d %s%s",
+		rem_nodename,
+		port->remoteport->node->info.type == IBND_SWITCH_NODE ?  port->remoteport->node->smalid : port->remoteport->info.lid,
+		ibnd_linkwidth_str(port->info.link_width_active),
+		ibnd_linkspeed_str(port->info.link_speed_active));
+
+	if (ibnd_is_xsigo_tca(port->remoteport->guid))
+		fprintf(f, " slot %d", port->portnum);
+	else if (ibnd_is_xsigo_hca(port->remoteport->guid))
+		fprintf(f, " (scp)");
+	fprintf(f, "\n");
+
+	free(rem_nodename);
+}
+
+void
+out_ca_port(ibnd_port_t *port, int group)
+{
+	char *str = NULL;
+	char *rem_nodename = NULL;
+
+	fprintf(f, "[%d]", port->portnum);
+	if (port->node->info.type != IBND_SWITCH_NODE)
+		fprintf(f, "(%" PRIx64 ") ", port->guid);
+	fprintf(f, "\t%s[%d]",
+		node_name(port->remoteport->node),
+		port->remoteport->portnum);
+	str = out_ext_port(port->remoteport, group);
+	if (str)
+		fprintf(f, "%s", str);
+	if (port->remoteport->node->info.type != IBND_SWITCH_NODE)
+		fprintf(f, " (%" PRIx64 ") ", port->remoteport->guid);
+
+	rem_nodename = remap_node_name(node_name_map,
+				port->remoteport->node->info.nodeguid,
+				port->remoteport->node->nodedesc);
+
+	fprintf(f, "\t\t# lid %d lmc %d \"%s\" lid %d %s%s\n",
+		port->info.lid, port->info.lmc, rem_nodename,
+		port->remoteport->node->info.type == IBND_SWITCH_NODE ?  port->remoteport->node->smalid : port->remoteport->info.lid,
+		ibnd_linkwidth_str(port->info.link_width_active),
+		ibnd_linkspeed_str(port->info.link_speed_active));
+
+	free(rem_nodename);
+}
+
+int
+dump_topology(int group, ibnd_fabric_t *fabric)
+{
+	ibnd_node_t *node;
+	ibnd_port_t *port;
+	int i = 0, dist = 0, p = 0;
+	time_t t = time(0);
+	uint64_t chguid;
+	char *chname = NULL;
+
+	fprintf(f, "#\n# Topology file: generated on %s#\n", ctime(&t));
+	fprintf(f, "# Max of %d hops discovered\n", fabric->maxhops_discovered);
+	fprintf(f, "# Initiated from node %016" PRIx64 " port %016" PRIx64 "\n",
+		fabric->from_node->info.nodeguid, fabric->from_node->info.nodeportguid);
+
+	/* Make pass on switches */
+	if (group) {
+		ibnd_chassis_list_t *ch = NULL;
+
+		/* Chassis based switches first */
+		for (ch = fabric->chassis; ch; ch = ch->next) {
+			int n = 0;
+
+			if (!ch->chassisnum)
+				continue;
+			chguid = out_chassis(fabric, ch->chassisnum);
+
+			chname = NULL;
+/**
+ * Hal will this work for Xsigo?
+ */
+			if (ibnd_is_xsigo_guid(chguid)) {
+				for (node = ch->nodes; node; node = node->chassis_next) {
+					if (ibnd_is_xsigo_hca(node->info.nodeguid)) {
+						chname = node->nodedesc;
+						fprintf(f, "Hostname: %s\n", clean_nodedesc(node->nodedesc));
+					}
+				}
+
+#if 0
+/**
+ * vs. this?
+ */
+				for (node = fabric->nodesdist[MAXHOPS]; node; node = node->dnext) {
+					if (!node->chrecord ||
+					    !node->chrecord->chassisnum)
+						continue;
+
+					if (node->chrecord->chassisnum != ch->chassisnum)
+						continue;
+
+					if (ibnd_is_xsigo_hca(node->nodeguid)) {
+						chname = node->nodedesc;
+						fprintf(f, "Hostname: %s\n", clean_nodedesc(node->nodedesc));
+					}
+				}
+#endif
+			}
+
+			fprintf(f, "\n# Spine Nodes");
+			for (n = 1; n <= (SPINES_MAX_NUM+1); n++) {
+				if (ch->spinenode[n]) {
+					out_switch(ch->spinenode[n], group, chname);
+					for (p = 1; p <= ch->spinenode[n]->info.numports; p++) {
+						port = ch->spinenode[n]->ports[p];
+						if (port && port->remoteport)
+							out_switch_port(port, group);
+					}
+				}
+			}
+			fprintf(f, "\n# Line Nodes");
+			for (n = 1; n <= (LINES_MAX_NUM+1); n++) {
+				if (ch->linenode[n]) {
+					out_switch(ch->linenode[n], group, chname);
+					for (p = 1; p <= ch->linenode[n]->info.numports; p++) {
+						port = ch->linenode[n]->ports[p];
+						if (port && port->remoteport)
+							out_switch_port(port, group);
+					}
+				}
+			}
+
+			fprintf(f, "\n# Chassis Switches");
+			for (node = ch->nodes; node; node = node->chassis_next) {
+				if (node->info.type == IBND_SWITCH_NODE) {
+					out_switch(node, group, chname);
+					for (p = 1; p <= node->info.numports; p++) {
+						port = node->ports[p];
+						if (port && port->remoteport)
+							out_switch_port(port, group);
+					}
+				}
+			}
+
+			fprintf(f, "\n# Chassis CAs");
+			for (node = ch->nodes; node; node = node->chassis_next) {
+				if (node->info.type == IBND_CA_NODE) {
+					out_ca(node, group, chname);
+					for (p = 1; p <= node->info.numports; p++) {
+						port = node->ports[p];
+						if (port && port->remoteport)
+							out_ca_port(port, group);
+					}
+				}
+			}
+
+		}
+
+	} else { /* !group */
+		for (node = fabric->switches; node; node = node->type_next) {
+				DEBUG("SWITCH: dist %d node %p\n", dist, node);
+				out_switch(node, group, chname);
+				for (p = 1; p <= node->info.numports; p++) {
+					port = node->ports[p];
+					if (port && port->remoteport)
+						out_switch_port(port, group);
+				}
+		}
+	}
+
+	chname = NULL;
+	if (group) {
+		fprintf(f, "\nNon-Chassis Nodes\n");
+		for (node = fabric->switches; node; node = node->type_next) {
+				DEBUG("SWITCH: dist %d node %p\n", dist, node);
+				/* Now, skip chassis based switches */
+				if (node->chrecord &&
+				    node->chrecord->chassisnum)
+					continue;
+				out_switch(node, group, chname);
+
+				for (p = 1; p <= node->info.numports; p++) {
+					port = node->ports[p];
+					if (port && port->remoteport)
+						out_switch_port(port, group);
+				}
+		}
+
+	}
+
+	/* Make pass on CAs */
+	for (node = fabric->ch_adapters; node; node = node->type_next) {
+		DEBUG("CA: dist %d node %p\n", dist, node);
+		/* Now, skip chassis based CAs */
+		if (group && node->chrecord &&
+		    node->chrecord->chassisnum)
+			continue;
+		out_ca(node, group, chname);
+
+		for (p = 1; p <= node->info.numports; p++) {
+			port = node->ports[p];
+			if (port && port->remoteport)
+				out_ca_port(port, group);
+		}
+	}
+
+	/* make pass on routers */
+	for (node = fabric->routers; node; node = node->type_next) {
+		DEBUG("RT: dist %d node %p\n", dist, node);
+		/* Now, skip chassis based CAs */
+		if (group && node->chrecord &&
+		    node->chrecord->chassisnum)
+			continue;
+		out_ca(node, group, chname);
+		for (p = 1; p <= node->info.numports; p++) {
+			port = node->ports[p];
+			if (port && port->remoteport)
+				out_ca_port(port, group);
+		}
+	}
+
+	return i;
+}
+
+void
+usage(void)
+{
+	fprintf(stderr, "Usage: %s [-d(ebug)] -s(how) -l(ist) -g(rouping) -H(ca_list) -S(witch_list) -R(outer_list) -V(ersion) -C ca_name -P ca_port "
+			"-t(imeout) timeout_ms --node-name-map node-name-map] -p(orts) [<topology-file>]\n",
+			argv0);
+	fprintf(stderr, "       --node-name-map <node-name-map> specify a node name map file\n");
+	exit(-1);
+}
+
+int
+main(int argc, char **argv)
+{
+	int list = 0;
+	char *ca = 0;
+	int ca_port = 0;
+	int group = 0;
+	int ports_report = 0;
+	ibnd_fabric_t *fabric = NULL;
+
+	static char const str_opts[] = "C:P:t:devslgHSRpVhu";
+	static const struct option long_opts[] = {
+		{ "C", 1, 0, 'C'},
+		{ "P", 1, 0, 'P'},
+		{ "debug", 0, 0, 'd'},
+		{ "show", 0, 0, 's'},
+		{ "list", 0, 0, 'l'},
+		{ "grouping", 0, 0, 'g'},
+		{ "Hca_list", 0, 0, 'H'},
+		{ "Switch_list", 0, 0, 'S'},
+		{ "Router_list", 0, 0, 'R'},
+		{ "timeout", 1, 0, 't'},
+		{ "node-name-map", 1, 0, 1},
+		{ "ports", 0, 0, 'p'},
+		{ "help", 0, 0, 'h'},
+		{ "usage", 0, 0, 'u'},
+		{ }
+	};
+
+	f = stdout;
+
+	argv0 = argv[0];
+
+	while (1) {
+		int ch = getopt_long(argc, argv, str_opts, long_opts, NULL);
+		if ( ch == -1 )
+			break;
+		switch(ch) {
+		case 1:
+			node_name_map_file = strdup(optarg);
+			break;
+		case 'C':
+			ca = optarg;
+			break;
+		case 'P':
+			ca_port = strtoul(optarg, 0, 0);
+			break;
+		case 'd':
+			debug = 1;
+			ibnd_debug(1);
+			break;
+		case 't':
+			timeout_ms = strtoul(optarg, 0, 0);
+			break;
+		case 's':
+			dumplevel = 1;
+			break;
+		case 'l':
+			list = LIST_CA_NODE | LIST_SWITCH_NODE | LIST_ROUTER_NODE;
+			break;
+		case 'g':
+			group = 1;
+			break;
+		case 'S':
+			list |= LIST_SWITCH_NODE;
+			break;
+		case 'H':
+			list |= LIST_CA_NODE;
+			break;
+		case 'R':
+			list |= LIST_ROUTER_NODE;
+			break;
+		case 'p':
+			ports_report = 1;
+			break;
+		default:
+			usage();
+			break;
+		}
+	}
+	argc -= optind;
+	argv += optind;
+
+	if (argc && !(f = fopen(argv[0], "w")))
+		fprintf(stderr, "can't open file %s for writing", argv[0]);
+
+	node_name_map = open_node_name_map(node_name_map_file);
+
+	if ((fabric = ibnd_discover_fabric(ca, ca_port, timeout_ms, NULL, -1)) == NULL) {
+		fprintf(stderr, "discover failed\n");
+		exit(1);
+	}
+
+	if (list)
+		list_nodes(fabric, list);
+	else
+		dump_topology(group, fabric);
+
+	ibnd_destroy_fabric(fabric);
+	close_node_name_map(node_name_map);
+	exit(0);
+}
diff --git a/libibnetdisc/test/testleaks.c b/libibnetdisc/test/testleaks.c
new file mode 100644
index 0000000..4c10afb
--- /dev/null
+++ b/libibnetdisc/test/testleaks.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2004-2007 Voltaire Inc.  All rights reserved.
+ * Copyright (c) 2007 Xsigo Systems Inc.  All rights reserved.
+ * Copyright (c) 2008 Lawrence Livermore National Lab.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#  include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <time.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <infiniband/complib/cl_nodenamemap.h>
+#include <infiniband/ibnetdisc.h>
+
+char *argv0 = "iblinkinfotest";
+static FILE *f;
+
+static int timeout_ms = 500;
+
+void
+print_port(ibnd_node_t *node, ibnd_port_t *port)
+{
+	char remote_guid_str[256];
+	char remote_str[256];
+	char link_str[256];
+	char speed_msg[256];
+	char ext_port_str[256];
+
+	if (!port)
+		return;
+
+	remote_guid_str[0] = '\0';
+	remote_str[0] = '\0';
+	link_str[0] = '\0';
+	speed_msg[0] = '\0';
+
+	if (port->remoteport) {
+		char  remote_name_buf[256];
+		strncpy(remote_name_buf, port->remoteport->node->nodedesc, 256);
+
+		if (port->remoteport->ext_portnum)
+			snprintf(ext_port_str, 256, "%d", port->remoteport->ext_portnum);
+		else
+			ext_port_str[0] = '\0';
+
+		snprintf(remote_str, 256,
+			"%s%6d %4d[%2s] \"%s\" (%s)\n",
+			remote_guid_str,
+			port->remoteport->info.lid ?
+				port->remoteport->info.lid :
+				port->remoteport->node->smalid,
+			port->remoteport->portnum,
+			ext_port_str,
+			port->remoteport->node->nodedesc,
+			speed_msg
+			);
+	} else {
+		snprintf(remote_str, 256,
+			"%6s %4s[%2s] \"\" ( )\n", "", "", "");
+	}
+
+	snprintf(link_str, 256,
+		"(%3s %s %6s/%8s)",
+		ibnd_linkwidth_str(port->info.link_width_active),
+		ibnd_linkspeed_str(port->info.link_speed_active),
+		ibnd_linkstate_str(port->info.link_state),
+		ibnd_physstate_str(port->info.phys_state)
+		);
+
+	if (port->ext_portnum)
+		snprintf(ext_port_str, 256, "%d", port->ext_portnum);
+	else
+		ext_port_str[0] = '\0';
+
+	printf("      %6d %4d[%2s] ==%s==>  %s",
+		node->smalid, port->portnum,
+		ext_port_str,
+		link_str,
+		remote_str
+		);
+}
+
+void
+print_switch(ibnd_node_t *node, void *user_data)
+{
+	int i = 0;
+
+	for (i = 1; i <= node->info.numports; i++) {
+		ibnd_port_t *port = node->ports[i];
+		if (!port)
+			continue;
+		if (port->info.link_state == IBND_LINK_DOWN) {
+			print_port(node, port);
+		}
+	}
+}
+
+void
+usage(void)
+{
+	fprintf(stderr,
+		"Usage: %s [-hclp -S <guid> -D <direct route> -C <ca_name> -P <ca_port>]\n"
+		"   Report link speed and connection for each port of each switch which is active\n"
+		"   -h This help message\n"
+		"   -S <guid> output only the node specified by guid\n"
+		"   -D <direct route> print only node specified by <direct route>\n"
+		"   -f <dr_path> specify node to start \"from\"\n"
+		"   -n <hops> Number of hops to include away from specified node\n"
+
+		"   -t <timeout_ms> timeout for any single fabric query\n"
+		"   -s show errors\n"
+
+		"   -C <ca_name> use selected Channel Adaptor name for queries\n"
+		"   -P <ca_port> use selected channel adaptor port for queries\n"
+		"   --debug print debug messages\n"
+		,
+			argv0);
+	exit(-1);
+}
+
+int
+main(int argc, char **argv)
+{
+	char *ca = 0;
+	int ca_port = 0;
+	ibnd_fabric_t *fabric = NULL;
+	uint64_t guid = 0;
+	char *dr_path = NULL;
+	char *from = NULL;
+	int hops = 0;
+	ib_portid_t port_id;
+
+	static char const str_opts[] = "S:D:n:C:P:t:shuf:";
+	static const struct option long_opts[] = {
+		{ "S", 1, 0, 'S'},
+		{ "D", 1, 0, 'D'},
+		{ "num-hops", 1, 0, 'n'},
+		{ "ca-name", 1, 0, 'C'},
+		{ "ca-port", 1, 0, 'P'},
+		{ "timeout", 1, 0, 't'},
+		{ "show", 0, 0, 's'},
+		{ "help", 0, 0, 'h'},
+		{ "usage", 0, 0, 'u'},
+		{ "debug", 0, 0, 2},
+		{ "from", 1, 0, 'f'},
+		{ }
+	};
+
+	f = stdout;
+
+	argv0 = argv[0];
+
+	while (1) {
+		int ch = getopt_long(argc, argv, str_opts, long_opts, NULL);
+		if ( ch == -1 )
+			break;
+		switch(ch) {
+		case 2:
+			ibnd_debug(1);
+			break;
+		case 'f':
+			from = strdup(optarg);
+			break;
+		case 'C':
+			ca = strdup(optarg);
+			break;
+		case 'P':
+			ca_port = strtoul(optarg, 0, 0);
+			break;
+		case 'D':
+			dr_path = strdup(optarg);
+			break;
+		case 'n':
+			hops = (int)strtol(optarg, NULL, 0);
+			break;
+		case 't':
+			timeout_ms = strtoul(optarg, 0, 0);
+			break;
+		case 'S':
+			guid = (uint64_t)strtoull(optarg, 0, 0);
+			break;
+		default:
+			usage();
+			break;
+		}
+	}
+	argc -= optind;
+	argv += optind;
+
+	while (1) {
+		if (from) {
+			/* only scan part of the fabric */
+			str2drpath(&(port_id.drpath), from, 0, 0);
+			if ((fabric = ibnd_discover_fabric(ca, ca_port, timeout_ms, &port_id, hops)) == NULL) {
+				fprintf(stderr, "discover failed\n");
+				exit(1);
+			}
+			guid = 0;
+		} else {
+			if ((fabric = ibnd_discover_fabric(ca, ca_port, timeout_ms, NULL, -1)) == NULL) {
+				fprintf(stderr, "discover failed\n");
+				exit(1);
+			}
+		}
+
+#if 0
+		if (guid) {
+			ibnd_node_t *sw = ibnd_find_node_guid(fabric, guid);
+			print_switch(sw, NULL);
+		} else if (dr_path) {
+			ibnd_node_t *sw = ibnd_find_node_dr(fabric, dr_path);
+			print_switch(sw, NULL);
+		} else {
+			ibnd_iter_nodes_type(fabric, print_switch, IBND_SWITCH_NODE, NULL);
+		}
+#endif
+
+		ibnd_destroy_fabric(fabric);
+	}
+
+	exit(0);
+}
-- 
1.5.4.5




More information about the general mailing list